Plunkr
I have this service I inject in my controllers. It is simply a service to share some properties.
angular.module('app', []).
service('sharedProperties', function () {
var list_name = '';
return {
getListName: function() {
return list_name;
},
setListName: function(name) {
list_name = name;
}
};
});
I have two controllers. In the first one, I set the value of list_name. In my second, I want to retried this information.
Here is how are defined my controllers :
function ListCtrl($scope, $http, sharedProperties) {
...
$scope.changeListName = function(list_name) {
sharedProperties.setListName(list_name);
console.log(list_name, sharedProperties.getListName()); # shows ( 'metro', 'metro') == metro being a dummy list_name
...
};
function ItemCtrl($scope, $http, sharedProperties) {
...
$scope.showOnlyList = sharedProperties.getListName();
console.log(this.sharedProperties.getListName()); # empty string
...
};
I logged the variable and checked them in the browser console and noticed that ListCtrl sets the shared Property properly. The issue comes from the ItemCtrl controller. It seems that when I try to access the list_name with sharedProperties.getListName();, the property is empty, or the function returns an empty string.
UPDATE
I thought the problem came from the service. So I decided to use Lungojs' data library.
I got the following code :
In ListCtrl :
$scope.changeListName = function(list_name) {
Lungo.Data.Cache.set("ListName", list_name);
console.log('LIST', Lungo.Data.Cache.get("ListName"));
};
In ItemCtrl :
$scope.showOnlyList = Lungo.Data.Cache.get("ListName");
console.log('ITEM', Lungo.Data.Cache.get("ListName"));
The log in ListCtrl shows that the cache is set to the correct list_name. However, the console for ItemCtrl shows that Lungo.Data.Cache.get("ListName") is undefined even if it was correct on the ListCtrl!
I also tried replacing the cache by HTML5 local storage without success...
Well, I think its because you instantly log your sharedListPropery to the console, right after instantiating your ItemCtrl.
When it is instantiated, sharedPropertyList has no value yet.
EDIT:
Sry, JSFiddle is currently not working, so I have to put this untested code here.
But it should give you an idea
angular.module('app', []).
service('sharedProperties', function () {
var list_name = '';
return {
getListName: function() {
return list_name;
},
setListName: function(name) {
list_name = name;
}
};
}).
controller('ListCtrl',['$scope','sharedProperties',function(scope,shared){
console.log(shared.getListName()); //empty, because nothing set yet.
scope.listname = shared.getListName();
//watching the change and updating the shared
scope.$watch('listname',function(value){
console.log('listname is now '+value);
shared.setListName(value);
})
//watching the shared directly
scope.shared=shared;
scope.$watch('shared.getListName()',function(value){
console.log("sharedProperty has changed to"+value);
})
}]);
Related
I have two controllers. The first one sets a variable into my service and my second one is supposed to get this variable, but it's undefined.
Aren't angular services supposed to be singletons ? Because my service seems to be instantiated for each controller.
Here's my code :
First controller
angular.module('myApp').controller('HomeCtrl', ['$scope', 'User', function ($scope, User) {
$scope.join = function () {
User.setRoom("test");
console.log(User.getRoom()); // displays 'test'
$window.location.href = '/talk';
}
}]);
In my second controller, I've just a
console.log(User.getRoom()); // displays ''
And here's my service
angular.module('myApp').factory('User', function () {
var data = {
room: ''
};
return {
getRoom: function () {
return data.room;
},
setRoom: function (room) {
data.room = room;
}
};
});
Do you have an idea?
You are using $window.location.href = '/talk'; to navigate - this triggers a full page reload, and all services are therefore also reinitialized.
You probably want to use the $location service. See the documentation and/or this answer for a summary of the difference between the two.
Folks:
I have 2 controllers, ctrlA and ctrlB - both unrelated to each other but are within the same page.
ctrlA queries an end point and returns a json object tags, which is then passed to a service method MyService.saveTags(tags) to store the object.
ctrlB then needs to populate a $scope variable $scope.tags by fetching the tags object created via ctrlA.
The service:
.factory('MyService', function($http, $q, $window) {
var myserviceFactory = {};
var savedTags = {};
// ..other methods..
myserviceFactory.saveTags = function(tags) {
if(!savedTags.tags){
console.log('saving tags..');
savedTags.tags = tags;
}
};
myserviceFactory.getSavedTags = function() {
console.log('returning tags..');
return savedTags.tags;
};
return myserviceFactory;
})
This issue appears to be ctrlB gets called first, so when $scope.savedTags = MyService.getSavedTags(); runs, it returns undefined.
Question: Angular n00b here - what would be the best way to fetch the tags after ctrlA has populated the object?
You can use a watch on the getSavedTags service method inside your ctrlB to know when is it populated. Something like
$scope.$watch(function() { return MyService.getSavedTags() },function(newValue) {
if(newValue) {
$scope.savedTags = newValue;
}
});
UPDATE 2:
I found out where the problem was coming from and I left out the important part.
I'm setting the property in the first controller INSIDE this
$rootScope.$on('anEvent', function (event, data) {
InjectedService.setToken(data.value);
})
But I can't grab it from outside that callback scope. The event that is listened for depends on an $http request so I'm guessing that is the problem. Are there any workarounds to this?
UPDATE:
The order of controllers are the other way around so that the code in secondController is actually being called first in my actual app. I have reordered them accordingly
Question:
I'm trying to grab the services specific property but when I try to grab the service property, I get undefined (using a getter function or not). But when I try to grab the full service, I get everything with the correct property and value.
main.js
angular.module('myApp', ['myModule', 'myServices'])
.controller('firstController', ['InjectedService', function(InjectedService) {
InjectedService.setProperty('Hello World')
}])
othermodule.js:
angular.module('myModule', [])
.controller('secondController', ['InjectedService',
function (InjectedService) {
console.log(InjectedService); // Full object with property == 'hello world'
console.log(InjectedService.property); // I get undefined
console.log(InjectedService.getProperty()); // I get undefined
// interesting to note:
InjectedService.setToken('A different string');
console.log(InjectedService.property); // I get 'A different string'
console.log(InjectedService); // Full object with property == 'Hello World' which was set in second controller
}])
services.js:
angular.module('myServices', function
.service('InjectedService', function(){
var Service = this;
Service.setProperty = function (value) {
Service.property = value;
}
Service.getProperty = function () {
return Service.property;
}
Service.unsetProperty = function () {
Service.property = null;
}
return Service;
})
It seems to me a scope problem, but the variable isn't a primitive type. Any suggestions?
My factory is undefined in my controller and I cannot figure out why. I have created a simple example to illustrate.
Here I create the app:
var ruleApp = angular
.module( "ruleApp", [
"ngRoute",
"ruleApp.NewFactory1",
"ruleApp.NewFactory2",
] );
In this dummy example I'd like to build a factory that does something simple, show an alert box. I'll show two methods of doing this (one works, one does not).
Factory 1:
angular
.module('ruleApp.NewFactory1', [])
.factory('NewFactory1', function() {
return function(msg) {
alert(msg);
};
});
Factory 2:
angular
.module('ruleApp.NewFactory2', [])
.factory('NewFactory2', function() {
var showMessageFunction = function(msg) {
alert(msg);
};
return
{
showMessage: showMessageFunction
};
});
Notice the return type of factory 1 is a function and the return type of factory 2 is an object with a property (which is of type function).
Now look at how I'd like to use both of these factories in my controller:
ruleApp.controller( "RuleController", function( $scope, NewFactory1, NewFactory2 ) {
NewFactory1("1");
NewFactory2.showMessage("2");
});
This is where the problem gets exposed. During execution, I am prompted with the alert box for NewFactory1("1");, but it fails during execution of NewFactory2.showMessage("2"); because NewFactory2 is undefined (TypeError: Cannot call method 'showMessage' of undefined).
Can you help me spot the issue? I want to be able to use factories like NewFactory2 because I want the factories to be able to do more than just one thing (i.e. have more than one single function). By the way, I'm using Angular version 1.2.1.
factory 2 should be (demo)
angular
.module('ruleApp.NewFactory2', [])
.factory('NewFactory2', function() {
var showMessageFunction = function(msg) {
alert(msg);
};
return { // <--------- do not go on a new line after return
showMessage: showMessageFunction
};
});
I've got an app defined this way:
angular.module("myApp", [...])
.config(function ($stateProvider, $controllerProvider) {
if (isControllerDefined(controllerName)) {
do_stuff();
}
})
The controllers are defined this way:
angular.module("myApp")
.controller("myController", function ($scope) { ... });
How can I define isControllerDefined() (in the config above) to check whether a given controller exists if I have the name of the controller? I feel like I should be able to do something like one of these:
var ctrl = angular.module("myApp").getController("myController");
var ctrl = $controllerProvider.get("myController");
or something like that... but I can't find any functionality for this. Help?
An example of a service that can check if a controller exists. Note that it looks for a global function with specified name as well as a controller in the $controller provider.
angular.service('ControllerChecker', ['$controller', function($controller) {
return {
exists: function(controllerName) {
if(typeof window[controllerName] == 'function') {
return true;
}
try {
$controller(controllerName);
return true;
} catch (error) {
return !(error instanceof TypeError);
}
}
};
}]);
See the fiddle for usage.
I came across this exact same issue the other day. I had a few issues with the currently accepted answer, namely because one of my controllers was performing an initialization call out to the server upon instantiation to populate some data (i.e):
function ExampleController($scope, ExampleService) {
ExampleService.getData().then(function(data) {
$scope.foo = data.foo;
$scope.bar = data.bar
});
}
As it stands, the currently accepted answer will actually instantiate the controller, before discarding it. This lead to multiple API calls being made on each request (one to verify that the controller exists, one to actually use the controller).
I had a bit of a dig around in the $controller source code and found that there's an undocumented parameter you can pass in called later which will delay instantiation. It will, however, still run all of the checks to ensure that the controller exists, which is perfect!
angular.factory("util", [ "$controller", function($controller) {
return {
controllerExists: function(name) {
try {
// inject '$scope' as a dummy local variable
// and flag the $controller with 'later' to delay instantiation
$controller(name, { "$scope": {} }, true);
return true;
}
catch(ex) {
return false;
}
}
};
}]);
UPDATE: Probably a lot easier as a decorator:
angular.config(['$provide', function($provide) {
$provide.delegate('$controller', [ '$delegate', function($delegate) {
$delegate.exists = function(controllerName) {
try {
// inject '$scope' as a dummy local variable
// and flag the $controller with 'later' to delay instantiation
$delegate(controllerName, { '$scope': {} }, true);
return true;
}
catch(ex) {
return false;
}
};
return $delegate;
}]);
}]);
Then you can simply inject $controller and call exists(...)
function($controller) {
console.log($controller.exists('TestController') ? 'Exists' : 'Does not exist');
}
There is currently no easy way of fetching a list of controllers. That is hidden for internal use only. You would have to go to the source code and add a public method that return the internal controllers variable (in $ControllerProvider function)
https://github.com/angular/angular.js/blob/master/src/ng/controller.js#L16
this.getControllers = function() {
return controllers;
// This will return an object of all the currently defined controllers
};
Then you can just do
app.config(function($controllerProvider) {
var myCtrl = $controllerProvider.getControllers()['myController'];
});
Since angular 1.5.1 (not released yet at the time of writing), there is a new way to check whether a controller exists or not through the $ControllerProvider.has('MyCtrlName') method.
Github issue: https://github.com/angular/angular.js/issues/13951
Github PR: https://github.com/angular/angular.js/pull/14109
Commit backported in 1.5.1 directly: https://github.com/angular/angular.js/commit/bb9575dbd3428176216355df7b2933d2a72783cd
Disclaimer: Since many people were interested by this feature, I made a PR, because I also need it is some of my projects. Have fun! :)
This PR has been based on #trekforever answer, thanks for the hint :)
You could use the $controller service and do $controller('myController') and wrap a try-catch arround it so you know if it fails.