Pregunta

I've spent the night on trying to figure this out and have finally decided to give up and ask for help.

I'm building a web-app with AngularJS that is designed to work with flakey connections (mobiles).

I'm trying to implement the functionality for a user to add a object (whether that's an appointment, book, etc is irrelevant) to the server.

Service that handles syncing objects with the server:

angular.module('App')
  .service('syncUp', function syncUp($http, $q, app) {

    this.addObject = function addObject(object) {
      var deferred = $q.defer();
      app.inSync = false;

      var httpConfig = {
        method: 'POST',
        url: 'http://myurl.dev/app_dev.php/api/add-object',
        data: object
      }

      function persist() { setTimeout(function() {
          $http(httpConfig).
              success(function(data, status) {
                  app.inSync = true;
                  deferred.resolve(data.id);
              }).
              error(function(data, status) {
                  app.inSync = false;
                  persist();
              });
      }, 3000);
      };

      persist();

      return deferred.promise;
    }
  });

'app' service that the status bar is bound to:

'use strict';

angular.module('App')
  .service('app', function app($http, $q) {
    this.inSync = true;
  });

Template binding to the 'app' service inSync property:

<div class="status" ng-class="{'insync':inSync}"></div>

Specific object service that sends data from the controller to the syncUp service:

this.addBook = function(book)
      {
        var tempId = syncUp.generateUid();
        this.books[tempId] = book;
        this.books[tempId].tempId = tempId;
        syncUp.addObject({
          'type': 'book',
          'data': this.books[tempId]
        }).then(function(newId) {
            booksRef[newId] = book;
            delete booksRef[tempId];
          }, function() {});
      }

Everything is working as it should (data is being persisted to the server and the ID is being returned and replacing the tempId just fine. The problem is, when the inSync key on the 'app' service is updated, the class isn't added/removed from the div as it should be with ng-class in the template. If I load another route, that will force iterate through whatever internal cycle angular is doing and update the class on the template.

I've tried all manner of $apply() solutions, moving where the app.inSync key is set back to true, looping a function watching it. It's being set in all the right places (from debugging I know it's set back to true correctly), I just can't figure out how to make the change appear on the UI.

I tried: $rootScope.$apply(function() { app.inSync = true; });

Which gave me an error (already running a digest, or something).

So I tried the 'safeApply' version that has been circulated on many answers/blogs, which didn't throw the error, but didn't work either.

As far as I can figure out, the UI should be updated when promises are resolved (both the http and my syncUp.addObject promise are resolved, so I'm not sure why it's not working.

Any ideas? I need to keep the current implementation of promises to be able to set the returned ID from the server on the added object, to avoid a circular-dependency issue between the syncUp and object angular services.

Edit:

And the status bar directive:

angular.module('App')
  .directive('navigation', function (app) {
    return {
      templateUrl: '/app/views/navigation.html',
      restrict: 'E',
      link: function (scope, element, attrs) {
          scope.inSync = app.inSync;
      }
    }
  });
¿Fue útil?

Solución

References you make in templates refer to objects on the current $scope. Services do usually not create or add anything to the $scope, so putting properties on a service, will not make them available to the template. To get stuff on the $scope, you need to use a controller. You can use the ng-controller directive to reference a controller, you'll find examples of this in the first AngularJS tutorials.

What you should do is create a controller and have it listen for events from the service. Here's an example of how to do that.

That's the nice way; You might also be able to get away with it by putting the inSync = true on the $rootScope as such;

service('syncUp', function syncUp($http, $q, app, $rootScope) {
    // (...)
    $rootScope.inSync = true;

Otros consejos

It looks like you're hoping to see bindings operating between a service ('app') and a template. It's hard to tell if we're not seeing the entire picture. Going on that assumption, you need to refactor so that you are setting up bindings on a controller.

I would expect the controller setup to look something like this:

angular.module('App')
.controller('app', function app($http, $q, $scope) {
    $scope.inSync = true;
});

Now you will have two-way binding hooked-up on the 'inSync' property.

Otherwise, your template looks fine.

If I'm off base, please update your question with more context, or better yet make a fiddle to boil down the problem.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top