two-way binding in angular factories - angularjs

I wrote an Angular factory to store an object that I need to pass to diferent controllers. The factory looks like this:
app.factory('SearchEngineConfigs', function () {
var configs = {
isInternational: false,
TripType: 1,
journeys: []
}
var _setInternational = function(val) {
configs.isInternational = val;
}
var _setTripType = function(val) {
configs.TripType = val;
}
var _setJourneys = function(journeys) {
configs.journeys = journeys;
}
var _getConfigs = function() {
return configs;
}
return {
setInternatioanl: _setInternational,
setTripType: _setTripType,
setJourneys: _setJourneys,
getConfigs: _getConfigs
}
});
So I have this factory injected in my controllers. In one of the controllers I'm setting the values of configs' factory like this:
SearchEngineConfigs.setInternatioanl($scope.searchEngine.isInternational);
SearchEngineConfigs.setTripType($scope.searchEngine.TripType);
SearchEngineConfigs.setJourneys($scope.journeys.slice());
So far so good. What is happening now is that everytime I change let's say the $scope.searchEngine.isInternational without calling the factory method SearchEngineConfigs.setInternatioanl this change still being reflected into the factory and thus, updating this property in another controller that is using that factory at the same time of the first controller. How can I avoid this to happen? I only want to change the value of the object inside the factory when I explicity make a call to the correponding factory method.

You could use angular.copy to avoid allowing any references to your internal state objects for existing outside your factory.
Note that you could have to do this on both the input and output, as either could create a leak.
One way of ensuring this was consistant would be to use a decorator function:
function decorate(func) {
return function() {
const argumentsCopy = _.map(arguments, a => angular.copy(a));
const result = func.apply(factory, argumentsCopy);
return angular.copy(result);
};
}
...which in turn is used like this:
var factory = {
setInternatioanl: decorate(_setInternational),
setTripType: decorate(_setTripType),
setJourneys: decorate(_setJourneys),
getConfigs: decorate(_getConfigs)
}
return factory

You can either use the new keyword to have different instances of the service.
Something like, var searchEngineConfigs = new SearchEngineConfigs(); and then use it to invoke factory methods.
Or, you can use the angular.copy() while setting the variables in your service to remove the reference which is causing the update in service.
Something like,
var _setInternational = function(val) {
configs.isInternational = angular.copy(val);
}

Related

AngularJS 1.6.9 controller variable bound to service variable doesn't change

I have 2 components which are both accessing a service. One component delivers an object and the other one is supposed to display it or just receive it. The problem is that after the initialization process is finished the variable in the display component doesn't change.
I have tried using $scope , $scope.$apply(), this.$onChanges aswell as $scope.$watch to keep track of the variable, but it always stays the same.
This controller from the display component provides a text, which is from an input field, in an object.
app.controller("Test2Controller", function ($log, TestService) {
this.click = function () {
let that = this;
TestService.changeText({"text": that.text});
}
});
That is the the service, which gets the objekt and saves it into this.currentText.
app.service("TestService", function ($log) {
this.currentText = {};
this.changeText = function (obj) {
this.currentText = obj;
$log.debug(this.currentText);
};
this.getCurrentText = function () {
return this.currentText;
};
});
This is the controller which is supposed to then display the object, but even fails to update the this.text variable.
app.controller("TestController", function (TestService, $timeout, $log) {
let that = this;
this.$onInit = function () {
this.text = TestService.getCurrentText();
//debugging
this.update();
};
//debugging
this.update = function() {
$timeout(function () {
$log.debug(that.text);
that.update();
}, 1000);
}
//debugging
this.$onChanges = function (obj) {
$log.debug(obj);
}
});
I spent quite some time searching for an answer, but most are related to directives or didn't work in my case, such as one solution to put the object into another object. I figured that I could use $broadcast and $on but I have heard to avoid using it. The angular version I am using is: 1.6.9
I see a problem with your approach. You're trying to share the single reference of an object. You want to share object reference once and want to reflect it wherever it has been used. But as per changeText method, you're setting up new reference to currentText service property which is wrong.
Rather I'd suggest you just use single reference of an object throughout and it will take care of sharing object between multiple controllers.
Service
app.service("TestService", function ($log) {
var currentText = {}; // private variable
// Passing text property explicitly, and changing that property only
this.changeText = function (text) {
currentText.text = text; // updating property, not changing reference of an object
$log.debug(currentText);
};
this.getCurrentText = function () {
return currentText;
};
});
Now from changeText method just pass on text that needs to be changed to, not an new object.

Is having a service that returns an object instead of using DI an Angular antipattern?

I have a situation at work where a developer created an angular service that returns a hook to an object that then is used to create an instance. Something like this...
var Item;
Item = function($q) {
return Item = (function() {
function Item(data) {
....
}
})
}
Then in the controller...
function ItemImport($q){
this.myItem = new this.Item(data);
}
This so this is using a singleton to exclusively create an instance which seems wrong to me. I would think something like this would be better...
function ItemImport($q, Item){
this.data = {}
Item.data = this.data
}
Is this an anti-pattern? Or is it just an alternative way to create a service?

How to store controller functions in a service and call them in AngularJS

I need to execute functions of some controllers when my application ends (e.g. when closing the navigator tab) so I've thought in a service to manage the list of those functions and call them when needed. These functions changes depending on the controllers I have opened.
Here's some code
Controller 1
angular.module('myApp').component('myComponent', {
controller: function ($scope) {
var mc = this;
mc.saveData = function(objectToSave){
...
};
}
});
Controller 2
angular.module('myApp').component('anotherComponent', {
controller: function ($scope) {
var ac = this;
ac.printData = function(objects, priority){
...
};
}
});
How to store those functions (saveData & printData) considering they have different parameters, so when I need it, I can call them (myComponent.saveData & anotherComponent.printData).
The above code is not general controller but the angular1.5+ component with its own controller scope. So the methods saveData and printData can only be accessed in respective component HTML template.
So to utilise the above method anywhere in application, they should be part of some service\factory and that needs to be injected wherever you may required.
You can create service like :
angular.module('FTWApp').service('someService', function() {
this.saveData = function (objectToSave) {
// saveData method code
};
this.printData = function (objects, priority) {
// printData method code
};
});
and inject it wherever you need, like in your component:
controller: function(someService) {
// define method parameter data
someService.saveData(objectToSave);
someService.printData (objects, priority);
}
I managed to make this, creating a service for managing the methods that will be fired.
angular.module('FTWApp').service('myService',function(){
var ac = this;
ac.addMethodForOnClose = addMethodForOnClose;
ac.arrMethods = [];
function addMethodForOnClose(idModule, method){
ac.arrMethods[idModule] = {
id: idModule,
method: method
}
};
function executeMethodsOnClose(){
for(object in ac.arrayMethods){
ac.arrMethods[object].method();
}
});
Then in the controllers, just add the method needed to that array:
myService.addMethodForOnClose(id, vm.methodToLaunchOnClose);
Afterwards, capture the $window.onunload and run myService.executeMethodsOnClose()

How to pass variables within controllers in AngularJS

Hi I need to use a variable from the result scope of one controller to another controller. I can achieve this by using nested controller but in my case my controller 2 is not a child of controller 1. I somehow able to achieve my output with the following controller but still i want to know is this best practice if not how can i pass variables between various controllers.
angular.module('test',[])
.factory('PassParameter', PassParameter)
function PassParameter(){
var thisValue = {};
return {
getParameter: function () {
return thisValue;
},
setParameter: function (setValue) {
_.extend(thisValue, setValue);
},
removeParameter : function(value) {
_.omit(thisValue, value);
}
};
};
i Pass an object to setParameter function and get by its value from getParameter function.
this is the way that I'm passing info between controllers.
*Note that I'm using angular.copy so I won't lose reference, this way when "obj" is changed, you don't need to get it again.(works only on objects {})
angular.module('test').service('mySrv',
function () {
var obj = {};
this.getObj = function(){
return obj;
};
this.setObj = function(obj){
obj = angular.copy(obj);
};
});

AngularJS, Using a Service to call into a Controller

I am new to Angular, what I would like to accomplish is: From a Service / Factory to call methods directly into a controller.
In the following code, I would like from the valueUserController I would like to create a method from the service myApi and set the value inside the valueController.
Here is my code:
modules/myApi.js
var MyApi = app.factory('MyApi', function()
var api = {};
api.getCurrentValue = function() {
// needs to access the Value controller and return the current value
}
api.setCurrentValue = function(value) {
// needs to access the Value controller and set current value
}
api.getValueChangeHistory = function() {
// access value controller and return all the values
}
);
controllers/value.js
app.controller('valueController', function($scope) {
var value = 0;
function getValue() {
return value;
}
function setValue(inValue) {
value = inValue;
}
// ......
});
controllers/valueUser.js
app.controller('valueUserController', function($scope, myApi) {
function doStuff() {
var value = myApi.getValue();
value++;
myApi.setValue(value);
}
});
I am finding to do this in AngularJS pretty difficult and I haven't found any similar post on here.
Thanks for any help,
Andrea
Trying to communicate with a specific controller from a service is not the correct way of thinking. A service needs to be an isolated entity (which usually holds some state), by which controllers are able to interact with.
With this in mind, you can use something like an event pattern to achieve what you are looking for. For example, when your service completes some particular process, you can fire an event like so:
$rootScope.$broadcast('myEvent', { myValue: 'someValue' });
Then any controller in your system could watch for that event and perform a specific task when required. For example, inside your controller you could do the following:
$scope.$on('myEvent', function(event, data){
// Do something here with your value when your service triggers the event
console.log(data.myValue);
});

Resources