Angular:何时使用 signal() 与 model()?

问题描述 投票:0回答:1

我们什么时候应该在 Angular 中使用

signal()
model()

Angular 表示以下内容:

ModelSignal 是一个 WritableSignal,这意味着可以使用 set 和 update 方法从任何地方更改它的值。当分配新值时,ModelSignal 将发送到其输出。这与InputSignal不同,InputSignal是只读的,只能通过模板更改。

看起来

ModelSignal
WriteableSignal
一样,唯一的区别是每当写入它时它都会发出change事件?但是当我们使用
WriteableSignal
WriteableSignal.set()
时,
WriteableSignal.update()
不也会发出更改事件吗?

下面我添加了两个例子。第一个示例使用

signal()
,而第二个示例使用
model()
。两者似乎都有效。对于我的情况,哪个最适合使用?

使用

signal()
的示例:

import { Component, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [FormsModule],
  template: `
  <input type="text"
        [(ngModel)]="quantity"/>

        {{quantity()}}
  `,
})
export class App {
  name = 'Angular';
  quantity = signal<number>(1);
}

bootstrapApplication(App);

使用

model()
的示例:

import { Component, model } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [FormsModule],
  template: `
  <input type="text"
        [(ngModel)]="quantity"/>

        {{quantity()}}
  `,
})
export class App {
  name = 'Angular';
  quantity = model<number>(1);
}

bootstrapApplication(App);
angular input signals angular-standalone-components
1个回答
0
投票

答案在 Angular 文档中给出。

model() 和 input() 之间的区别

input() 和 model() 函数都是在 Angular 中定义基于信号的输入的方法,但它们在一些方面有所不同:

  1. model() 定义输入和输出。输出的名称始终是带有 Change 后缀的输入名称,以支持双向绑定。您的指令的使用者将决定他们是否想仅使用输入、仅使用输出或两者都使用。
  2. ModelSignal 是一个 WritableSignal,这意味着可以使用 set 和 update 方法从任何地方更改它的值。当分配新值时,ModelSignal 将发送到其输出。这与InputSignal不同,InputSignal是只读的,只能通过模板更改。
  3. 模型输入不支持输入转换,而信号输入则支持。

现在对于信号,我觉得最好的用例是除了父子通信(

@Input
)之外的任何场景,但信号也可以与
ngModel
一起使用,但请注意它们没有 signalChange 类型的功能
models

最好仔细阅读下面的工作示例,其中包含注释,以便您更好地理解差异并深入了解信号!

我已尽力通过以下工作示例以及上述三点来强调我对信号的充分理解!

主要(家长)

import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
import { ChildComponent } from './app/child/child.component';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [ChildComponent],
  template: `
  <!-- model has @Input and @Output like syntax in once, notice the suffix Change added to the model's name (here its "modelName") -->
  <!-- inputs are great for @Input and better because you can make them mandatory, they cannot be changed, as far as I know!
-->
    <app-child 
    [modelName]="name" 
    (modelNameChange)="updateModelName($event)"
    [inputNameWithTransformAlias]="inputNameWithTransform"
    [inputNameWithoutTransformAlias]="inputNameWithoutTransform"
    [inputNameButMandatoryAlias]="inputNameButMandatory"
    />
    <hr/>
  `,
})
export class App {
  name: string = 'Angular';
  inputNameWithTransform: string = 'input';
  inputNameWithoutTransform: string = 'input';
  inputNameButMandatory: string = 'input but mandatory';

  updateModelName(event: string | undefined) {
    this.name = event || '';
  }
}

bootstrapApplication(App);

儿童(儿童)

import { Component, input, model, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
  selector: 'app-child',
  standalone: true,
  imports: [FormsModule],
  template: `
  <h1>Model Explanation</h1>
  <input [(ngModel)]="modelName"/> <!-- use model when you have an ngModel, it works great with it -->
  <button (click)="getModel()">get model value</button>
  <button (click)="setModel()">set model value</button>
  <div>Model Name: {{modelName()}}</div>
  <hr/>
  <h1>Input Explanation</h1>
  <div>Input Name With Transform: {{inputNameWithTransform()}}</div>
  <div>Input Name Without Transform: {{inputNameWithoutTransform()}}</div>
  <button (click)="getInputs()">get inputs values</button>
  <button (click)="setInputs()">set inputs values</button>
  <h1>Signal Explanation</h1>
  <div>Signal Name: {{signalName()}}</div>
  <button (click)="getSignal()">get signal value</button>
  <button (click)="setSignal()">set signal value</button>
  `,
})
export class ChildComponent {
  modelName = model<string>('', { alias: 'modelName' }); // notice there is no transform
  inputNameWithTransform = input<string, string>('', {
    // notice there is transform when type is <string, string>
    alias: 'inputNameWithTransformAlias',
    transform: (value: string) => `transformed ${value}`,
  });
  inputNameWithoutTransform = input<string>('', {
    // notice there is no transform when type is <string>
    alias: 'inputNameWithoutTransformAlias',
  });
  inputNameButMandatory = input.required<string, string>({
    // no default value, but if not specified on parent will error out!
    // notice there is transform when type is <string, string>
    alias: 'inputNameButMandatoryAlias',
    transform: (value: string) => `transformed ${value}`,
  });
  signalName = signal<string>('signalName', {
    // can set an initial value
    // signal do not have alias or transform, good for local variable like functionality!
    equal: (a, b) => a === b, // we can write a function which tells us when the signal detects a change,
  });

  constructor() {}

  // model has both get and set methods
  setModel() {
    this.modelName.set('was set using button');
  }
  getModel() {
    alert(this.modelName());
  }

  // input has get but no set methods
  setInputs() {
    alert('check the commented code!');
    // uncomment to see the errors!
    // this.inputNameWithTransform.set('was set using button');
    // this.inputNameWithoutTransform.set('was set using button');
    // this.inputNameButMandatory.set('was set using button');
  }
  getInputs() {
    alert(`
    inputNameWithTransform: ${this.inputNameWithTransform()}
    inputNameWithoutTransform: ${this.inputNameWithoutTransform()}
    inputNameButMandatory: ${this.inputNameButMandatory()}
    `);
  }

  // signals has both get and set methods
  setSignal() {
    this.signalName.set('was set using button');
  }
  getSignal() {
    alert(this.signalName());
  }
}

Stackblitz 演示

© www.soinside.com 2019 - 2024. All rights reserved.