Angular keeping objects in sync across app

This is an angular question and also a javascript question.

I have a lot of objects in our angular app coming from the backend that needs to stay up to date with the latest developments. I am having trouble setting up a good data binding to sync this app.

I created a DataService that connects via websocket to our server. Retrieves data as needed and caches it locally in a named object store

. For example:

When the controller needs a list of users, it will query the DataService like this:

DataService.get(users, {}).then(
  /* function to set something on the scope */
)
// (or as a resolve)

      

The DataService will fetch users from our backend and a) cache users locally in the store object and b) return an array with the result.

When another controller needs the same data, we just return the cache.


This works great except when the data changes and the backend tells the DataService to do so. The DataService can update its local storage cache, but the controller is unaware of these changes. Sample doom scripts:

  • When the controller needs a complete list of users, and after a while a new user is added (outside the angular environment via the backend), how can the DataService update the previously returned array?
  • Controllers can query a specific subset of users (for example, all users from city A). When new users from City A log in, how does the DataService know that this was something that the controller has been interested in for some time? If the DataService stores all the requests, it needs a way to map new users (for example) to those requests.
+3


source to share


2 answers


I'm a big fan of the $ resource service in Angular core. When you use this, the properties returned from your data are assigned to the $ resource instance object. So

// js
scope.myThing = $resource('thing_url').get();

// html
{{ myThing.myProperty }}

      

This works using Angular's digest loops. When the resource is returned, the pattern "magically" works. This is because the service $resource

starts a digest loop when it completes its request and the bindings are now displaying the correct information. You can do the same with observers:

scope.$watch('myThing.myProperty', function (newValue) { /... });

      

You can do something like this: create a service that returns an object containing results from your server. Assuming you can hook up to an event at your websocket layer, you can start a digest loop whenever the data has been updated. This way the controllers and templates will be updated.


Question Author:

Removing digest loops in a service is only half the job. The other half ensures that you don't overwrite references to objects (one model) and arrays (set of models). You can achieve this by using angular.copy

the upgrade of local models in your life
.



pseudocode of our working solution:

  • Create a storage object inside the API service (serves as a cache of all database data)
  • Whenever the controller needs certain information, it requests an API service.
  • The API service requests data from the backend and stores it locally in the storage object.
  • Besides the store object, we also created a minimal query parser capable of filtering the store object in the same way as the backend filters (we have a mongo style query API where queries are objects like {city: "amsterdam"}

    or {city: {"!": "amsterdam"}}

    ).
  • Run the query in local storage, save the query and results, and pass it back (query or controller).
  • Whenever the model is updated, repeat any queries that might be related. If the results have changed, use copy.angular

    to modify the object / array without losing the reference. After that, start the digest cycle so that the controllers are aware of the data changes.

This solves all doomscenarios:

When the controller needs a full list of users and after a while a new user is added (outside the Angular framework via the backend), how can the DataService update the previously returned array?

angular.copy

Controllers can query a specific subset of users (for example, all users from city A). When new users from City A log in, how does the DataService know that this was something that the controller has been interested in for some time? If the DataService stores all the requests, it needs a way to map new users (for example) to those requests.

The service will automatically reprogram all requests when the underlying data changes. Thus, the service will keep all data in the entire corner space up to date.

+1


source


There are two ways to do this. Both require you to be inducted $rootScope

into your service.

The first way is good if you are using something like $scope.users = DataService.users

in your controllers. Just activate all scopes to update the incoming message, for example:

$rootScope.$apply(function () {
    users.push(newUser);
});

      

The second way is broadcasting an event from the root area when receiving a message from the server:



$rootScope.$broadcast('server:update', data);

      

And listen to it in your controllers:

$scope.$on('server:update', function (data) {
    // Whatever you need to do
});

      

0


source







All Articles