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

问题描述

如何使用编写代码的角度方式" 将两个指令或一个指令正确地连接到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';
}


假设我要在自定义组件上使用ngModel,该组件不代表 string s ,但可以代表其他任何值,例如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实现一个设计填充程序,并且这些组件将像(或与之一起)本机<input> 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将是一个对象,而不是字符串:

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:

  • 使用@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.

    由于角度更改/更改非常频繁,因此特定版本我正在使用的可能是相关的,这是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ünterZö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中对host指令的引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

  • 10-28 13:00