简体   繁体   中英

Angular 2 + ngrx/store: one-way binding to an <input>

When using one-way binding via [ngModel] on an <input> , typing characters into the input always adds characters to the <input> 's value. The problem is that if the [ngModel] expression continues to return its existing value, the <input> value is not refreshed.

Here is a simple example:

@Component({
  selector: 'my-component',
  template: `
    <input type="text" [ngModel]="foo.bar" />
  `
})
export class MyComponent {
  private foo = {
    bar: 'baz'
  };
}

I would expect the input to always show "baz" regardless of user input, but this is not the case.

The reason I am looking for this behavior is for an ngrx/store / redux application, where an <input> 's value should be determined by state flowing one-way. I've created an example use case on Plunker , where Misko Hevery's description should not be editable. The model is indeed unchanged, but the <input> shows whatever the user types.

In the "No trackBy" section, it works correctly, but only because Angular is redrawing all of the DOM elements which forces a proper re-evaluation (but this is not a solution). Adding disabled or readonly attributes to the <input> is not an acceptable answer for me since the component should be unaware of the potentially complex state logic that disallows changes to this field.

I've seen this behavior in React Redux, and I'm wondering how we can use one-way binding in Angular 2 if we cannot stop the user from mutating their own view.

Because ChangeDetection won't trigger when the value returned from the state is the same, the only straight-forward way to do this is by binding to a customerDescriptionLocked property set from the store's customer.descriptionLocked property in the component's constructor. I know you didn't want the component to use readonly because you didn't want the component to be aware of the state's logic for determining the lock. By binding to the customerDescriptionLocked property, the component is still unaware of the state's logic for setting it.

<input type="text" placeholder="Description" [ngModel]="record.customerDescription" 
[readonly]="record.customerDescriptionLocked" (ngModelChange)="updateDescription(record, $event)" />

The component constructor:

constructor(public store: Store<AppState>, private ref: ChangeDetectorRef){
  this.customerState = store.select('customer');
  this.customerState.subscribe((customerState:CustomerState) => {
    this.dashboardRecords = customerState.customers.map((customer:Customer):DashboardRecord => {
      return {
        customerId: customer.id,
        customerName: `${customer.personalInfo.firstName} ${customer.personalInfo.lastName}`,
        customerDescription: customer.description,
        customerDescriptionLocked: customer.descriptionLocked,
        customerUpdateCount: customer.updated
      };
    })
  });
}
@Component({
  selector: 'my-component',
  template: `
    <input type="text" [value]="foo.bar" />
//[ngModel] is for two way binding
  `
})
export class MyComponent {
  private foo = {
    bar: 'baz'
  };
}
@Component({
  selector: 'my-component',
  template: `
    <input type="text" [value]="foo.bar" (keydown) = "$event.preventDefault();"/>
//add preventDefault() to keydown event to prevent user from changing the value shown in input field
  `
})
export class MyComponent {
  private foo = {
    bar: 'baz'
  };
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM