1

I have an Ionic application where I have created a component to show some data of an object. My problem is that when I update the data in the parent that hosts the component the data within the component does not update:

my-card.component.ts

@Component({
    selector: 'my-card',
    templateUrl: './my-card.html'
})
export class MyCard {
    @Input('item') public item: any;
    @Output() itemChange = new EventEmitter();
    constructor() {

    }

    ngOnInit() {
        // I do an ajax call here and populate more fields in the item.
        this.getMoreData().subscribe(data => {
            if (data.item){
                this.item = data.item;
            }
            this.itemChange.emit(this.item);
        });
    }
}

my-card.html

<div class="comment-wrapper" *ngFor="let subitem of item.subitems">
    {{subitem.title}}
</div>

And in the parent I use the component like this:

<my-card [(item)]="item"></my-card>

And the ts file for the parent:

@IonicPage()
@Component({
    selector: 'page-one',
    templateUrl: 'one.html',
})
export class OnePage {
    public item = null;
    constructor(public navCtrl: NavController, public navParams: NavParams) {
        this.item = {id:1, subitems:[]};
    }

    addSubItem():void{
        // AJAX call to save the new item to DB and return the new subitem.
        this.addNewSubItem().subscribe(data => {
            let newSubItem = data.item;
            this.item.subitems.push(newSubItem);
        }
    }
}

So when I call the addSubItem() function it doesnt update the component and the ngFor loop still doesnt display anything.

4
  • This makes no sense to me in the first place, you are passing input, but not using it in child component. Commented Mar 17, 2018 at 8:26
  • Hi sorry if my code is not very clear I had to strip back some of the code. I basically pass in an item object that only contains the I'd and then on load of the card component I do an ajax call to hydrate the rest of the items fields. Commented Mar 17, 2018 at 8:55
  • Okay, I get it now. So you talk about two-way-binding, so the parent needs to have the same value as the child (after api request) or is it enough that what you do in parent reflects in child. I'm thinking of the solution,so this would be important :) Commented Mar 17, 2018 at 9:18
  • Okay so the parent would pass the id to the child and the child would hydrate the results from AJAX. Then the user would interact with an element on the parent that creates a new subitem and I need to then have that new sub item show in the clild view. Does that make sense? Commented Mar 17, 2018 at 9:23

4 Answers 4

1

You are breaking the object reference when you are making the api request. You are assigning new value, that is overwriting the input value you get from the parent, and the objects are no longer pointing to the same object, but item in your child is a completely different object. As you want two-way-binding, we can make use of Output:

Child:

import { EventEmitter, Output } from '@angular/core';

// ..

@Input() item: any;
@Output() itemChange = new EventEmitter();

ngOnInit() {
  // I do an ajax call here and populate more fields in the item.
  this.getMoreData(item.id).subscribe(data => {
    this.item = data;
    // 'recreate' the object reference
    this.itemChange.emit(this.item)
  });
}

Now we have the same object reference again and whatever you do in parent, will reflect in child.

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

2 Comments

Hi I have updated my code as you suggest however it still does not update the list of items in the view when I call the addSubItem() mentod.
I have it working now it was because the data being passed back from the addNewSubItem request was not correct. Thanks
0

If the getMoreData method returns an observable, this code needs to look as follows:

ngOnInit() {
    // I do an ajax call here and populate more fields in the item.
    this.getMoreData().subscribe(
        updatedItem => this.item = updatedItem
    );
}

The subscribe causes the async operation to execute and returns an observable. When the data comes back from the async operation, it executes the provided callback function and assigns the item to the returned item.

Comments

0

You declared item with @Input() decorator as:

 @Input('item') public item: any;

But you use two-way binding on it:

<my-card [(item)]="item"></my-card>

If it is input only, it should be

<my-card [item]="item"></my-card>

Now if you invoke addSubItem() it should display the new added item.

    this.item = this.getMoreData();

The getMoreData() doesn't make sense if you put it in your card component as you want to use the item passed via @Input()

1 Comment

I tried removing the () and it still doesnt work. Also i am only passing in the id of the item, the card component is being used to get the rest of the items data from the server via an AJAX request.
0

Your component interactions are a little off. Check out the guide on the Angular docs (https://angular.io/guide/component-interaction). Specifically, using ngOnChanges (https://angular.io/guide/component-interaction#intercept-input-property-changes-with-ngonchanges) or use a service to subscribe and monitor changes between the parent and the child (https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service).

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.