如何在 ControlValueAccessor 中正确获取对主机指令的引用? [英] How do I properly get a reference to the host directive in a ControlValueAccessor?

查看:24
本文介绍了如何在 ControlValueAccessor 中正确获取对主机指令的引用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在编写代码的角度方式"中将两个指令或一个指令正确连接到angular2中的组件(也是一个指令)?

How do I properly connect two directives, or a directive to a component (which is a directive too) in angular2 in the "angular way of writing code"?

由于关于 angular2 的文档仍然非常稀缺,非常感谢任何见解或参考.

Since the documentation on angular2 is still quite scarce, any insight or reference is greatly appreciated.

这是每个 angular2 示例所展示的 - 通过 ngModel 绑定到 string:

This is what every angular2 example shows - binding to a string via ngModel:

@Component({
    template: 'Hello <input type="text" [(ngModel)]="myVariable">!'
})
class ExampleComponent() {
    myVariable: string = 'World';
}

<小时>

假设我想在不代表 strings自定义组件 上使用 ngModel,但是任何其他值,例如 number 或类或接口:


Suppose I want to use ngModel on a custom component which does not represent strings, but any other value, for example a number or a class or interface:

interface Customer {
    name: string;
    company: string;
    phoneNumbers: string[];
    addresses: Address[];
}

@Component({
    selector: 'customer-editor',
    template: `
        <p>Customer editor for {{customer.name}}</p>
        <div><input [(ngModel)]="customer.name"></div>`
})
class CustomerEditor {
    customer: Customer;
}

我为什么要使用 ngModel,你可能会问,什么时候有其他属性会让数据绑定更容易?好吧,我正在为 angular2 实现一个设计垫片,并且这些组件将像(或与)原生 s 一样使用:

Why do I use ngModel, you may ask, when any other attribute would make data binding a lot easier? Well, I am implementing a design shim for angular2, and the components would be used like (or alongside) native <input>s:

<input name="name" [(ngModel)]="user.name">
<pretty-select name="country" [(ngModel)]="user.country" selectBy="countryCode">
    <option value="us">United States of America</option>
    <option value="uk">United Kingdom</option>
    ...
</pretty-select>

user.country 将是一个对象,而不是一个字符串:

user.country would be an object, not a string:

interface Country {
    countryCode: string,
    countryName: string
}

class User {
    name: string;
    country: Country;
    ...
}

到目前为止我所做的工作,但感觉不对":

此示例的 GitHub 存储库

要将提供给 ngModel 指令的引用与我的 CustomerEditor 组件链接起来,目前我正在使用我自己的 ControlValueAccessor:(简化)

To link up the reference supplied to the ngModel directive with my CustomerEditor component, currently I am using my own ControlValueAccessor: (simplified)

const CUSTOMER_VALUE_ACCESSOR: Provider = CONST_EXPR(
    new Provider(NG_VALUE_ACCESSOR, {
        useExisting: forwardRef(() => CustomerValueAccessor)
    })
);

@Directive({
    selector: 'customer-editor[ngModel]',
    providers: [CUSTOMER_VALUE_ACCESSOR]
})
@Injectable()
class CustomerValueAccessor implements ControlValueAccessor {
    private host: CustomerEditor;

    constructor(element: ElementRef, viewManager: AppViewManager) {
        let hostComponent: any = viewManager.getComponent(element);
        if (hostComponent instanceof CustomerEditor) {
            this.host = hostComponent;
        }
    }

    writeValue(value: any): void {
        if (this.host) { this.host.setCustomer(value); }
    }
}

现在,让我烦恼的是 ControlValueAccessor 是我获取对主机组件的引用的方式:

Now, what disturbs me about that ControlValueAccessor is the way I get a reference to my host component:

        if (hostComponent instanceof CustomerEditor) {
            this.host = hostComponent;
        }

这不仅需要 3 个应该足够的依赖项(ElementRefAppViewManagerCustomerEditor),而且做类型也感觉很错误- 在运行时检查.

This not only requires 3 dependencies where one should suffice (ElementRef, AppViewManager, CustomerEditor), it also feels very wrong to do type-checking during runtime.

如何在 angular2 中获得对宿主组件的引用的正确"方式?

How is the "proper" way to get a reference to the host component in angular2?

我尝试过的其他事情,但没有开始工作:

Other things I have tried, but not have gotten to work:

  • This answer by Thierry Templier notes the component class in the constructor of the ControlValueAccessor for it to be injected by angular:

class CustomerValueAccessor implements ControlValueAccessor {
    constructor(private host: CustomerEditor) { }
}

不幸的是,这对我不起作用,并且给了我一个例外:

Unfortunately, that does not work for me, and gives me an exception:

无法解析CustomerValueAccessor"的所有参数(未定义).确保所有参数都使用 Inject 修饰或具有有效的类型注释,并且CustomerValueAccessor"使用 Injectable 修饰.

Cannot resolve all parameters for 'CustomerValueAccessor'(undefined). Make sure that all the parameters are decorated with Inject or have valid type annotations and that 'CustomerValueAccessor' is decorated with Injectable.

  • 使用@Host:

    class CustomerValueAccessor implements ControlValueAccessor {
        constructor(@Host() private editor: CustomerEditor) { }
    }
    

    抛出与上述解决方案相同的异常.

    Throws the same exception as the solution above.

    使用@Optional:

    class CustomerValueAccessor implements ControlValueAccessor {
        constructor(@Optional() private editor: CustomerEditor) { }
    }
    

    不抛出异常,但 CustomerEditor 未注入并保持 null.

    Does not throw an exception, but CustomerEditor is not injected and stays null.

    由于angular变化/变化非常频繁,具体版本我正在处理的可能是相关的,即 angular2@2.0.0-beta.6.

    Since angular changed/changes very frequently, the specific versions I am working with might be relevant, which is angular2@2.0.0-beta.6.

    推荐答案

    评论来自 Günter Zöchbauer 为我指明了正确的方向.

    The comment by Günter Zöchbauer pointed me into the right direction.

    要使用 ngModel 绑定组件上的值,组件本身需要实现 ControlValueAccessor 接口并提供 forwardRef 到自身在组件配置的providers:键:

    To bind a value on a component with ngModel, the component itself needs to implement the ControlValueAccessor interface and provide a forwardRef to itself in the providers: key of the component configuration:

    const CUSTOMER_VALUE_ACCESSOR: Provider = CONST_EXPR(
        new Provider(NG_VALUE_ACCESSOR, {
            useExisting: forwardRef(() => CustomerEditor),
            multi: true
        })
    );
    
    @Component({
        selector: 'customer-editor',
        template: `template for our customer editor`,
        providers: [CUSTOMER_VALUE_ACCESSOR]
    })
    class CustomerEditor implements ControlValueAccessor {
        customer: Customer;
        onChange: Function = () => {};
        onTouched: Function = () => {};
    
        writeValue(customer: Customer): void {
            this.customer = customer;
        }
    
        registerOnChange(fn: Function): void {
            this.onChange = fn;
        }
    
        registerOnTouched(fn: Function): void {
            this.onTouched = fn;
        }
    }
    

    来自父组件的使用:

    @Component({
        selector: 'customer-list',
        template: `
            <h2>Customers:</h2>
            <p *ngFor="#c of customers">
                <a (click)="editedCustomer = c">Edit {{c.name}}</a>
            </p>
            <hr>
            <customer-editor *ngIf="editedCustomer" [(ngModel)]="editedCustomer">
            </customer-editor>`,
        directives: [CustomerEditor]
    })
    export class CustomerList {
        private customers: Customer[];
        private editedCustomer: Customer = 0;
    
        constructor(testData: TestDataProvider) {
             this.customers = testData.getCustomers();
        }
    }
    

    <小时>

    ControlValueAccessor 的每个示例总是展示如何将它与单独的类或宿主组件上的指令一起使用,从未在宿主组件类本身上实现.


    Every example for ControlValueAccessor always show how to use it with a separate class or a directive on a host component, never implemented on the host component class itself.

    这篇关于如何在 ControlValueAccessor 中正确获取对主机指令的引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆