AngularJS 1.5.x Service Not Bound Correctly and is Not Updating in Controllers

69 Views Asked by At

For some reason when I update the factory value the associated views aren't updating. I'm binding to an object in the service not a string so I don't see what I've done wrong, but there is definitely something wrong with the implementation. I've reduced the code for brevity, and added it to a plunker. I thought I was over screwing services up, but apparently not. I even read this article again to make sure what I thought I understood was correct.

The value of the title is picked up the first time and you see 'Welcome!' in the title, but after that it won't update to 'Up Next!'.

https://plnkr.co/edit/ma1SDJyIKoFPWznXdxTO?p=preview

<!DOCTYPE html>
<html ng-app="app">
<head>
    <html-title></html-title>
</head>
<body>

<main></main>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>

<script>

(function () {

    'use strict';

    function MainController(HtmlTitleFactory) {

        HtmlTitleFactory.set('Welcome!'); // title gets set

        // check title was changed
        this.data = HtmlTitleFactory.get();

        setTimeout(function() {
            // updates title
            HtmlTitleFactory.set('Up Next!');

            // title looks to be set, but changes not picked up in view
            console.log(HtmlTitleFactory.get());
        }, 3000);
    }

    MainController.$inject = [
        'HtmlTitleFactory'
    ];

    var main = {
        template: '<div>{{ vm.data.title }}</div>',
        controller: MainController,
        controllerAs: 'vm'
    };

    ////////////////////////////

    function HtmlTitleController(HtmlTitleFactory) {

        var vm = this;

        //vm.title = HtmlTitleFactory.get().title;
        vm.title = HtmlTitleFactory.get();
    }

    HtmlTitleController.$inject = [
        'HtmlTitleFactory'
    ];

    var htmlTitle = {
        template: '<title>{{ vm.title.title }}</title>',
        controller: HtmlTitleController,
        controllerAs: 'vm'
    };

    ////////////////////////////

    function HtmlTitleFactory() {

        var service = {
            title: 'Default'
        };

        function set(title) {
            // originally tried this since bound to object this should work
            // service.title = title;

            // wasn't working so tried this as well
            angular.extend(service, { 
               title: title
            });
        }

        function get() {
            return service;
        }

        return {
            set: set,
            get: get
        };
    }

    HtmlTitleFactory.$inject = [];

    ////////////////////////////

    angular
        .module('app', [])
        .component('main', main)
        .component('htmlTitle', htmlTitle)
        .factory('HtmlTitleFactory', HtmlTitleFactory);

})();

</script>

</body>
</html>
1

There are 1 best solutions below

2
On BEST ANSWER

setTimeout is event which would not let tell angular that changes has occurred in angular context as its custom event which runs outside angular context. In that case digest cycle doesn't get fire, you need to run it manually.

Ideally you should use $timeout which works as similar as setTimeout and it runs a digest cycle when callback gets fired.

$timeout(function() {
   HtmlTitleFactory.set('Up Next!'); // update title
   console.log(HtmlTitleFactory.get()); // title looks to be set but not picked up in view
}, 3000);

Plunkr