Angular 6/7 库组件 - 提供 formGroup 输入的正确方法?
Angular 6/7 library components - proper way to provide formGroup input?
更新 3
我按照@Ingo 的建议使我的库组件实现了 CVA,但我发现由于我的组件包装了@ng-select/ng-select,一些必要的功能破坏了 ng-[= 的基本功能78=]。也就是说,当 select 从项目列表中输入一个值时,它会填充该值,但它会保持隐藏状态。删除所有必需的 CVA 方法生成一个工作库组件,但在调用表单中显示 selected 值的挑战对我来说仍然存在(仍在努力)。
或者,添加一个事件发射器并定义一个@Output 似乎 运行 进入类似的情况,即试图拦截 ng-select 中的更改事件导致中断它。很可能 ng-select 不适合嵌入库组件。
更新 2
从大量资料来看,使用@Output 和 EventEmitter 似乎是对此的正确答案。如果没有其他人愿意提供答案,我会在一切正常后写一个,然后 post。
原题
我决定开始试用 Angular 6 库功能,但我已经升级到 Angular 7 CLI。我不认为这很重要,但我对 Angular 开发还很陌生。
我想做成库的组件是一个基于ng-select的表单控件。它通过父输入从父表单组件接收 formGroup。
<div [formGroup]="parent" _ngcontent-ikt-1 class="container">
<ng-select
[items]="peopleBuffer"
bindLabel="text"
bindValue="id"
[typeahead]="input$"
formControlName="assocNumber"
#select>
<ng-template ng-option-tmp let-item="item" let-search="searchTerm">
<span [ngOptionHighlight]="search">{{item?.text}}</span>
</ng-template>
</ng-select>
</div>
此 .ts 文件的父级为 @Input()
@Input() parent: FormGroup;
并且在父表单组件中,我像这样安装控件
<app-lookuplist _nghost-ikt-1
[parent]="form">
</app-lookuplist>
在父表单.ts文件中,form类型为FormGroup,在调用@ngOnInit时构造。在一个独立的项目中,这工作正常。
我的大问题是,不过,抽象这种关系的正确方法是什么?
我应该使用原理图而不是库吗?或者是否有适当的方法来公开此输入以便最初构建?我可以将它放入测试工具中并像独立应用程序一样提供该输入,但在构建时,它会产生一个您可能非常熟悉的错误:
Can't bind to 'formGroup' since it isn't a known property of 'div'.
我遵循了几个不同的教程,它们似乎都遵循相同的总体结构。
https://blog.angularindepth.com/creating-a-library-in-angular-6-87799552e7e5
https://angular.io/guide/creating-libraries ...举两个例子,我想答案就在这里,但我对这个问题的理解还处于初级阶段。
感谢所有帮助和指导。
更新
我上面提供的第二个 link 似乎提出了输入应该是无状态的意见...
To make your solution reusable, you need to adjust it so that it does
not depend on app-specific code. Here are some things to consider in
migrating application functionality to a library.
Declarations such as components and pipes should be designed as
stateless, meaning they don’t rely on or alter external variables.
(强调)。我一直在做的是将此输入作为表单的一部分,然后在提交时从表单中获取值。听起来我应该颠倒它,以便库组件发出一个事件,如 。问题仍然是这是否是最好的方法。通过事件处理库组件的值似乎更合乎逻辑。
好的,我想我明白了。或者至少,我有一个在我目前的理解水平上对我有意义的工作解决方案。如果您看到优化的方法,请告诉我。
部分问题是让 ControlValueAccessor 与 ng-select 一起工作而不破坏其本机功能。具体来说,当您使用 ng-select 作为提前输入控件,并且 select 是一个值时,它应该添加一个标签来显示您目前已经 select 编辑的值。尝试实现 ControlValueAccessor 接口破坏了这个,可能是因为它覆盖了 ng-select.
提供的接口 onChange 处理程序
所以我采用的方法结合了我在问题中看到的和提到的两种方法。我的 ng-select 实现现在存在于一个嵌套组件中,该组件包含在一个包装器组件中,该组件上有 ControlValueAccessor。
这样就形成了如下的粗略结构:
app.component (test harness)
people-search.component (ControlValueAccessor)
namelookup.component (@Outputs an EventEmitter)
将 namelookup 从原始项目中迁移出来只需要做一些小的改动。我从 html 中编辑了 [formGroup]="parent" 部分,因为这不再是控件上下文的一部分。
在namelookup.component.html中,我添加了:
<ng-select
...
(change)="handleSelection($event)"
...>
在namelookup.component.ts
@Component({
selector: 'ps-namelookup',
templateUrl: './namelookup.component.html',
styleUrls: ['./namelookup.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush, // new code
})
export class NameLookupComponent implements OnInit {
...
@Output() selectedValue = new EventEmitter<string>();
handleSelection(event) {
console.log('handleSelection in NameLookupComponent fired');
this.selectedValue.emit(event);
}
...
}
然后创建了一个新的包装器,people-search.component,它有一个基本的 ControlValueAccessor 实现 as outlined in many examples。
其中要点是:
在模板中为 CVA 包装器中的子组件发出的事件 (selectedValue) 提供输出挂钩。
@Component({
selector: 'ps-people-search',
template: `
<ps-namelookup (selectedValue)="handleselectedvalue($event)"></ps-namelookup>
`,
...
向 class 定义添加方法并实施 CVA。
export class PeopleSearchComponent implements ControlValueAccessor {
....
private propagateChange = (_: any) => { };
handleselectedvalue($event) {
console.log('handleselectedvalue fired');
console.log($event);
this.data = $event[0].id;
this.propagateChange(this.data);
console.log('handling change event and propagating ' + this.data);
}
至少还没有实现 registerOnChange 和 registerOnTouched。编译器可能会坚持其他一些。
最后,在测试工具 app.component.html 中,为了在表单上显示值以便我知道 selected 值已经传回,我添加了:
{{form.value | json}}
并在 app.component.ts 中提供了一个替代的 FormGroup 对象。
public form: FormGroup;
constructor(private fb: FormBuilder){
console.log('constructing test harness appComponent');
this.form = this.fb.group({});
}
这是回答我的根本问题的答案,可能还有其他好的方法可以回答这个问题。欢迎反馈、优化和更正。
更新 3
我按照@Ingo 的建议使我的库组件实现了 CVA,但我发现由于我的组件包装了@ng-select/ng-select,一些必要的功能破坏了 ng-[= 的基本功能78=]。也就是说,当 select 从项目列表中输入一个值时,它会填充该值,但它会保持隐藏状态。删除所有必需的 CVA 方法生成一个工作库组件,但在调用表单中显示 selected 值的挑战对我来说仍然存在(仍在努力)。
或者,添加一个事件发射器并定义一个@Output 似乎 运行 进入类似的情况,即试图拦截 ng-select 中的更改事件导致中断它。很可能 ng-select 不适合嵌入库组件。
更新 2
从大量资料来看,使用@Output 和 EventEmitter 似乎是对此的正确答案。如果没有其他人愿意提供答案,我会在一切正常后写一个,然后 post。
原题
我决定开始试用 Angular 6 库功能,但我已经升级到 Angular 7 CLI。我不认为这很重要,但我对 Angular 开发还很陌生。
我想做成库的组件是一个基于ng-select的表单控件。它通过父输入从父表单组件接收 formGroup。
<div [formGroup]="parent" _ngcontent-ikt-1 class="container">
<ng-select
[items]="peopleBuffer"
bindLabel="text"
bindValue="id"
[typeahead]="input$"
formControlName="assocNumber"
#select>
<ng-template ng-option-tmp let-item="item" let-search="searchTerm">
<span [ngOptionHighlight]="search">{{item?.text}}</span>
</ng-template>
</ng-select>
</div>
此 .ts 文件的父级为 @Input()
@Input() parent: FormGroup;
并且在父表单组件中,我像这样安装控件
<app-lookuplist _nghost-ikt-1
[parent]="form">
</app-lookuplist>
在父表单.ts文件中,form类型为FormGroup,在调用@ngOnInit时构造。在一个独立的项目中,这工作正常。
我的大问题是,不过,抽象这种关系的正确方法是什么?
我应该使用原理图而不是库吗?或者是否有适当的方法来公开此输入以便最初构建?我可以将它放入测试工具中并像独立应用程序一样提供该输入,但在构建时,它会产生一个您可能非常熟悉的错误:
Can't bind to 'formGroup' since it isn't a known property of 'div'.
我遵循了几个不同的教程,它们似乎都遵循相同的总体结构。
https://blog.angularindepth.com/creating-a-library-in-angular-6-87799552e7e5
https://angular.io/guide/creating-libraries ...举两个例子,我想答案就在这里,但我对这个问题的理解还处于初级阶段。
感谢所有帮助和指导。
更新
我上面提供的第二个 link 似乎提出了输入应该是无状态的意见...
To make your solution reusable, you need to adjust it so that it does not depend on app-specific code. Here are some things to consider in migrating application functionality to a library.
Declarations such as components and pipes should be designed as stateless, meaning they don’t rely on or alter external variables.
(强调)。我一直在做的是将此输入作为表单的一部分,然后在提交时从表单中获取值。听起来我应该颠倒它,以便库组件发出一个事件,如
好的,我想我明白了。或者至少,我有一个在我目前的理解水平上对我有意义的工作解决方案。如果您看到优化的方法,请告诉我。
部分问题是让 ControlValueAccessor 与 ng-select 一起工作而不破坏其本机功能。具体来说,当您使用 ng-select 作为提前输入控件,并且 select 是一个值时,它应该添加一个标签来显示您目前已经 select 编辑的值。尝试实现 ControlValueAccessor 接口破坏了这个,可能是因为它覆盖了 ng-select.
提供的接口 onChange 处理程序所以我采用的方法结合了我在问题中看到的和提到的两种方法。我的 ng-select 实现现在存在于一个嵌套组件中,该组件包含在一个包装器组件中,该组件上有 ControlValueAccessor。
这样就形成了如下的粗略结构:
app.component (test harness)
people-search.component (ControlValueAccessor)
namelookup.component (@Outputs an EventEmitter)
将 namelookup 从原始项目中迁移出来只需要做一些小的改动。我从 html 中编辑了 [formGroup]="parent" 部分,因为这不再是控件上下文的一部分。
在namelookup.component.html中,我添加了:
<ng-select
...
(change)="handleSelection($event)"
...>
在namelookup.component.ts
@Component({
selector: 'ps-namelookup',
templateUrl: './namelookup.component.html',
styleUrls: ['./namelookup.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush, // new code
})
export class NameLookupComponent implements OnInit {
...
@Output() selectedValue = new EventEmitter<string>();
handleSelection(event) {
console.log('handleSelection in NameLookupComponent fired');
this.selectedValue.emit(event);
}
...
}
然后创建了一个新的包装器,people-search.component,它有一个基本的 ControlValueAccessor 实现 as outlined in many examples。 其中要点是:
在模板中为 CVA 包装器中的子组件发出的事件 (selectedValue) 提供输出挂钩。
@Component({
selector: 'ps-people-search',
template: `
<ps-namelookup (selectedValue)="handleselectedvalue($event)"></ps-namelookup>
`,
...
向 class 定义添加方法并实施 CVA。
export class PeopleSearchComponent implements ControlValueAccessor {
....
private propagateChange = (_: any) => { };
handleselectedvalue($event) {
console.log('handleselectedvalue fired');
console.log($event);
this.data = $event[0].id;
this.propagateChange(this.data);
console.log('handling change event and propagating ' + this.data);
}
至少还没有实现 registerOnChange 和 registerOnTouched。编译器可能会坚持其他一些。
最后,在测试工具 app.component.html 中,为了在表单上显示值以便我知道 selected 值已经传回,我添加了:
{{form.value | json}}
并在 app.component.ts 中提供了一个替代的 FormGroup 对象。
public form: FormGroup;
constructor(private fb: FormBuilder){
console.log('constructing test harness appComponent');
this.form = this.fb.group({});
}
这是回答我的根本问题的答案,可能还有其他好的方法可以回答这个问题。欢迎反馈、优化和更正。