代码之家  ›  专栏  ›  技术社区  ›  J E Carter II

解决:角度6/7库组件-提供表单组输入的正确方法?

  •  0
  • J E Carter II  · 技术社区  · 6 年前

    更新3

    我按照@ingo的建议完成了使库组件实现CVA的工作,但我发现由于我的组件包装了@ng select/ng select,一些必需的函数破坏了ng select的基本功能。也就是说,当从项目列表中选择一个值时,它将填充该值,但它将保持隐藏。删除所有必需的cva方法将成为一个可工作的库组件,但在调用表单中呈现所选值的挑战仍然存在(仍在努力实现这一点)。

    或者,添加一个事件发射器并定义@output似乎遇到了类似的情况,尝试在ng select中截取更改事件会导致破坏它。很可能ng select对于嵌入到库组件中是一个不好的候选者。

    更新2

    从大量来源来看,使用@output和eventEmitter似乎是正确的答案。如果没有人愿意提供答案,我会写一个当我有这一切工作,然后张贴。


    原始问题

    我决定开始尝试Angular6库功能,但我已经升级到Angular7CLI。我不认为这很重要,但是我还是很绿色的,有角度的发展。

    我要生成库的组件是一个基于ng select构建的表单控件。它通过父输入从父表单组件接收表单组。

    <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文件中,窗体的类型为FormGroup,并在调用@ngoninit时构造。在一个独立的项目中,这是很好的工作。

    我的大问题是 但是, 抽象这种关系的正确方法是什么 ?

    我应该使用示意图而不是图书馆吗?或者,是否有适当的方法来公开这个输入,以便最初构建它?我可以将其放入测试工具中,并像独立的应用程序那样提供输入,但在构建时,它会产生一个您可能非常熟悉的错误:

    无法绑定到“FormGroup”,因为它不是“Div”的已知属性。

    我学习了几个不同的教程,它们似乎都遵循相同的总体结构。

    https://blog.angularindepth.com/creating-a-library-in-angular-6-87799552e7e5

    https://angular.io/guide/creating-libraries ……举两个例子,我怀疑答案就在这里,但我对这个问题的理解还处于初级阶段。

    感谢您的帮助和指导。

    更新

    我上面提供的第二个链接似乎提供了这样的观点:输入应该是无状态的…

    为了使您的解决方案可重用,您需要对其进行调整,以便 不依赖于应用程序特定的代码。这里有一些事情要考虑 将应用程序功能迁移到库。

    组件和管道等声明应设计为 无状态的, 意味着它们不依赖或改变外部变量 .

    (增加了重点)。我所做的是将此输入作为表单的一部分,然后在提交时从表单中获取值。听起来我应该反转这个函数,以便库组件发出一个事件,如 this answer . 如果这是最好的方法,问题仍然存在。通过事件处理库组件的值似乎更合乎逻辑。

    1 回复  |  直到 6 年前
        1
  •  0
  •   J E Carter II    6 年前

    好吧,我想我明白了。或者至少,在我目前的理解水平上,我有一个可行的解决方案。如果你看到优化的方法,请告诉我。

    部分问题是让controlValueAccessor在不破坏其本机功能的情况下使用ng select。具体地说,当您使用ng select作为一个预输入控件并选择一个值时,它应该添加一个标记,显示到目前为止您选择的值。尝试实现ControlValueAccessor接口会破坏这一点,可能是因为它重写了与ng select一起提供的接口onchange处理程序。

    所以我所采取的方法结合了我在问题中所看到和提到的两种方法。我的ng select实现现在位于一个嵌套组件中,该组件被封装在一个包装组件中,该组件上有controlValueAccessor。

    这使得以下结构变得粗糙:

    app.component  (test harness)
        people-search.component  (ControlValueAccessor)
            namelookup.component (@Outputs an EventEmitter)
    

    将namelookup从原始项目中迁移出来,然后只需要进行一些小的更改。我从其HTML中编辑了[FormGroup]=“父”部分,因为这不再是控件上下文的一部分。

    在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>
        `,
    ...
    

    向类定义中添加方法并实现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。编译器可能会坚持使用其他一些。

    最后,在test harness app.component.html中,为了在表单上显示值,以便我知道所选值已被传递回,我添加了:

    {{form.value | json}}
    

    在app.component.ts中提供了一个stand-in-formgroup对象。

    public form: FormGroup;
    
    constructor(private fb: FormBuilder){
        console.log('constructing test harness appComponent');
    
       this.form = this.fb.group({});
    }
    

    这是一个回答我根本问题的答案,可能还有其他的好方法来回答这个问题。当然欢迎反馈、优化和更正。