0

Im having problem to get value change from a component property. In this simple example, I have an ngFor that create instance of TextField Component passing fields[i].value as property. I value has a default, its correctly printed on input but the changes are not reflected on the original model.

<ng-container *ngFor="let field of fields.all(); let i = index;">
                        <dynamic-text-field

                            [id]        = "field.name"
                            [name]      = "field.name"
                            [label]     = "field.label"
                            [note]      = "field.translate_help"
                            [(val)]     = "fields[i].value"
                        >

                        </dynamic-text-field>
 </ng-container>


export class TextField
{

    @Input() id         : string = "";
    @Input() name       : string = "";
    @Input() note       : string = "";
    @Input() label      : string = "";
    @Input() val        : string = null;

    [...]
}

<mat-label translate> {{ label | translate }} </mat-label>
<input  matInput placeholder="" [(ngModel)]="val" name="{{name}}" />
2
  • Is there a specific reason why you don't just do [(val)]="field.value"? Commented May 28, 2021 at 11:15
  • Because during ngfor you have to reference the current iteration model otherwise you are passing the last element to every component Commented May 28, 2021 at 11:19

1 Answer 1

2

I'm guessing thats because an @Input is not really a two-way binding. In addition, it looks like your fields[i].value is a primitive (string).

This might be a workaround that is not ideal, but you can try passing an object as val and then, in your TextField component, refer to the value by something like [(ngModel)]="val.value". That way, val is an object, so it's passed by the reference to a child component - any updates to the model will be available in the parent component.

The other way could be to pass a callback function to the child component that you'd call on model update - something like:

                        <dynamic-text-field

                            [id]        = "field.name"
                            [name]      = "field.name"
                            [label]     = "field.label"
                            [note]      = "field.translate_help"
                            [val]       = "fields[i].value"
                            (valueChange)="onValueChange($event)"
                        >

                        </dynamic-text-field>

with:

<mat-label translate> {{ label | translate }} </mat-label>
<input  matInput placeholder="" [ngModel]="val" (change)="valueChange.emit($event.value)" name="{{name}}" />

with:

export class TextField
{

    @Input() id         : string = "";
    @Input() name       : string = "";
    @Input() note       : string = "";
    @Input() label      : string = "";
    @Input() val        : string = null;
    @Output() valueChange = new EventEmitter<string>();

    [...]
}

and then trigger whatever logic you need in the parent's onValueChange method

Sign up to request clarification or add additional context in comments.

2 Comments

I would agree with this answer because the original TextField component has no @Output() declared, then you cannot expect a twoway bind in the ng-container. So the mapInput supports the binding but TextField does not. This can all be read @ angular.io/guide/two-way-binding
Thank you. Im using a JustString model instead of privimite and it works perfectly

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.