1

I need to keep the data updated in a child component to do some maths

I have my parent component:

@Component({
  selector: 'car-brand-index',
  templateUrl: './index.component.html',
  styleUrls: ['./index.component.scss'],
  providers: [CarBrandService, Response, SessionService, ServerService,     ListTableComponent, TableAction, Table, TableRow, TableRowField]
})
export class CarBrandIndexComponent implements OnInit {
  table = new Table();

  constructor(private mainService : CarBrandService) {
    this.refresh(this.table.page,this.table.size);
}

 refresh(pageNumber : number, pageSize : number){
   this.mainService.get(pageNumber, pageSize).then(result => {

     this.table.total = result.data.total;
   });
 }

And in the view I have:

 <list-table [info]="table" (updateTable)="updateTable($event)"></list-table>

So i'm sending the var table to the component list-table

And in my child component I have

@Component({
  selector: 'list-table',
  templateUrl: './list-table.component.html',
  styleUrls: ['./list-table.component.scss'],
  providers: [ListTableService]
})
export class ListTableComponent implements OnInit {
  @Input() 
  info: Table;
  @Output()
  updateTable = new EventEmitter();
  pages : number[] = [];
  constructor(public listTableService : ListTableService) {
  }

  updateFooter() {
    if(this.info){
      var amountofPages = this.info.total % this.info.size == 0 ? ( this.info.total % this.info.size ) : ( this.info.total % this.info.size ) + 1;
      for(var i = 1; i <= amountofPages; i++){
        this.pages.push(i);
      }
    }
  }

  ngOnInit(){
    this.updateFooter();
  }
}

The issue is that in the line:

 var amountofPages = this.info.total % this.info.size == 0 ? ( this.info.total % this.info.size ) : ( this.info.total % this.info.size ) + 1;

the key total is still undefined, because the view is done before the service call is completed.

I tried to do the refresh function with promise, but the result is the same. Is there a way I can wait to complete the variable before sending it to the child compoenent or that the child componet knows when the value was updated so it can execute the math?

Faabass
  • 1,394
  • 8
  • 29
  • 58
  • Use a service, with published observable events. Components can subscribe to these events, and perform math when the events fire. I think the Flux pattern will help you: http://stackoverflow.com/questions/42219858/how-can-i-maintain-the-state-of-dialog-box-with-progress-all-over-my-angular-2-a/42221273?noredirect=1#comment71607237_42221273 – Michael Kang Feb 15 '17 at 01:44
  • I'm using a service to get all the data from the backend... so you mean I need to have a service just for the child component so the parent store the information there? But in my case the data is being updated, but the updateFooter() function is not fire AFTER that update. So the var amountofPages is outofdate – Faabass Feb 15 '17 at 02:05
  • If not, what I can do is to do that math on the parent, and then sent the final value, in that way I don't need any function in the child, but is not the best approach – Faabass Feb 15 '17 at 02:08
  • Sorry, its probably not the answer you're looking for. I think it could be useful in your case, with a bit of re-work to your service. – Michael Kang Feb 15 '17 at 03:07
  • Can you explain a bit which is the solution based on my code? Thanks! – Faabass Feb 15 '17 at 05:18

1 Answers1

1

As mentioned, a service would be the best way to go. You don't need to create a new Service, you can utilize the one you have and just make some additions. So add a Subject that the child component will subscribe to. I have a simplified example set up for you:

The service, here I'm using your ListTableService

private tableVar = new Subject<number>();

newTable = this.tableVar.asObservable();

tableReceived(table: string) {
  this.tableVar.next(table);
}

Parent component, when data has been retrieved

 refresh(pageNumber : number, pageSize : number){
   this.mainService.get(pageNumber, pageSize).then(result => {
     this.table.total = result.data.total;
     this.listTableService.tableReceived(this.table); // add this!
   });
 }

And in your child, move the call of the updateFooter-function inside the subscription of table instead. So in your child constructor:

constructor(public listTableService : ListTableService) {
   listTableService.newTable.subscribe(table => {
      this.info = table;
      this.updateFooter();
   })
}

Example plunker.

One thing to notice, is that you MUST remove the provider of the service in the child component:

providers: [ListTableService]

Because if you do not, you will end up with a new instance of the service, that will not contain the table value set by the parent. When removing this provider, Angular will follow the hierarchy and find the next level provider, which would in your case be the parent with provider ListTableService, which means that your parent and child will share the same instance of the service.

As a sidenote, I would though recommend that you do not declare providers in your components, if you do not explicitly want to have a new instance of a service. To declare your providers in the NgModule is much cleaner and easier to maintain :)

More about component interaction: shared service (+ other possibilities)

AT82
  • 71,416
  • 24
  • 140
  • 167