Let's say I have a predefined module/controller which sets the apple as green.
var _core = angular.module('_core', []);
_core.controller('mainController', function($scope, $controller, $http) {
$scope.apple = 'green';
});
Can I get the apple outside of the controller? Something like.
_core.mainController.apple
Is this possible? I also need to set variables with external plugins, sorry I'm a complete angular noob, its a bit daunting.
If you want the value to be accessible outside the controller, then you can store it in $rootScope instead of $scope and you can directly use the values stored in $rootScope where it is injectable. So the code becomes:
var _core = angular.module('_core', []);
_core.controller('mainController', function($scope, $controller, $http, $rootScope) {
$rootScope.apple = 'green';
});
Now in some other place say a factory, you can use it as:
var _core1 = angular.module('_core1', []);
_core1.factory('someFactory', function($scope, $rootScope, $http) {
var fruit = $rootScope.apple;
//variable fruit now contains green
});
You can use factory to get set any module outside the controller.
var App = angular.module('app', []);
App.factory('messages', function () {
var messages = {};
messages.list = [];
messages.add = function (someString) {
messages.list.push(someString);
};
messages.get = function() {
};
messages.clearList = function () {
messages.list.length = 0;
}
return messages;
});
And you can call this service method into your controller.
App.Controller('myController',['$scope', 'messages', function(scope, messages) {
messages.add('Hello');
var myListy = messages.get();
}]);
Related
I am trying to share a variable between a controller and a function. But i get an error from the controller, saying this:
TypeError: Cannot read property 'getSet' of undefined
I have gone through numerous tutorials, but don't know where am I going wrong.
My service code is like this:
app.service('shareData', function() {
var selected = ["plz", "print", "something"];
var putSet = function(set) {
selected = set;
};
var getSet = function() {
return selected;
};
return {
putSet: putSet,
getSet: getSet
};
});
I am able to reach selected from my function defined like this:
setDisplay = function($scope, $mdDialog, shareData) {
console.log(shareData.getSet()); // this is working
$scope.selected = shareData.getSet();
$scope.hide = function() {
$mdDialog.hide();
};
$scope.cancel = function() {
$mdDialog.cancel();
};
$scope.answer = function(answer) {
$mdDialog.hide(answer);
};
};
My controller is like this:
app.controller('topicController', ['$scope', '$http', '$mdDialog', 'shareData',
function ($scope, $http, $mdDialog, $mdToast, shareData) {
console.log(shareData.getSet()); // NOT WORKING
}]);
You had extra $mdToast in your topicController controller's factory function, you need to remove it.
The reason behind it was not working is, currently you had 4 dependency mentioned in array like ['$scope', '$http', '$mdDialog', 'shareData', function & then you are using its instance inside the function next to DI array. Inside that function you had actually 5 dependencies where $mdToast extra. So behind the scene what happening is $scope of function hold an value of '$scope' DI array likewise you go right to left. But when it comes to $mdToast(in controller function) it was holding a value of 'shareData'(of DI array) & then the next parameter shareData get nothing.
app.controller('topicController', ['$scope', '$http', '$mdDialog', 'shareData',
function ($scope, $http, $mdDialog, shareData) { //<--removed $mdToast
console.log(shareData.getSet());
}
]);
NOTE: You are using DI inline array annotation, so the sequence in which dependency are injected in array, in same sequence you should
inject then in underlying factory function.
I have two controllers and in one of them I declared a $scope variable that I would like visible in the second controller.
First controller
app.controller('Ctrl1', function ($scope) {
$scope.variable1 = "One";
});
Second Controller
app.controller('Ctrl2', function ($scope, share) {
console.log("shared variable " + share.getVariable());
});
I researched the best Angular approach and I found that is the use of service. So I added a service for Ctrl1
Service
.service('share', function ($scope) {
return {
getVariable: function () {
return $scope.variable1;
}
};
});
This code return this error:
Unknown provider: $scopeProvider <- $scope <- share
So my question is: is possible share $scope variable between controllers? Is not the best Angular solution or i'm missing something?
Sorry for my trivial question but I'm an Angular beginner.
Thanks in advance
Regards
Yes, it is possible to share the scope variables between controllers through services and factory.But, the $scope variables are local to the controller itself and there is no way for the service to know about that particular variable.I prefer using factory, easy and smooth as butter. If you are using the service of factory in a separate file you need to include the file in index.html
app.controller('Ctrl1', function ($scope,myService,$state) {
$scope.variable1 = "One";
myService.set($scope.variable1);
$state.go('app.thepagewhereyouwanttoshare');//go to the page where you want to share that variable.
});
app.controller('Ctrl2', function ($scope, myService) {
console.log("shared variable " + myService.get());
});
.factory('myService', function() {
function set(data) {
products = data;
}
function get() {
return products;
}
return {
set: set,
get: get
}
})
You cannot inject $scope dependency in service factory function.
Basically shareable service should maintain shareable data in some object.
Service
.service('share', function () {
var self = this;
//private shared variable
var sharedVariables = { };
self.getSharedVariables = getSharedVariables;
self.setVariable = setVariable;
//function declarations
function getSharedVariables() {
return sharedVariables;
}
function setVariable() {
sharedVariables[paramName] = value;
}
});
Controller
app.controller('Ctrl1', function ($scope, share) {
$scope.variable1 = "One";
share.setVariable('variable1', $scope.variable1); //setting value in service variable
});
app.controller('Ctrl2', function ($scope, share) {
console.log("shared variable " + share.getSharedVariables());
});
Your try with service it quite good, but you shouldn't try to use $scope in dependency injection into the service code. Service is meant to be a data provider to you, then if you want to share some data between 2 controllers, you should make place for this data in your service
.service('share', function ($scope) {
this.variable1 = '';
return {
getVariable: function () {
return this.variable1;
}
setVariable(variable)
{
this.variable1 = variable;
}
};
});
app.controller('Ctrl1', function (share) {
$scope.variable1 = share.getVariable();
});
This is the broader description of this solution:
https://stackoverflow.com/a/21920241/4772988
Best way is really using services. Services have only access to $rootScope, they don't have their own $scope.
However, you shouldn't use scopes for this task. Just make a service with some shared variable outside any scopes.
.service('share', function ($scope) {
var variable = 42;
return {
getVariable: function () {
return variable;
}
};
});
app.controller('Ctrl', function ($scope, share) {
console.log("shared variable " + share.getVariable());
// Watch for changes in the service property
$scope.$watch(function() { return share.getVariable(); }, function(oldValue, newValue) {
// whatever
});
});
First, your service should be like this:
app.service('share', function () {
// this is a private variable
var variable1;
return {
// this function get/sets the value of the private variable variable1
variable1: function (newValue) {
if(newValue) variable1 = newValue;
return variable1;
}
};
});
To set the value of the shared variable pass the value to the function we created on the service
app.controller('Ctrl1', function ($scope, share) {
$scope.variable1 = "One";
// share the value of $scope.variable1
share.variable1($scope.variable1);
});
To get the value of the shared variable also use the function in the service without passing a value
app.controller('Ctrl2', function ($scope, share) {
console.log("shared variable " + share.variable1());
});
Update: Using $rootScope is considered bad practice.
What you need is the $rootScope. The $scope is just for one controller. Here is an example:
angular.module('myApp', [])
.run(function($rootScope) {
$rootScope.test = new Date();
})
.controller('myCtrl', function($scope, $rootScope) {
$scope.change = function() {
$scope.test = new Date();
};
$scope.getOrig = function() {
return $rootScope.test;
};
})
.controller('myCtrl2', function($scope, $rootScope) {
$scope.change = function() {
$scope.test = new Date();
};
$scope.changeRs = function() {
$rootScope.test = new Date();
};
$scope.getOrig = function() {
return $rootScope.test;
};
});
Take the following plunk as an example:
http://plnkr.co/edit/vKFevXhhSprzFvesc6bG?p=preview
var app = angular.module('plunker', []);
app.service('SomeService', ['$rootScope', function ($rootScope) {
var service = {
value: false
}
return service;
}]);
app.controller('MainCtrl', ['$scope', 'SomeService', function($scope, SomeService) {
$scope.value = SomeService.value;
//$scope.$watch(function () { return SomeService.value; }, function (data) { $scope.value = data; });
}]);
app.controller('SecondaryCtrl', ['$scope', 'SomeService', function($scope, SomeService) {
$scope.toggleValue = function () {
SomeService.value = !SomeService.value;
}
}]);
2 controllers and a service, 1 controller (SecondaryCtrl) updates a property on the service and the other controller (MainCtrl) references this property and displays it.
Note the $watch expression commented out in MainCtrl - with this line uncommented, everything works as expected but my question - is it necessary? Shouldn't the watch be implicit or am I doing something wrong?
When you assign the value of SomeService.value to your scope variable, you are creating a copy of the variable which is a distinct object from the value inside SomeService. By adding the watch expression you were simply keeping the two variables (the one in the scope and the one in SomeService) synchronised.
The easiest way to go about this is not to copy the value, but create a reference to the service itself. So in MainCtrl
$scope.someService = SomeService;
and in your html
Value: {{someService.value}}
this way you are actually binding to the value inside SomeService.
I want to give a set of controllers access to methods and properties defined in a trait. Right now the best implementation I have come up with is:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, CtrlTrait) {
$scope.name = CtrlTrait.presetName;
CtrlTrait.setGreeting.call($scope, 'Hello');
});
app.service('CtrlTrait', function() {
this.setGreeting = function(greeting) { this.greeting = greeting; }
this.presetName = 'tom';
});
Plunkr Code
This is fine, but I would like the properties and method to be accessible via the controller's $scope without having to manually create the alias in each controller. I want to be able to use the properties and method from the template just by having injected the service into the controller.
Is this possible, or do I have to create a [wrapper around]/[provider for] $scope like $specialCtrlScope that presets the properties and methods I want?
You can try using angular.extend like this: angular.extend($scope,CtrlTrait); It will allows us to use in the $scope the same functions that your service. So, you can use the function directly in your html like this:
<button ng-click="setGreeting('Good bye! ')">Good Bye</button>
Here is your plunker demo adapted:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, CtrlTrait) {
$scope.name = CtrlTrait.presetName;
// CtrlTrait.setGreeting.call($scope, 'Hello');
angular.extend($scope,CtrlTrait);
$scope.setGreeting('Hello World');
});
app.service('CtrlTrait', function() {
this.setGreeting = function(greeting) { this.greeting = greeting; }
this.presetName = 'tom';
});
http://plnkr.co/edit/BENS78mjFfpc6VCEtgK8?p=preview
You Can try the below in your controller
$scope.setGreeting = CtrlTrait.setGreeting
and can later use
$scope.setGreeting.call($scope, 'Hello');
EDIT AFTER THE COMMENT
Try this
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, CtrlTrait) {
$scope.trait = CtrlTrait;
$scope.name = $scope.trait.presetName;
$scope.trait.setGreeting.call($scope,'Hello');
});
app.service('CtrlTrait', function() {
var trait = {};
trait.setGreeting = function(greeting) { this.greeting = greeting; }
trait.presetName = 'tom';
return trait;
});
So I'll preface this with a disclaimer... I would not recommend that you actually do this, or at least not do it in this way. You're adding extra coupling between your controllers and services inside of a framework built around modularity and injection, all for the sake of saving a few method calls.
That said, here's a way to implement what you want. (JSFiddle here)
var app = angular.module('myApp', []);
var controllerMaker = function(trait,controllerCode){
return function($scope, $injector){
//'apply' traits to this scope
var apply = function(trait){
trait.applyTo($scope);
}
apply.$inject = [trait];
$injector.invoke(apply);
//Finishes the other injections
controllerCode.$inject = ['$scope'];
controllerCode($scope);
};
}
//Here's a sample 'trait'
app.service('CtrlTrait', function() {
this.applyTo = function(obj){
obj.setGreeting = function(greeting) { this.greeting = greeting; }
obj.presetName = 'tom';
}
});
//Then, setup your controller like this
app.controller('GreatController', controllerMaker("CtrlTrait",function($scope){ //Not using injection though!
$scope.bleh = $scope.presetName; //will be 'tom'
}))
There are certainly weaknesses with this, like how your controller loses injection, but if you reeeeeeally wanted to, I'm sure you could play aruond with $inject and find something that suits your needs.
Angular will inject the return value of a the function if it is an object. So in your code:
var app = angular.module('plunker', []);
app.controller('MainCtrl',["$scope","DefaultName","TraitService", function($scope, defaultName, traitService) {
$scope.name = defaultName;
$scope.OKPressed = function() {
traitService.setName($scope.name);
};
});
// You can use .constant() for a constant value;
app.constant("DefaultName", "tom");
app.service('TraitService', function() {
var traitService = {}; // The name does't matter
traitService.setName = function(name) {
// Not this.name = name because (this)is not guaranteed to persist or be the same across injections.
// I am only using local storage to illustrate. I usually use $rootScope to store
// Global variables. since they are always available on the $scope object without
// needing a service.
// That might be a better way for you ($rootScope)
localStorage.setItem("nameKey", name);
}
traitService.getName = function () {
return localStorage.getItem("nameKey");
}
return traitService; // This is what will be injected above
});
I am trying to include a library of functions, held in a factory, into a controller.
Similar to questions like this:
Creating common controller functions
My main controller looks like this:
recipeApp.controller('recipeController', function ($scope, groceryInterface, ...){
$scope.groceryList = [];
// ...etc...
/* trying to retrieve the functions here */
$scope.groceryFunc = groceryInterface; // would call ng-click="groceryFunc.addToList()" in main view
/* Also tried this:
$scope.addToList = groceryInterface.addToList();
$scope.clearList = groceryInterface.clearList();
$scope.add = groceryInterface.add();
$scope.addUp = groceryInterface.addUp(); */
}
Then, in another .js file, I have created the factory groceryInterface. I've injected this factory into the controller above.
Factory
recipeApp.factory('groceryInterface', function(){
var factory = {};
factory.addToList = function(recipe){
$scope.groceryList.push(recipe);
... etc....
}
factory.clearList = function() {
var last = $scope.prevIngredients.pop();
.... etc...
}
factory.add = function() {
$scope.ingredientsList[0].amount = $scope.ingredientsList[0].amount + 5;
}
factory.addUp = function(){
etc...
}
return factory;
});
But in my console I keep getting ReferenceError: $scope is not defined
at Object.factory.addToList, etc. Obviously I'm guessing this has to do with the fact that I'm using $scope in my functions within the factory. How do I resolve this? I notice that in many other examples I've looked at, nobody ever uses $scope within their external factory functions. I've tried injecting $scope as a parameter in my factory, but that plain out did not work. (e.g. recipeApp.factory('groceryInterface', function(){ )
Any help is truly appreciated!
Your factory can't access your $scope, since it's not in the same scope.
Try this instead:
recipeApp.controller('recipeController', function ($scope, groceryInterface) {
$scope.addToList = groceryInterface.addToList;
$scope.clearList = groceryInterface.clearList;
$scope.add = groceryInterface.add;
$scope.addUp = groceryInterface.addUp;
}
recipeApp.factory('groceryInterface', function () {
var factory = {};
factory.addToList = function (recipe) {
this.groceryList.push(recipe);
}
factory.clearList = function() {
var last = this.prevIngredients.pop();
}
});
Alternatively, you can try using a more object oriented approach:
recipeApp.controller('recipeController', function ($scope, groceryInterface) {
$scope.groceryFunc = new groceryInterface($scope);
}
recipeApp.factory('groceryInterface', function () {
function Factory ($scope) {
this.$scope = $scope;
}
Factory.prototype.addToList = function (recipe) {
this.$scope.groceryList.push(recipe);
}
Factory.prototype.clearList = function() {
var last = this.$scope.prevIngredients.pop();
}
return Factory;
});
You cannot use $scope in a factory as it is not defined. Instead, in your factory functions change the properties of the object the factory is returning, e.g.
factory.addToList = function (recipe) {
this.groceryList.push(recipe);
}
these will then get passed on to your $scope variable
$scope.addToList = groceryInterface.addToList;
// ... = groceryInterface.addToList(); would assign to `$scope.addToList` what is returned, instead of the function itself.
This isn't the exact answer for this question, but I had a similar issues that I solved by simply passing $scope as an argument to a function in my factory. So it won't be the normal $scope, but $scope at the time the function in the factory is called.
app.controller('AppController', function($scope, AppService) {
$scope.getList = function(){
$scope.url = '/someurl'
// call to service to make rest api call to get data
AppService.getList($scope).then(function(res) {
// do some stuff
});
}
});
app.factory('AppService', function($http, $q){
var AppService = {
getList: function($scope){
return $http.get($scope.url).then(function(res){
return res;
});
},
}
return AppService;
});