【问题标题】:How can i make my ngModel to work automatically in my ng-template?如何让我的 ngModel 在我的 ng-template 中自动工作?
【发布时间】:2021-07-08 05:16:22
【问题描述】:

我有很多带有标签和输入的表单组。我想制作可重复使用的 ng-template

原来如此

  <div class="form-group">
      <label class="form-control-label" for="address">Address</label>
       <input [disabled]="isTheLoggedInUserAdmin" id="address" class="form-control form- 
       control-alternative"
       [(ngModel)]="sharedService.tempUser.address" name="address" type="text">
  </div>

并使用 ng-template 将其转换为

 <ng-template [ngTemplateOutlet]="formGroup"
      [ngTemplateOutletContext]="{data: {inputId:'address', label:'Address', ngModel: 
      sharedService.tempUser.address }}"
></ng-template>


<ng-template #formGroup let-data="data">
 <div class="form-group">
    <label class="form-control-label" [for]="data.inputId">{{data.label}}</label>
    <input [disabled]="isTheLoggedInUserAdmin" [id]="data.inputId" class="form-control form-control- 
    alternative"
    [(ngModel)]="sharedService.tempUser.address" [name]="data.inputId" type="text">
  </div>
</ng-template>

所以我在这里自动传递 inputId,标签名称,现在 ngModel 是硬编码的,它指向 sharedService.tempUser.address

但是我的 ng-template 需要是动态的,所以使用 ng-template 调用我应该传递参数 like label for example - 参数 shpuld 指向我的打字稿文件中的不同 ngModel 变量

但是当我这样做时

 <div class="form-group">
    <label class="form-control-label" [for]="data.inputId">{{data.label}}</label>
    <input [disabled]="isTheLoggedInUserAdmin" [id]="data.inputId" class="form-control form-control-alternative"
      [(ngModel)]="data.ngModel" [name]="data.inputId" type="text">
  </div>

现在 data.ngModel 是从 ng-template 调用发送的 - 这是 sharedService.tempUser.address,我从 sharedService.tempUser.address 获得实际值,但*问题是 ngModel 在这里不起作用`

当我输入内容时它不会更新

我该如何解决这个问题?

【问题讨论】:

  • 不使用ng-template,您认为编写一个实现ControlValueAccessor 的自定义控件组件可能是更好的解决方案吗?
  • @JackySky 能否请您提供一个如何使用 ControlValueAccessor 完成此操作的示例
  • 我已经添加了一个答案,请随时检查它是否符合您的情况。

标签: angular angular-forms


【解决方案1】:

您可能想要编写ControlValueAccessor 的实现作为替代方案。这是创建与 Form API 兼容的可重用组件的 Angular 官方方法。一个最小的例子是这样的:

my-custom-control.component.html

<div class="form-group">
   <label class="form-control-label" for="{{data.field}}">{{data.label}}</label>
   <input [disabled]="disabled" id="{{data.field}}" class="form-control form-control-alternative"
          [value]="value" name="{{data.field}}" type="text"
          (change)="onChange($event)" (blur)="onBlur($event)">
</div>

my-custom-control.component.ts

/**
    import things....
**/

@Component({
  selector: 'MyControl',
  templateUrl: './my-custom-control.component.html',
  styleUrls: ['./my-custom-control.component.scss'],
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => MyControlComponent),
    multi: true
  }]
})
export class MyControlComponent implements OnInit, ControlValueAccessor {

  @Input() value: any;
  
  @Input() data: { 'field': string, 'label': string } =  { field: "default", label: "default" };
  
  @Input() disabled: boolean = false;
  setDisabledState(isDisabled: boolean) {
    this.disabled = isDisabled;
  }

  propagateChange = (_: any) => { };
  propagateTouch = () => { };
  registerOnChange(fn) {
    this.propagateChange = fn;
  }
  registerOnTouched(fn) {
    this.propagateTouch = fn;
  }
  
  constructor() {
  }

  ngOnInit() {
      
  }
  
  // value changed from ui
  onChange(event: any){
    this.value = event.target.value;
    // tell angular that the control value is changed
    this.propagateChange(this.value);
  }
  
  // received value from form api
  writeValue(newFormValue: any) {
    this.value = newFormValue;
  }
  
  // tell angular that this form control is touched
  onBlur(event: any) {
    this.propagateTouch();      
  }
}

之后,你可以像这样重用这个组件:

<!-- With FormGroup -->
<form [formGroup]="myForm">
  <MyControl formControlName="address" [disabled]="isTheLoggedInUserAdmin" [data]="{ field: 'address', label: 'Address' }">
  <MyControl formControlName="title" [disabled]="isTheLoggedInUserAdmin" [data]="{ field: 'title', label: 'Title' }">
  <MyControl formControlName="surname" [disabled]="isTheLoggedInUserAdmin" [data]="{ field: 'surname', label: 'Surname' }">
  <!-- other controls... -->
</form>

<!-- Without FormGroup -->
<div>
  <MyControl [(ngModel)]="myModel.address" [ngModelOptions]="{standalone: true}" [disabled]="isTheLoggedInUserAdmin" [data]="{ field: 'address', label: 'Address' }">
  <MyControl [(ngModel)]="myModel.title"   [ngModelOptions]="{standalone: true}" [disabled]="isTheLoggedInUserAdmin" [data]="{ field: 'title', label: 'Title' }">
  <MyControl [(ngModel)]="myModel.surname" [ngModelOptions]="{standalone: true}" [disabled]="isTheLoggedInUserAdmin" [data]="{ field: 'surname', label: 'Surname' }">
  <!-- other controls... -->
</div>

如果您有模型描述数组,也可以轻松将其写入ngFor

编辑:添加示例 scss 样式。

my-custom-control.component.scss

@import "variable.scss";

:host(.ng-dirty.ng-invalid, .ng-touched.ng-invalid) input {
  border-color: $c-alert !important;
  border-width: 2px;
  border-style: solid;
  color: $c-alert;
}

【讨论】:

  • 天空谢谢你,我接受你的回答。但是有bug。当我在输入中写一些东西时,我没有打印 onChange 事件,并且在对象失去焦点后,我在对象中获得了 [Object event] 的值...你能修复吗
  • 哦,我得到了整个事件的内部 - 所以如果我想获得价值,我只做 event.target.value ....
  • 我也无法准备好使用 disabled 属性获取 undefined --- 我找不到解决此问题的方法
  • @sdsd 你能解释一下禁用的问题吗?比如如何绑定值,是否正确接收到值?
  • 你好 Jacky。你能编辑你的答案吗 - 例如我如何使用控制值访问器验证自定义组件上的输入?我需要像需要验证器一样思考,检查它是否被触摸等
猜你喜欢
  • 2022-11-15
  • 1970-01-01
  • 2010-09-23
  • 2021-05-10
  • 2019-05-06
  • 2013-04-20
  • 2018-08-27
  • 2015-12-31
  • 1970-01-01
相关资源
最近更新 更多