如何在 ControlValueAccessor 中正确获取对主机指令的引用?

How do I properly get a reference to the host directive in a ControlValueAccessor?

如何在 "angular way of writing code" 中将 angular2 中的两个指令或一个指令正确连接到一个组件(这也是一个指令) ?

由于关于 angular2 的文档仍然非常稀少,任何见解或参考都将不胜感激。


这是每个 angular2 示例显示的内容 - 通过 ngModel:

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

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

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:

一样使用
<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 将是一个 object,而不是 string:

interface Country {
    countryCode: string,
    countryName: string
}

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

到目前为止我已经完成了工作,但是 "feels wrong":

GitHub repository for this example

为了 link 使用我的 CustomerEditor 组件提供给 ngModel 指令的引用,目前我正在使用我自己的 ControlValueAccessor: (简化)

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 是我获取主机组件引用的方式:

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

这不仅需要3个依赖(ElementRefAppViewManagerCustomerEditor),在运行时做类型检查也感觉很不对。

"proper" 如何获取对 angular2 中主机组件的引用?


其他我尝试过但没有奏效的东西:


由于 angular changed/changes 非常频繁,the specific versions I am working with might be relevant, which is angular2@2.0.0-beta.6.

在您的示例中,您的 CustomerValueAccessor 指令似乎附加在 CustomerComponent 组件上(一个是选择器 customer-editor)而不是 [=13= 类型之一].我认为这就是你不能注入它的原因。

CustomEditor对应什么?

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

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

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 的每个示例始终显示如何将其与单独的 class 或主机组件上的指令一起使用,从未在主机组件 class 本身上实现。