Inject a variable from Service to html - javascript

I am asked to debug some things in an angular JS app, problem is I dont know anything about this framework and I need to get this done.
I need to inject a variable from my service to a html template.
So I thought I will need to get this variable from Controller, which get it from the Service.
For the moment I have this :
controller.js
$scope.fileURL = null;
var fileURL = ItemsService.fileURL;
$scope.fileURL.push(fileURL);
services.js
I declare this inside my service class :
this.fileURL = null;
and i declare it my method this way :
self.fileURL = fileURL;
But I got this error TypeError: Cannot call method 'push' of null
And fileURL is defined and got a value in my method.
Any idea why ?
Thanks

more performant solution
Thanks to maurycy for his comments.
Making your service visible through $scope like this:
$scope.itemService = ItemService;
and using it in your template for example like this:
{{itemService.fileURL}}
will be more perfomant, less complex and have all other advantages as well.
old solution
Why don't you just pass through your variable:
In your service:
this.fileURL = "something";
in your controller:
$scope.fileURL = function() {
return ItemsService.fileURL;
};
This way changes made in your service (like a update of fileURL will trigger an update of the view through the controller.
FYI:
If you define your $scope-variable like this:
Object.defineProperty($scope, 'fileURL', {
get: function() {
return ItemService.fileURL;
},
set: function(newValue) {
ItemService.fileURL = newValue;
}
});
You may also update the variable in the service from the controller AND childscopes will have the same functionality, since upon $scope.fileURL = 'different'; they won't introduce a new property fileURL which hides the original one and doesn't wire to the service anymore.

Related

AngularJS communication between modules in different html files

There are a couple of variables I want to share between two html files and two modules. Firstly, I used the service as follows but it did not work.
In vote.js:
var vote = angular.module('vote', []);
vote.service('voteParam', function () {
this.candidates = [];
this.show_result;
this.vote_begin;
this.elec_begin;
this.setValue = function (voteParam) {
this.department = voteParam.department;
this.group = voteParam.group;
this.type = voteParam.type;
this.round = voteParam.round;
this.times = voteParam.times;
this.advance_num = voteParam.advance_num;
}
});
In vote-setting.js:
angular.module('admin')
.controller('vote_setting', function ($scope, $http, voteParam) {
$scope.begin_vote = function () {
init();
startit();
$scope.vote_begin = true;
$scope.elec_begin = true;
voteParam.vote_begin = true;
voteParam.elec_begin = true;
voteParam.show_result = false;
voteParam.setValue($scope.voteParam);
if (voteParam.candidates.length == 0) {
$scope.get_candidate();
}
};
}
When the value is set as true in vote-setting.js, it is showed as undefined in vote.js. I think it's because when I import vote-setting.js in vote-setting.html, variables in the service are initialized again. Is there any other way I can make this work?
The service worked. But the value of variables in it get initialized when the js file is imported again. This is the key of this problem. Thanks
You might want to know the following:
Generally,an angular app should have only one base module. This is the module which you add to your html using ng-app directive. So your html content will know about only one angular app.
You can however create as many other modules as you would like or need. These can be custom modules as in your case, or these can be third party library (modules written by other developers for your ready use.). In any of the above case you need to add extra modules to your base module as dependencies as shown below:
angular.module('baseModule', ['extraModule1, extraModule2]);
Once you are done with this, any component like any directive , service, factory written in any of the extra modules shall be available to you for use, as if they were all written in your baseModule.
Having said this and coming to your question, the code provided by you doesn't seem sufficient to entirely diagnose your issue. However from whatever I get, I suggest you to add vote module to admin module or vice-versa. If you have any other module as a base module, you need to add both vote and admin as dependencies to that module.
Hope this helps you to start fixing the issue. Let me know if any thing seems unclear yet.
Implement your shared data service as a factory.
Implement proper getter and setters.
Encapsulate your variables and only expose the getter, setters.
Example of shared data service (can also be used as a shared Model)
app.factory('voteParam', function() {
var param1;
var param2;
var param3;
return {
setParam1: function(value){
param1 = value;
},
getParam1: function(){
return param1;
},
setParam2: function(value){
param2 = value;
},
getParam2: function(){
return param2;
},
setParam3: function(value){
param3 = value;
},
getParam3: function(){
return param3;
}
}
});
In an application this factory should be initialized once only.
If you are using 2 different angular applications (in your case 'vote' and 'admin') then use local storage or session storage to share data between them

How not to use 'this' in Angular?

New to Angular and I'm trying to follow JSLint ( written by Douglas Crockford ) recommendation of not using this in JS at all.
How can I prevent myself to use this in Angular? All the tutorials I've seen so far rely on using this and SO search had resulted in no answer.
For the purpose of being clear, let's assume I'm trying to write this controller:
app.controller('StoreController', function() {
this.products = {};
});
How can I access the controller object so I can add the products property?
var my_controller = app.controller('StoreController', function() {
});
my_controller.products = {}; // Maybe like this?
Inject $scope, for example:
app.controller('StoreController', function($scope) {
$scope.products = {};
});
Here is more information about them: https://docs.angularjs.org/guide/scope
Also, when using things like services, you can avoid this by keeping a local state and returning an interface object. For example:
.service('MyService', function(){
// state
var mystate = {};
// interface object
return {
someExposedFunc: function(){
}
};
});
I would just like to note though, that I don't see any good reason why you should avoid this when using angular. I think using $scope has more semantic usefulness when reading controller code, but when using things like services, I write less code by creating exposed functions like this.somefunc = ....

Angular controller undefined within jasmine test

I'm currently having an issue writing some tests for a controller. Within the beforeEach block below I need to instantiate an activityController and inject the scope object. I have added a console log before the call to the $controller service and this is outputted however the one after never gets called therefore something is breaking within the $controller block.
beforeEach(inject(function($controller) {
console.log(activityController);
activityController = $controller('activityController', {
'$scope': $scope
});
console.log("TEST");
}));
Within my tests I'm seeing Type Error: activityController is undefined in C:\.......\activity.controller.test.js so I know it's definitely not being instantiated.
I've created a gist of the relevant files here: https://gist.github.com/junderhill/e181ce866ab1ebb1f805
The activity controller not being instantiated correctly is causing my tests to fail. Any ideas on what may be causing this would be appreciated. Thanks
Jason.
Try to set activityService whilst creating controller, because you should also inject all services.
Looks like this line may be causing the problem:
mockRoleService.setCurrentRole({"AssignmentID":21,"EndDate":"2049-12-31T00:00:00","StartDate":"2000-01-01T00:00:00","UserType":1,"AccessLevel":"00000000-0000-0000-0000-000000000000","Description":"Demonstration Territory 1","TeamID":null});
It looks like you're using an actual injected version of your roleService instead of a stubbed literal, so it's actually going to fire off your implementation, which is...
this.setCurrentRole = function(role){
currentRole = role;
$http.get("http://localhost:14938/api/User/GetTeamForAssignment?assignmentId=" + role["AssignmentID"] + "&assignmentType=" + role["UserType"])
.success(function (data) {
currentTeam = data;
});
}
If you're going to use that service directly with an $httpBackend mock, I'd actually wrap that operation in a $q.defer(), because currently as that stands, that is an asychronous call. You'll want that operation to complete to set the currentTeam properly. So, maybe something like..
this.setCurrentRole = function(role){
var deferred = $q.defer();
currentRole = role;
$http.get("http://localhost:14938/api/User/GetTeamForAssignment?assignmentId=" + role["AssignmentID"] + "&assignmentType=" + role["UserType"])
.success(function (data) {
currentTeam = data;
deferred.resolve();
});
return deferred.promise;
}
And obviously do a deferred.reject of some sort if something wonky comes back from HTTP.
Hope that helps!
Eric

Injecting service which define by module.provider

Sorry to ask something like this, but i m already spend couple of days to solve this one.
But i m really need help. I am already read Understanding Dependency Injection
and also other stack question use $http inside custom provider in app config use the
$injector
on run block also not work in my code.
Perhaps someone here can help me, i am also worried how annotate the injection on my provider for minification.
My code in
Here
The problem is that when you call $http in the _getItem you are in the "controller". A solution could be to do this:
this.$get = function($http) {
return {
mainUrl : _mainUrl,
getItem : _getItem,
httpWrapper : $http
};
And then in the _getItem assign (or use the httpWrapper directly):
var $http = this.httpWrapper;
In order to avoid problems with the minification, you could add the injection to the method $get:
this.$get = ['$http', function($http) {
Here the updated Plunker: http://plnkr.co/edit/m7oXNolo6iV8Zy1NJ0vx
EDIT: I think that what you wanted to achieve is this:
var _getItem = function() {
var _d = null;
console.log(this)
var $http = this.httpWrapper;
var $q = this.qWrapper;
_d = $q.defer();
$http.get(_mainUrl)
.success(function(data) {
console.log(data);
_d.resolve( {data: data, x: "efe"} );
})
.error(function(status) {
_d.resolve( status );
});
return _d.promise;
};
$q.defer() allows to defer the http call to your controller, then on your controller you have to edit this:
facade.getItem().then(function(data){
$scope.myReturn = data;
}
Updated Plunker: http://plnkr.co/edit/xR1d3KnWhX51jHHZ8t3m
Note: Consider that the nature of then is asynchronous, thus if you use the variable straight afterwards, it still will be undefined

AngularJS learning about services and factories, a service in a factory?

I'm really trying to wrap my head around angular's service/factory/provider constructs before I refactor a pretty big project.
I've read lots of docs and articles on services vs. factories and thought I understood how each of them are created and what they do.
However, while trying stuff I attempted to use a service in a factory or two...
This was really useful: I now understand that there is only one of my 'jsonService' (it's a singleton), so this simple approach WILL NOT WORK... (I'll need each factory to have a separate instance of something)
.service('jsonService', ['$http', function ($http) {
var data= {'msg':'no data'};
this.serviceData= data;
this.get= function(url){
$http.get(url).then(function (resp) {
data= resp.data;
});
}
}])
.factory('fac1', ['jsonService', function(jsonService){
jsonService.get('json/data1.json');
return jsonService.serviceData;
}])
.factory('fac2', ['jsonService', function(jsonService){
jsonService.get('json/data2.json');
return jsonService;
}])
When I use the factories in a controller like:
myController.f1= fac1;
myController.f2= fac2.serviceData;
I can see that fac1 and fac2 both return the same object, they both have {msg:'no data'}, if I change one then they both change.
My question is:
Even though I can break on the service and see data= {msg:'no data'} and see it being set to the response data - why do I not see any change in fac1 or fac2 ?
All I can think is that somewhere there must be more than one var data, something is not a 'singleton' ????
EDIT: I have now tried:
this.serviceData= function(){return data;};
and:
myController.f2= fac2.serviceData(); // this is always the 'no data' object
myController.f3= fac2.serviceData;
if I then (a long time later) call:
var something= myController.f3();
then I do get the json data... but myController.f2 is still {msg:'no data'} why ?
Ok, after trying m.e.conroy's suggestion I finally figured it out...
The problem is nothing to do with angular, it's how javascript passes objects.
(see: Is JavaScript a pass-by-reference or pass-by-value language? )
The factories passed back a 'copy-reference' of the original {msg:'no data'} object, and when the service eventually assigned:
data= resp.data;
that replaced 'data', but the factory-supplied references persist as the old object.
Now, if I do:
.service('jsonService', ['$http', function ($http) {
var data= {'msg':'no data', 'result':null};
this.serviceData= data;
this.get= function(url){
$http.get(url).then(function (resp) {
data.result= resp.data; // update properties of the data object
data.msg='got data!'; // instead of replacing it
});
}
}])
...then everything makes sense!
The variables in myController are changed when data arrives (since I have not 'swapped out' the data object).
Obviously, I still have the problem that my two factories return the same object (I'll look at Sacho's suggestion on this) - but I think I've learned something pretty fundamental here.
Try this:
this.serviceData = function(){
return data;
};
Also you probably have a race situation. $http may not have returned by the time the application asks for the serviceData
The difference between angular's "services" and "factories" is miniscule - I suggest just using one or the other, and sticking with it. From this point out, I'll refer to these elements as "services", even though I exclusively use angular.factory to declare them.
For what you ultimately want to do, there is a much simpler solution - simply return the promise from your service.
this.get= function(url){
return $http.get(url)
}
Then in your controller/directive/whatever, just use it like:
jsonService.get(url).then(function success(response) {
// do things with the response
})
But you seem to want to use a service to create many instances, so here's a contrived example achieving that:
.factory('jsonService', jsonService)
jsonService.$inject = ['$http']
function jsonService($http) {
return Fixed
function Fixed(url) {
this.url = url
this.promise = null
this.get = get.bind(this)
function get() {
// This caches the request so you only do it once, for example
if (this.promise == null) {
this.promise = $http.get(url)
}
return this.promise
}
}
}
Then in your controller, you would do something like:
var fixedOne = new jsonService('fixedOne')
fixedOne.get().then(...)

Categories

Resources