I want to be able to preserve scope data between route changes, so I created a service to return saved data to a controller for it to initiate on scope.
app.factory('answerService', function () {
var answers = {
1 : { text : "first answer" },
2 : { text : "second answer" }
}
return {
getAllAnswers: function () {
return answers;
},
getFirstAnswer: function () {
return answers[1];
}
}
});
In my controller, I initiate the first answer by calling the service to get the first answer.
app.controller('myController', function ($scope, answerService) {
$scope.firstAnswer = answerService.getFirstAnswer();
$scope.answers = answerService.getAllAnswers();
});
http://jsfiddle.net/hientc/gj5knrg7/2/
The problem I'm having is that $scope.firstAnswer is somehow also being binded to $scope.answers. I don't want that, I only want the input to bind to scope.firstAnswer.text
What am I doing wrong?
This is because answers[1] is an object reference, and assigning its value to another variable signifies that the variable is a reference to that object. In order to get a copy of that value you can copy it using angular.copy().
Simply change getFirstAnswer() function to something like this:
DEMO
getFirstAnswer: function () {
return angular.copy(answers[1]);
}
Related
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.
I want automatically refresh $scope.variable in both controllers to new value if data.variable in SharedFactory was changed:
.controller('FirstController', function($scope, SharedFactory) {
$scope.variable = SharedFactory.getVal();
})
.controller('SecondController', function($scope, SharedFactory) {
$scope.variable = SharedFactory.getVal();
SharedFactory.setVal("test string 2");
})
.factory("SharedFactory", function () {
var data = { // all variables by default
variable : 'test string'
};
return {
getVal: function () {
return data.variable
},
setVal: function (i) {
data.variable = i;
}
}
});
http://plnkr.co/edit/b1RNcl6Pz2iuRr2t2Q9x?p=preview
So at this example correct result must be "test string 2" in both controllers. How to do that?
Easiest (and possibly more efficient) would be to have a reference to SharedFactory.data directly in your controllers - rather than to SharedFactory.data.variable. That way, when the value of data.variable changes it would change in all controllers as you reference the data-variable rather than the specific value. Using primitives is generally not reccomended.
Another solution would be to use $scope.$watch in your controllers, and just watch for changes on the value and update the local-variable when it changes.
Because you are using primitive variable instead of using object so once you set it you actually lose your reference to original object, so instead of returning data object value (which is primitive) you can return all data object...
getVal: function () {
return data;
}
Here is update plunker...
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);
};
});
what is the difference between all these Factory definitions
app.factory('myFirstFactory', function () {
var customShow = function () {
return "My First Factory";
}
return customShow;
});
app.factory('mySecondFactory', function () {
return {
show: function () {
return "My Second Factory";
}
}
});
app.factory('myThirdFactory', function () {
function myCustomShow() {
return "My Third Factory";
}
return {
show: myCustomShow
}
});
Here is how its been called in the controller. What is the ideal case of defining the factory. What is the actual return type from the factory, In one defintion, it could seems like Factory and Service are look alike. Can someone please clarify
$scope.message1 = myFirstFactory();
$scope.message2 = myService.show();
$scope.message3 = mySecondFactory.show();
$scope.message4 = myThirdFactory.show();
The first one returns customShow. customShowis declared as a function. So the service returned by this factory is a function. So, when you inject myFirstFactoryin a controller or another service, the injected value will be a function. BTW, you shouldn't choose xxxFactoryas the name. The component you're defining, thanks to a factory, is a service, not a factory. What is injected is the returned service, not its factory.
The second one and the thirst one both return an object:
return {
show: ...
}
So what will be injected in both cases is an object, and not a function like in the first case.
The returned object has a single field named show, which is a function. The second one defines a named function, and assigns this named function to the showproperty of the returned object, whereas the third one directly assigns an anonymous function to the showproperty of the returned object. But the end result is the same. The only difference you'll see is when printing (for debugging) the function itself:
console.log(theService.show);
will print
function myCustomShow() { ... }
for the second one, but will print
function () { ... }
for the third one.
It's all about your preference how to handle the interface of the factory. Another point is best practice. The third one is suggested by John Papa in his detailed angular-styleguide even though he puts the the interface at the top and not below all functions due to readability. It's actually derived by the Revealing Module Pattern.
The first remindes me of a class definiton if there is only a single function returned by the factory. Thus it has to be invoked when you inject it into your controller as follows:
function MyController($scope, myFirstFactory)
{
// $scope.myFirstFactory would print out "My First Factory"
$scope.myFirstFactory = myFirstFactory();
}
This is usually used if you plan to write object-oriented AngularJS services, since factories are useful to define a classes that you can instantiate many times using the new keyword, while services always create singletons.
app.factory('MyFirstFactory', function() {
var privateVariable = 'foo';
// Constructor
var MyFirstFactory = function(bar) {
this.bar = bar;
};
MyFirstFactory.prototype.showCustom = function() {
return "My Third Factory";
};
return MyFirstFactory;
});
You could then create many instances like so:
function MyController($scope, myFirstFactory)
{
// $scope.myFirstFactory would print out "My First Factory"
$scope.myFirstFactory = new MyFirstFactory();
$scope.showCustom = myFirstFactory.showCustom();
}
The second is a variation of the third one which both return an object as #jb-nizet mentioned.
For instance this service :
services.factory('ElementsService', function () {
var currentElement = 'default element';
var service = {
getCurrentElement: function () {
return currentElement;
},
setCurrentElement: function (elmnt) {
currentElement = elmnt;
}
}
return service;
I often find useful to do the following from controllers :
controllers.controller('ElementsCtrl', function($scope, ElementsService) {
$scope.elementsService = ElementsService;
});
To be able to bind the service variables in the html and stay up to date if the variables get changed by some other controller or service. Like so :
<p>The current element is : {{elementsService.getCurrentElement()}}</p>
My question is : is this ok or should I avoid doing this?
Sure the concept is OK and saves having to make a number of different scope variables
Another way you can do it is
angular.extend($scope, ElementsService );
Then in the view you immediately have access to the same data and methods that are returned from the factory
<button ng-click="setCurrentElement(someObj)">test</button>