canbrian
canbrian

Reputation: 139

How to pass an array from one component to its sub component

After googling lots of related answers and tries, I seemly have to seek help here. I have an angular application, one of its components named stock-subscribe, which is used to display the feeTypes that a customer subscribed. Within this component, I created another component, stock-addsubs, used to display the feeTypes that are not yet subscribed by this customer. enter image description here

Obviously, the two feeType lists can compose one whole list. From stock-subscribe, I can get an array, subscribedFeeIds, which holds all the ids of those subscribed feeTypes.

My requirement is to pass this array, subscribedFeeIds, to the stock-addsubs component so that I can filter out those yet unsubscribed feeTypes based on those ids of the array.

To my best understanding, the data passing from one component to its sub component should be a simple process and neither of two component html templates should be involved for my case. Of the many googled solutions, using @Output and @Input seems the simpler than event emitting. However, none can successfully pass the elements of the array in the sub component.

I can get the expected id list (subscribedFeeIds[]) from the stock-subscribe component, and all the rest code work fine so long as the array passed to the sub component is not EMPTY.

1) stock-subscribe.component.ts

    @Component({
      selector: 'app-stock-subscribe',
      templateUrl: './stock-subscribe.component.html',
      styleUrls: ['./stock-subscribe.component.scss']
    })
    export class StockSubscribeComponent implements OnInit {    
      userSubscribe: ISubscribe;    
      @Output() subscribedFeeIds: any = [];  

      listData: MatTableDataSource<any>;

      constructor(private accountService: AccountService,
                  private stockService: StockService) { }

      ngOnInit(): void {
        this.createSubsList();    
      }

      createSubsList() {    
        this.accountService.getUserAccount()
          .subscribe(account => {      
          let userId = account.id.toString();            
          this.stockService.getUserSubscribes(userId).subscribe((data: ISubscribe[]) => {        
            
            // get the id of subscribed fee type and thenpublish to other component
            for (var i = 0; i < data.length; i++)
            {
              if (data[i].status)
                this.subscribedFeeIds.push(data[i].fees[0].id);
            }
            console.log(this.subscribedFeeIds);

            // prepare the list source for display
            this.listData = new MatTableDataSource(data);
            this.listData.sort = this.sort; 
            }, error => {
              console.log(error);
          });
        }, error => {
          console.log(error);
        });    
      }
    }

2) stock-addsubs.component.ts

    @Component({
      selector: 'app-stock-addsubs',
      templateUrl: './stock-addsubs.component.html',
      styleUrls: ['./stock-addsubs.component.scss']
    })
    export class StockAddsubsComponent implements OnInit {
      
      listData: MatTableDataSource<any>; 

      @Input() subscribedFeeIds: any []; 
      
      constructor(private feesService: FeesService) { }

      ngOnInit(): void {
        this.createFeeList();    
      }    

      createFeeList() {                
        this.feesService.getFeeList().subscribe((data: IFee[]) => { 
          
          // filter those already subscribed
          for (var i = 0; i < this.subscribedFeeIds.length; i++)
            {
              for (var j = 0; j < data.length; j++)
              {
                data = data.filter(f => f.id != this.subscribedFeeIds[i]); 
              }          
            }

          // prepare data to display
          this.listData = new MatTableDataSource(data);      
          }, error => {
            console.log(error);
        });
      } 
}

Upvotes: 0

Views: 1446

Answers (1)

KShewengger
KShewengger

Reputation: 8269

You can implement either one of these methods:

1.) Enhance your @Input and @Output based on your code above:

stock-subscribe.component.ts

@Component({...})
export class StockSubscribeComponent implements OnInit {    

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

   list: any[] = [];               // To store your ids, do not use the @Output variable above to push things inside a loop, you may not want to continuously emit data for an nth number of the sequence.

   ...

   createSubsList() {    

      ...
    
      for (var i = 0; i < data.length; i++) {
        ...
    
        if (data[i].status)
           this.list.push(data[i].fees[0].id);      // Push IDs on this.list not on this.subscribedFeeIds
      }  

      this.subscribedFeeIds.emit(this.list);        // After the loop finishes, emit the final list value
                                                    // Do not use .push() on @Output variable, this is an emission variable so use .emit()

   

   }

}

or you could also do any of these methods when handling your array list:

// METHOD #1
this.filteredIds = [];
for(const { status, fees } of FEES_DATA) {
   if (status) filteredIds.push(fees[0].id);
}

OR    

// METHOD #2
this.filteredIds = FEES_DATA
  .filter(({ status }) => status)
  .map(({ fees }) => fees[0].id);



// Then emit the filtered ids
this.list.emit(this.filteredIds);

stock-addsubs.component.ts

@Component({...})
export class StockAddsubsComponent implements OnInit {

   // Since you are waiting for emission from StockSubscribeComponent which the 
   // emission also waits till the loop is finished based on the code above, better to
   // implement the @Input like this so as to avoid processing a an empty list

   @Input() set subscribedFeeIds(list: any[]) {
       if (list && list.length) {              // If the list now has items, call createFeeList() function
         this.createFeeList();
       }
   } 

   ...

}

____

or

@Component({...})
export class StockAddsubsComponent implements OnInit, OnChanges {    // Add OnChanges

  @Input() subscribedFeeIds: any []; 


  ngOnChanges({ subscribedFeeIds }: SimpleChanges) {          // SimpleChanges from @angular/core

    if (subscribedFeeIds && subscribedFeeIds.currentValue && subscribedFeeIds.currentValue.length) {
      this.createFeeList();
    }

    // You can also console the subscribedFeeIds and check it's value or changes
    console.log(subscribedFeeIds);

  }

}

Have created a Stackblitz Demo for your reference


2.) Use RxJS Subject or BehaviorSubject to emit data

Use this Stackblitz Demo for your reference.

Upvotes: 1

Related Questions