vedmaque
vedmaque

Reputation: 323

Changing controller $scope vars dynamically

So. I have simple controller and service:

angular
    .module('field', [])
    .controller('FieldController', function(FieldService) {
         var vm = this;
         vm.name = FieldService.getName();
    })
    .service('FieldService', function() {
         var name = 'John'
         this.getName = function() {
             return name;
         };
         this.setName = function(newName) {
             name = newName;
         };
    })
    ;

Then i have some $interval in anotherService, that getting data every 1 second and calling FieldService.setName:

    .service('UpdateService', function($http, FieldService) {
        $interval(function() {
            $http.get('/somUrl')
                .then(function(response) {
                    FieldService.setName(response.name);
                });
        });
    })

But it won't change my HTML. If i switch from primitive to object in returning value getName, then it's working.

Is there another approach? I personally think, that this structure i created is bad, but can't understand how it should be done.

Upvotes: 0

Views: 93

Answers (4)

Hazarapet Tunanyan
Hazarapet Tunanyan

Reputation: 2865

There are several ways to solve that problem.

1) Move the $interval to controller.Then you will have a variable, which holds that data and you can bind it in view

2) You can use AngularJs Events.$rootScope will help you to send signal and catch it wherever you want.

If you want more info about this solutions, you can see it here: http://www.w3docs.com/snippets/angularjs/bind-value-between-service-and-controller-directive.html

Upvotes: 0

sma
sma

Reputation: 9597

JavaScript is always pass-by-value, but when your variable is an object, the 'value' is actually a reference to the object. So in your case, you are getting a reference to the object, not the value. So when the object changes, that change isn't propagated like a primitive would be.

Your code seems a bit incorrect, too. You are setting the value of response.name to FieldService.setName, which is actually a function.

If you want to use the getter/setter approach you have listed, then you could use events to let the controller know that name has changed.

.service('UpdateService', function($http, FieldService, $rootScope) {
        $interval(function() {
            $http.get('/somUrl')
                .then(function(response) {
                    FieldService.setName(response.name);
                    $rootScope.$broadcast('nameChanged', {
                        name : response.name
                    });
                });
        });
    })

  .controller('FieldController', function(FieldService, $scope) {
         var vm = this;
         vm.name = FieldService.getName();

         $scope.$on('nameChanged', function (evt, params) {
             vm.name = params.name;
         });
    })

Another way to accomplish this is to use a $scope.$watch on the service variable in the controller:

.controller('FieldController', function($scope, FieldService) {
         $scope.name = FieldService.getName();
         $scope.$watch(function () {
            return FieldService.getName();
         }, function (newVal, oldVal) {
            if (newVal !== oldVal) {
                $scope.name = newVal;
            }            
         });
    })

Upvotes: 1

Vipul Agarwal
Vipul Agarwal

Reputation: 1631

I would use it this way:

angular
    .module('field', [])
    .controller('FieldController', function($scope, FieldService) {
         $scope.name = function(){
            FieldService.getName();
         };
    })
    .service('FieldService', function() {
         var name = 'John'
         this.getName = function() {
             return name;
         };
         this.setName = function(newName) {
             name = newName;
         };
    });

Use name() in your html to see the update value.
And your other service:

.service('UpdateService', function($http, FieldService) {
        $interval(function() {
            $http.get('/somUrl')
                .then(function(response) {
                    FieldService.setName(response.name);
                });
        }, 1000);
    })

There are numerous ways in which you can achieve this. No way is the best way. Depends on person to person.

Hope this helps.

Upvotes: 0

Lars Juel Jensen
Lars Juel Jensen

Reputation: 1683

I would move my $interval function inside a controller and then just update a $scope attribute every second. Then Angular will take care of the rendering.. Or you must also use an $interval function in your controller which gets the service content (ie FieldService.getName) every second.

Upvotes: 0

Related Questions