1

My model has a property that is an array, something like this:

{
    name: string;
    posts: []
}

So I bind the posts property to my component:

<app-my [posts]="person.posts"></app-my>
or
<app-my [(posts)]="person.posts"></app-my>

Inside MyComponent.ts I add or remove elements from this array:

export class MyComponent implements OnInit {

    @Input() posts: any;
    ...
    addNew(newPost) { //this work
       this.posts.push(newPost);
    }

    remove(post) { //this does not
      this.posts = this.posts.filter(x => x.id != post.id);
    }

The view is correctly rendered, but the original person.posts does not change.

That I'm missing?

6
  • Doesn't it change even when you add? Commented May 13, 2020 at 20:03
  • Is this component implements ‘onPush’ strategy? Commented May 13, 2020 at 20:06
  • @AshishRanjan, you right, only remove is not working. Commented May 13, 2020 at 20:10
  • @RazRonen, no it doesn't Commented May 13, 2020 at 20:11
  • 1
    replace this.posts.push(newPost); with this.posts = [...this.posts, newPost] You want to create a new array instance. Commented May 13, 2020 at 20:15

2 Answers 2

1

To answer your question you need to understand how immutable object are used in angular inputs. Thus is a good explaining article about it https://vsavkin.com/immutability-vs-encapsulation-90549ab74487. So in your case you need to rewrite entire input array in order to changes to be detected. Approach bellow, with a second example is how you would do it.

And there is a good article about two way binding https://blog.thoughtram.io/angular/2016/10/13/two-way-data-binding-in-angular-2.html

export class MyComponent implements OnInit {
    @Input() posts: any;

    @Output()
    public postsChange: EventEmitter<any> = new EventEmitter<any>();

    ...
    addNew(newPost) { //this work
       this.postsChange.emit(newPost)
    }
    ...
}

following binding will be appiable

<app-my [(posts)]="person.posts"></app-my>

//above is identical to below 

<app-my [posts]="person.posts" (postsChange)="person.posts = $event"></app-my>
Sign up to request clarification or add additional context in comments.

Comments

0

Angular would pass Input properties by reference if your variable is of reference type, so you would have the same reference in both Parent in child, unless you change the reference in the child.'

In case of Adding, you do a push(), this adds value to the same reference

In case of Remove, you do a filter, this creates a new reference and leaves the original array as it is.

One workaround for what you have asked, i.e. changing the original array while doing a remove too would be to change the same reference. Something like this may work.

const indexesToRemove = [];
this.posts.forEach((x, i) => {
    if (x.id != post.id) {
        idsToRemove.push(i)
    }
});

for (let eachIndex of indexesToRemove) {
    this.posts.splice(eachIndex, 1);
}

Please Note: It is not a good programming practice to change same Object reference while updating them and we should avoid this as much as possible.

2 Comments

It works. But as you said ,this is making my code ugly. I will try something like @Derek Kite comment.
@Beetlejuice Its not about making the code ugly, but about making the flow ugly, it would get hard to determine an object value if it can be changed from multiple places in your app.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.