Rafael Lourenço Rafael Lourenço - 5 months ago 15
AngularJS Question

AngularJS Factory object not being updated on controller and view

I'm with a problem with binding an object of a Factory and a Controller and it's view.

I am trying to get the fileUri of a picture selected by the user. So far so good. The problem is that I am saving the value that file to

overlays.dataUrl
. But I am referencing it on the view and it isn't updated. (I checked and the value is actually saved to the
overlays.dataUrl
variable.

Here goes the source code of
settings.service.js
:

(function () {
"use strict";
angular
.module("cameraApp.core")
.factory("settingsService", settingsService);

settingsService.$inject = ["$rootScope", "$cordovaFileTransfer", "$cordovaCamera"];

function settingsService($rootScope, $cordovaFileTransfer, $cordovaCamera) {

var overlays = {
dataUrl: "",
options: {
sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
destinationType: Camera.DestinationType.FILE_URI
}
};

var errorMessages = [];

var service = {
overlays: overlays,
selectOverlayFile: selectOverlayFile,
errorMessages: errorMessages
};

return service;

function selectOverlayFile() {
$cordovaCamera.getPicture(overlays.options).then(successOverlay, errorOverlay);
}

//Callback functions
function successOverlay(imageUrl) {
//If user has successfully selected a file
var extension = "jpg";
var filename = getCurrentDateFileName();
$cordovaFileTransfer.download(imageUrl, cordova.file.dataDirectory + filename + '.' + extension, {}, true)
.then(function (fileEntry) {
overlays.dataUrl = fileEntry.nativeURL;
}, function (e) {
errorMessages.push(e);
});
}

function errorOverlay(message) {
//If user couldn't select a file
errorMessages.push(message);
//$rootScope.$apply();
}
}
})();


Now the controller:

(function () {
angular
.module("cameraApp.settings")
.controller("SettingsController", SettingsController);

SettingsController.$inject = ["settingsService"];

function SettingsController(settingsService) {
var vm = this;
vm.settings = settingsService;

activate();

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

function activate(){
// Nothing here yet
}
}
})();


Finnally on the view:

<h1>{{vm.settings.overlays.dataUrl}}</h1>
<button id="overlay" class="button"
ng-click="vm.settings.selectOverlayFile()">
Browse...
</button>


Whenever I change the value in the factory, it doesn't change in the view.

Thanks in advance!

Answer

Unfortunately Factories in angularjs are not meant to be used as two way bindings. Factories and Services are only singletons. They are only there to be used when called.

Ex Factory:

app.factory('itemFactory', ['$http', '$rootScope', function($http, $rootScope) {
var service = {};

service.item = null;

service.getItem = function(id) {
    $http.get(baseUrl + "getitem/" + id)
        .then(function successCallback(resp) {
                service.item = resp.data.Data;
                $rootScope.$broadcast("itemready");
        }, function errorCallback(resp) {
            console.log(resp)
        });
};

return service;
}]);

I use the $broadcast so if I call getItem my controller knows to go get the fresh data.

Ex Directive:

angular.module("itemApp").directive("item", ['itemFactory', '$routeParams', '$location', '$rootScope', '$timeout', function (itemFactory, $routeParams, $location, $rootScope, $timeout) {
return {
    restrict: 'E',
    templateUrl: "components/item.html",
    link: function (scope, elem, attr) {
        scope.item = itemFactory.item;

        scope.changeMade = function(){
           itemFactory.getItem(1);
        }

        scope.$on("itemready", function () {
            scope.item = itemFactory.item;
        })
    }
}

}]);

So as you can see in my code above anytime I need a fresh item I use $broadcast and $on to update my service and directive. I hope this makes sense, feel free to ask any questions.