I'm trying to pass a Javascript object into my AngularJS controller and having no luck.
I've tried passing it into an init function:
<div ng-controller="GalleryController" ng-init="init(JSOBJ)">
And on my controller side:
$scope.init = function(_JSOBJ)
{
$scope.externalObj = _JSOBJ;
console.log("My Object.attribute : " + _JSOBJ.attribute );
};
Though the above doesn't seem to work.
Alternatively, I've tried pulling the attribute from the AngularJS controller that I am interested in for use in an inline <script> tag:
var JSOBJ.parameter = $('[ng-controller="GalleryController"]').scope().attribute ;
console.log("My Object.parameter: " + JSOBJ.attribute );
Can anyone tell me: what is the best practice regarding this?
I don't have the option to rewrite the plain Javascript object as it is part of a 3rd party library.
Let me know if I need to provide further clarification and thanks in advance for any guidance!
-- JohnDoe
Try setting it as a value:
angular.module('MyApp')
.value('JSOBJ', JSOBJ);
Then inject it into your controller:
angular.module('MyApp')
.controller('GalleryController', ['JSOBJ', function (JSOBJ) { ... }]);
Since your object is a part of third-party library you have to wrap it app in something angular.
Your options are:
if it is jquery pluging init'ed for a DOM node etc you can create a directive
example
myApp.directive('myplugin', function myPlanetDirectiveFactory() {
return {
restrict: 'E',
scope: {},
link: function($scope, $element) { $($element).myplugin() }
}
});
if it is something you need to init you can use factory or service
example
myApp.service(function() {
var obj = window.MyLib()
return {
do: function() { obj.do() }
}
})
if it is plain javascript object you can use value
example
myApp.value('planet', { name : 'Pluto' } );
if it is constant ( number, string , etc) you can use constant
example
myApp.constant('planetName', 'Greasy Giant');
Reference to this doc page: https://docs.angularjs.org/guide/providers
Also I strongly encourage you to read answer to this question: "Thinking in AngularJS" if I have a jQuery background?
If you have JSOBJ accessible via global scope (via window), than you can access it through window directly as in plain JavaScript.
<script>
...
window.JSOBJ = {x:1};
...
</script>
Option 1.
<script>
angular.module('app',[]).controller('ctrl', ['$scope', function($scope) {
$scope.someObject = window.JSOBJ;
}]);
</script>
However it makes the controller code not testable. Therefore $window service may be used
Option 2.
<script>
angular.module('app',[]).controller('ctrl', ['$scope', '$window', function($scope, $window) {
$scope.someObject = $window.JSOBJ;
}]);
</script>
If you want to make some abstraction layer to make your controller agnostic for the source from where you get the object, it is recommended to define service which is responsible for fetching value and then inject it to your controllers, directives, etc.
Option 3.
<script>
angular.module('app',[])
.service('myService', function() {
var JSOBJ = ...; // get JSOBJ from anywhere: localStorage, AJAX, window, etc.
this.getObj = function() {
return JSOBJ;
}
})
.controller('ctrl', ['$scope', 'myService', function($scope, myService) {
$scope.someObject = myService.getObj();
}]);
</script>
Besides of it, for simple values you can define constant or value that may be injected to any controller, service, etc.
Option 4.
<script>
angular.module('app',[]).
value('JSOBJ', JSOBJ).
controller('ctrl', ['$scope', 'JSOBJ', function($scope, JSOBJ) {
$scope.someObject = JSOBJ;
}]);
</script>
Related
I have these controller codes from different js files.
NLGoalsCtrl.js
angular.module('mysite').controller('NLGoalsCtrl', function ($scope) {
$scope.goals_selected = [];
});
NLSessionsCtrl.js
angular.module('mysite').controller('NLSessionsCtrl', function ($scope) {
//access $scope.goals_selected here
});
I need to be able to access the $scope.goals_selected from the NLSessionsCtrl. How do I do this? Thanks guys.
Use a factory/service to store the goals which will be responsible for sharing data among the controllers.
myApp.factory('myService', [function() {
var goals = {};
return {
getGoals: function() {
return goals
},
setGoals: function(op) {
goals = op;
},
}
}])
.controller('NLGoalsCtrl', [function($scope, myService) {
$scope.goals_selected = {};
//Update goals_selected
myService.setGoals($scope.goals_selected );
}])
.controller('NLSessionsCtrl', [function($scope, myService) {
//Fetch
$scope.goals_selected = myService.getGoals();
}]);
$scope is an "object" that "binds" to DOM element where you apply controller. So the context of $scope remains inside the controller.
If you want to access the variables in 2 controllers , try to share it via a service/Factory or $rootScope
Here is a sample App
the one that you want to access in any controller assign it to the $rootScope.
and access it in any controller you want.
I'd like to do simple notifications in angular. Here is the code I've written.
http://pastebin.com/zYZtntu8
The question is:
Why if I add a new alert in hasAlerts() method it works, but if I add a new alert in NoteController it doesn't. I've tried something with $scope.$watch but it also doesn't work or I've done something wrong.
How can I do that?
Check out this plnkr I made a while back
http://plnkr.co/edit/ABQsAxz1bNi34ehmPRsF?p=preview
I show a couple of ways controllers can use data from services, in particular the first two show how to do it without a watch which is generally a more efficient way to go:
// Code goes here
angular.module("myApp", []).service("MyService", function($q) {
var serviceDef = {};
//It's important that you use an object or an array here a string or other
//primitive type can't be updated with angular.copy and changes to those
//primitives can't be watched.
serviceDef.someServiceData = {
label: 'aValue'
};
serviceDef.doSomething = function() {
var deferred = $q.defer();
angular.copy({
label: 'an updated value'
}, serviceDef.someServiceData);
deferred.resolve(serviceDef.someServiceData);
return deferred.promise;
}
return serviceDef;
}).controller("MyCtrl", function($scope, MyService) {
//Using a data object from the service that has it's properties updated async
$scope.sharedData = MyService.someServiceData;
}).controller("MyCtrl2", function($scope, MyService) {
//Same as above just has a function to modify the value as well
$scope.sharedData = MyService.someServiceData;
$scope.updateValue = function() {
MyService.doSomething();
}
}).controller("MyCtrl3", function($scope, MyService) {
//Shows using a watch to see if the service data has changed during a digest
//if so updates the local scope
$scope.$watch(function(){ return MyService.someServiceData }, function(newVal){
$scope.sharedData = newVal;
})
$scope.updateValue = function() {
MyService.doSomething();
}
}).controller("MyCtrl4", function($scope, MyService) {
//This option relies on the promise returned from the service to update the local
//scope, also since the properties of the object are being updated not the object
//itself this still stays "in sync" with the other controllers and service since
//really they are all referring to the same object.
MyService.doSomething().then(function(newVal) {
$scope.sharedData = newVal;
});
});
The notable thing here I guess is that I use angular.copy to re-use the same object that's created in the service instead of assigning a new object or array to that property. Since it's the same object if you reference that object from your controllers and use it in any data-binding situation (watches or {{}} interpolation in the view) will see the changes to the object.
I have an object that updates via socket.io.
var my_dynobj;
socket.on('update dynobj', function(dynobj){
my_dynobj = dynobj;
});
I want to have it in my angular app as a factory or a service that could be injected as a dependency.
In there I will want to attach my_object to a $scope so that it can be plugged into HTML {{my_object}} and will update whenever it is itself updated in its own factory definition via socket. socket.on('update object', …
But I can't figure out a way to make that happen.
angular.module('app', [])
.factory('dynobj_factory', [
function(){
var my_dynobj;
socket.on('update dynobj', function(dynobj){
my_dynobj = dynobj;
});
return {what?}
}])
.controller('ctrl', ['$scope', 'dynobj_factory',
function($scope, dynobj) {
$scope.my_dynobj = dynobj.what?
}])
<div>{{my_dynobj}}</div>
For
return {/* what ? */}
You'll need to return something like;
return {dynamicObj: my_dynobj}
And that will do the trick. Any changes to my_dynobj will reflect through all directives, controllers, factories it is injected into.
I'm kinda new in AngularJS unit testing and I'm having some troubles to test a controller method that was written in a directive.
This is my directive.js
app.directive('formLogin', ['AuthService', function(AuthService){
return {
restrict: 'E',
templateUrl: utils.baseUrl + 'partials/_home-form-login.html',
replace: true,
controller: function ($scope, $element, $http, $location) {
$scope.visible = false;
$scope.showForm = function () {
$scope.visible = !$scope.visible;
};
}
};
}]);
And here goes my unit-test.js
describe('formLogin ctrl', function () {
var element, scope, compile;
beforeEach(module('Application'));
beforeEach(inject(function ($rootScope, $compile) {
element = angular.element('<form-login></form-login>');
scope = $rootScope.$new();
compile = $compile(element)($scope);
}));
it('Test', function() {
expect(scope.visible).toBe(false);
})
});
And by doing this the "scope.visible" come as undefined.
There are some way to take the $scope from my controller that assumes in "scope" variable the "visible" property and the "showForm" method?
From this link
it looks like you might need to do a scope.$digest();
You appear to have a couple problems:
compile = $compile(element)($scope); -- here, $scope is undefined. It should be compile = $compile(element)(scope);
As mentioned by smk, you need to digest your scope to finish the directive creation process
This is especially important because you are using templateUrl. When you just use a locally-defined template, as Krzysztof does in his example, you can get by with skipping this step.
You will probably notice that when you add scope.$digest() you will get a different problem about an unexpected GET request. This is because Angular is trying to GET the templateUrl, and during testing all HTTP requests must be configured / expected manually. You might be tempted to inject $httpBackend and do something like $httpBackend.whenGet(/partials\//).respond('<div/>'); but you will end up with problems that way.
The better way to accomplish this is to inject the template $templateCache -- Karma has a pre-processor to do this for you, or you can do it manually. There have been other StackOverflow questions you can read about this, like this one.
I've modified your example to manually insert a simple template into the $templateCache as a simple example in this plunkr.
You should take a look into Karma's html2js pre-processor to see if it can do the job for you.
If your directive hasn't isolated scope, you can call methods from directive controller and test how it impacts to scope values.
describe('myApp', function () {
var scope
, element
;
beforeEach(function () {
module('myApp');
});
describe('Directive: formLogin', function () {
beforeEach(inject(function ($rootScope, $compile) {
scope = $rootScope.$new();
element = angular.element('<form-login></form-login>');
$compile(element)(scope);
}));
it('.showForm() – changes value of $scope.visible', function() {
expect(scope.visible).toBe(false);
scope.showForm();
expect(scope.visible).toBe(true);
});
});
});
jsfiddle: http://jsfiddle.net/krzysztof_safjanowski/L2rBV/1/
I have a few functions that are used in different controllers, and rather than copy and paste it multiple times into the controllers, I want to pull it out and place it in a factory.
However, when I try to call the function in the HTML via Angular {{expressions}} it doesn't work.
Instead, I've made functions inside each of the controllers' $scopes that call the factory's functions so the DOM can read the expressions--but that seems redundant. Is there a way to fix this so I can simply call functions from the factory?
Here was what I originally had tried:
index.html:
<div ng-controller="MyController">
The rating of this item is: {{MakeGraph.getRating(whichItem)}}<br />
The votes of this item is: {{MakeGraph.getVotes(whichItem)}}
</div>
MyController.js:
controllers.controller('MyController', ['$scope', 'MakeGraph', '$routeParams', function($scope, MakeGraph, $routeParams){
$scope.whichItem = $routeParams.itemId;
//no other reference to MakeGraph here
}])
factory.js:
app.factory('MakeGraph', function(){
var service = {};
service.getRating = function(itemNo){
...
return ...;
};
service.getVotes = function(itemNo){
...
return ...;
};
return service;
});
Instead, I can only get it to work when I change MyController to have something like this:
$scope.getRating = function(){
return MakeGraph.getRating(itemNo);
};
Can I fix this so I don't have to call the function inside the $scope? Thanks.
add $scope.MakeGraph = MakeGraph to your controller
controllers.controller('MyController', ['$scope', 'MakeGraph', '$routeParams', function($scope, MakeGraph, $routeParams){
$scope.whichItem = $routeParams.itemId;
$scope.MakeGraph = MakeGraph
}])
then you could access MakeGraph from your view
example:
<a ng-click="MakeGraph.getVotes(12)"> Click me </a>
note that if you return promises in your service, you will probably still need to wrap then in your controller in order to properly handle the .success / .then / .error ... events