Bryan
Bryan

Reputation: 3009

Angular 2: Update component view from service

I have a component that displays a list of varieties that are retrieved from a service, as you can see here.

export class SellingMenuVarietiesComponent implements OnInit {
    varieties: Variety[];

    constructor(public sellingMenuService: SellingMenuService) { }

    ngOnInit(): void {        
        this.getVarietyList();
    }
    // Get Variety List
    getVarietyList(): void {
        this.sellingMenuService.getVarieties().subscribe(res => {
            this.varieties = res;
        });
    }    
}

I have another component that removes varieties, as seen here:

export class SellingVarietyEditComponent implements OnInit {
    constructor(private sellingMenuService: SellingMenuService) { }

    // Remove variety
    removeVariety(id: number) {        
        this.sellingMenuService.removeVariety(id).subscribe(res => {
            this.sellingMenuService.getVarieties();
        });        
    }
}

In this example, both components are visible on the page at the same time, so when you delete a variety, I want the list to automatically update and reflect that. Currently, the variety will successfully delete from the database, but will remain on the list until I refresh the page. Notice in the removeVariety() function from SellingVarietyEditComponent, I re-invoke the getVarieties() method from my service in an attempt to get the list to update, but it doesn't work.

What am I doing wrong? How Can I accomplish this?

Here are the relevant functions from that service:

@Injectable()
export class SellingMenuService {
    // Get Variety List
    public getVarieties(): Observable<any> {
        return this.http.get(this.varietyList).map(response => {            
            return response.json();
        }, (error: any) => {
            console.log(error);
        });
    }
    // Remove Variety
    public removeVariety(varietyId: any): Observable<any> {
        return this.http.get(this.removeVarietyUrl + varietyId).map(response => {
            return response;
        }, (error: any) => {
            console.log(error);
        });
    }
}

EDIT:

Attempting EventEmitter solution:

In SellingMenuService, I added this code (Notice I added this.updateVarietyList.emit(null); to getVarieties()):

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

// Get Variety List
public getVarieties(): Observable<any> {
    return this.http.get(this.varietyList).map(response => {            
        this.updateVarietyList.emit(null);
        return response.json();
    }, (error: any) => {
        console.log(error);
        // TODO: implement error handling here.
    });
}

In SellingVarietyEditComponent, my remove button looks like this:

<a class="remove-button" (click)="removeVariety(varietyId)" (updateVarietyList)="onVarietyListUpdate($event)" >Remove</a> 

And finally in SellingMenuVarietiesComponent I added this:

onVarietyListUpdate() {
    console.log('test');
    this.getVarietyList();
}

This code however does not work. I don't get any errors, but the onVarietyListUpdate() method never executes. Am I on the right path at all? How can I fix this? The example I've found are confusing and I am having trouble applying them to my code.

Upvotes: 2

Views: 1108

Answers (2)

Parth Chokshi
Parth Chokshi

Reputation: 652

I just realized you have different components. So the solutions changes actually. But I have still kept the old solution which gives you a better idea on tackling the stuff if you need in the future.

NEW SOLUTION

You can make component-interactions.service.ts, in that

updateVarieties = new Subject();
updateVarieties$ = this.updateVarieties.asObservable();
getVarieties(){
this.updateVarieties.next();
}

In here,

export class SellingMenuVarietiesComponent implements OnInit {
    varieties: Variety[];

    constructor(private ciService:ComponentInteractionService, public sellingMenuService: SellingMenuService) { }

    ngOnInit(): void {        
        this.ciService.updateVarieties$.subscribe(()=> {
            this.getVarieyList();
      });
  this.getVarietyList();
    }
    // Get Variety List
    getVarietyList(): void {
        this.sellingMenuService.getVarieties().subscribe(res => {
            this.varieties = res;
        });
    }    
}

In Edit Component,

export class SellingVarietyEditComponent implements OnInit {
    constructor(private ciService:ComponentInteractionService, private sellingMenuService: SellingMenuService) { }

    // Remove variety
    removeVariety(id: number) {        
        this.sellingMenuService.removeVariety(id).subscribe(res => {
            this.ciService.getVarieties();
        });        
    }
}

OLD SOLUTION: IF SINGLE COMPONENT HANDLES EDIT AND SHOW

The thing is you need to update the UI with the new data. One way to do it is return the updated varieties with the success response from backend after deleting the variety.

So, I hit the removeVariety API, perform delete operation with id, and you do something like varieties.getAll() in the backend and send the updated varieties in the success response. And then you just do

this.varieties = res;

This is simple for the Frontend to do.

Another approach is to save the id in a variable before you hit the delete API. Now you have the id you are deleting, you hit the API, it is successful and then you do

export class SellingVarietyEditComponent implements OnInit {
constructor(private sellingMenuService: SellingMenuService) { }

// Remove variety
removeVariety(id: number) {
    let deleteItemId = id; 

    this.sellingMenuService.removeVariety(id).subscribe(res => {
         if(res.status=="SUCCESS"){ /* check if it was successful.(Good practice to include that in API Response*/
         this.deleteItemById(deleteItemId);
       }
     });        
    }

deleteItemById(id:number){
this.varieties.forEach((value)=> {
  if(value.varietyId==id){
      let index = this.varieties.indexOf(value);
      if(index>-1){
         this.varieties.splice(index, 1);
      }
   }
 });
}

I think it is not a good idea to call the getVarieties again. In your code why it is not working is because it is returning you an observable and you are not subscribing to that and updating the varieties array. If you still want to call the get API. You can do

 this.getVarietyList(); 

instead of

this.sellingService.getVarieties();

and make that function again in the edit component the same you have in other component.

Upvotes: 1

Eamonn McEvoy
Eamonn McEvoy

Reputation: 8986

You could use an EventEmitter in your SellingMenuService to emit an event when a variety is removed/updated. Listen for this event in SellingMenuVarietiesComponent and update your array accordingly

Upvotes: 0

Related Questions