【问题标题】:How do I properly get a reference to the host directive in a ControlValueAccessor?如何在 ControlValueAccessor 中正确获取对主机指令的引用?
【发布时间】:2016-03-03 14:58:29
【问题描述】:

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

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


这是每个 angular2 示例显示的内容 - 通过 ngModel 绑定到 string

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

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

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 实现一个设计 shim,这些组件将像(或与)原生 &lt;input&gt;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;
    ...
}

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

GitHub repository for this example

要将提供给ngModel 指令的引用与我的CustomerEditor 组件链接起来,目前我正在使用我自己的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),而且在运行时进行类型检查也感觉非常错误。

在 angular2 中获取对主机组件的引用的“正确”方式是什么?


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

  • This answer by Thierry Templier 在 ControlValueAccessor 的构造函数中注明组件类,以便通过 angular 进行注入:

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

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

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

  • 使用@Host

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

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

  • 使用@Optional:

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

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


由于角度变化/变化非常频繁,the specific versions I am working with might be relevant, which is angular2@2.0.0-beta.6

【问题讨论】:

  • 我猜如果CustomValueAccessor 应该是一个通用的,那么它应该避免访问主机元素。你为什么需要这个?如果绑定到组件,那么组件本身就可以实现ControlValueAccessor接口,那么访问宿主应该没有问题。
  • 如果你以这种方式使用它,你可能需要将访问器类添加到directives

标签: typescript angular angular-ngmodel


【解决方案1】:

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

要将组件上的值绑定到ngModel组件本身需要实现ControlValueAccessor接口并在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 的每个示例始终显示如何将它与单独的类或宿主组件上的指令一起使用,从未在宿主组件类本身上实现。

【讨论】:

  • 一直在找这个在同一堂课上做,感觉很自然,谢谢。
【解决方案2】:

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

CustomEditor对应什么?

【讨论】:

  • 是的,这是我的问题中的错字,而不是示例中的错字(这是一个完整的存储库),感谢您的注意。但这应该是评论,而不是答案,还是我弄错了?
猜你喜欢
  • 2019-01-14
  • 2018-08-06
  • 2016-07-20
  • 2014-11-10
  • 1970-01-01
  • 1970-01-01
  • 2020-06-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多