Da Bazz
Da Bazz

Reputation: 61

Angular2 update all clients UI after backend change by 1 client

I have an Angular2 application that communicates with a c# WebApi. Upon loading I make a GET call to retrieve a list of objects, subscribe them to an array and display the list using ng2-smart-table. My application works for a single user without any problems, but I don't get the correct behaviour when I add more site users.

Based on whichever type of user is logged in I display 2 different views, both views use the same component and thus have access to the same lists. The role type of the user determines which parameters he/she can change.

Imagine the following scenario: I have 2 users; User A and User B. Both are logged in in a different browser tab/window, have a different role type and see the 2 different views.

Step 1: User A actions On loading the webpage the browser for User A calls the Get method to fill the lists which are displayed on their screen. User A then clicks on the "Mark" button for a certain object in the list, this sets a boolean property to True in the backend for that object. The updated object is then send back to the frontend and updated in the list.

So far so good. User A himself sees the UI updated as the object is now "Marked".

Step 2: User B actions User B had already loaded the page and thus has already called the Get method to fill the lists on his end. But since User A has since updated an item, User B must also see that that specific item is now set to "Marked". This however doesn't happen, the list in User B's browser is different from the one in User A's browser.

Question How do I implement this scenario, so that when any updates to the objects by 1 user are also preceived by the other users?

Other Info: All api calls that edit an object return the edited object, as displayed in the example below.

Edit: I was able to implement the function by following this post: http://beyondscheme.com/2016/angular2-discussion-portal . However I want to avoid calling the GET All API call constantly as it would create massive data flows that are seemingly waisted as all I need is 1 item.

order-information.component

GET

ngOnInit() {
    this.orderInformationAddEditList = new LocalDataSource();
    this.orderInformationListDataSource = new LocalDataSource();
    if (this.AuthService.isLoggedIn()) {
      this.OrderInformationService.getAll().subscribe(orderInfo => {
        this.orderInformationList = orderInfo;
        this.orderInformationListDataSource.load(this.orderInformationList);
        //this.NotificationsService.success("Success", "Loaded all items.", { timeOut: 2000, clickToClose: true, showProgressBar: true });
        console.log("Loaded all items");

        if (!this.AuthService.hasRole("desk")) {
          this.userIsDesk = false;
        }
      },
        e => {
          console.log("Error: " + e);
          if (e.status == 0) {
            this.NotificationsService.error("Error", "Unable to connect to API", { timeOut: 0, clickToClose: true });
          }
          if (e.status == 500) {
            console.log(e.json().data);
            this.NotificationsService.error(e.json().data.Name, e.json().data.Message, { timeOut: 0, clickToClose: true });
          }
        })
    } else {
      this.router.navigate(['']);      
    }
  }

Mark

markOrderInformation(id: number, event: any) {
this.apiAttempts++;

this.OrderInformationService.markOrderInformation(id).subscribe(orderInfo => {
  console.log("Marked: " + orderInfo.id);
  this.NotificationsService.success("Success", "Marked: " + orderInfo.id, { timeOut: 2000, clickToClose: true, showProgressBar: true });
  this.updateOrderList(orderInfo, false);
  event.confirm.resolve(orderInfo);
  this.apiAttempts = 0;

},
  e => {
    var data = e.json();
    if (e.status == 0) {
      this.NotificationsService.error("Error", "Unable to connect to API. Please try again later..", { timeOut: 0, clickToClose: true });
    }
    if (e.status == 401) {
      this.NotificationsService.error(data.Name, data.message, { timeOut: 0, clickToClose: true });
    }
    if (e.status == 500) {
      if (this.apiAttempts < 4) {
        this.NotificationsService.error("Error", "Verify your input and try again. Attempts: " + this.apiAttempts, { timeOut: 0, clickToClose: true });
      } else {
        this.NotificationsService.error(data.Name, data.Message, { timeOut: 0, clickToClose: true });
        this.apiAttempts = 0;
      }
    }
  })
}

order-information.service

GET

  getAll(): Observable<OrderInformation[]> {
    return this.http.get(`${this.baseUrl}`, new RequestOptions({ headers: this.getAuthorizationHeaders() }))
    .map(mapOrderInformationList);
  }

Mark

  markOrderInformation(id: number) {
    return this.http.put(`${this.baseUrl}/` + id + `/mark`, { 
    },
    { headers: this.getAuthorizationHeaders() })
    .map(mapOrderInformation);
  }

Upvotes: 1

Views: 1189

Answers (1)

Da Bazz
Da Bazz

Reputation: 61

Thanks to the tutorial I found here: http://beyondscheme.com/2016/angular2-discussion-portal , I was able to send a GET request every 5 seconds. This was however extremely expensive as I'm sending alot of data accross the web.

I fixed this by returning a smaller list that holds objects that were only edited recently. I did this by adding a new DateTime column to my Sql entity called: lastUpdated. Now whenever I update an object I change the lastUpdated attribute to DateTime.Now().

Finally I created a new GET request method in the Api called GetUpdatedList, that returns all items that were updated within a specific time frame, I took 3 minutes cause I wasn't sure about how big latency can be but I think 1 minute is more than enough. The list received from the Api is then parsed and edited into the existing array in Angular2.

public List<Object> GetUpdatedList() {
 try {
    DateTime now = DateTime.Now;
    DateTime before = now.AddMinutes(-3);
    return context.Xdock_controle.Where(order => order.lastUpdated != null && order.lastUpdated <= now && order.lastUpdated >= before).ToList();
 } catch (Exception ex) {
    Logger.log(ex.Message);
    throw ex;
 }
}

Upvotes: 1

Related Questions