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
Related
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.
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
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(...)
I am writing an app in AngularJs, and I am having some conceptual difficulties regarding promises. More specifically, the role of promises in asynchronously pulling data from an api.
When a user loads the app, the following should happen:
Send AJAX request to api.
Render View.
Receive response from api.
Do stuff with data.
However since the AJAX request (by definition) is asynchronous, the app tries to do stuff with the data before it is returned by the server, which causes object is undefined errors and the like.
At the I have a service something like this
app.service('bookService', ['$http', function($http){
var someData;
$http.get('Some URL').success(function (data){
someData = data.someData;
});
var bookService = {};
bookService.getSomeData = function (){
return someData;
};
return bookService;
}]);
And a controller like this
app.controller('BookController', ['bookService', function(bookService){
console.log(bookService.getSomeData().property);
}]);
The controller loads as soon as the attached view is displayed, and attempts to do something with the someData object. At that point it is still undefined (the http request has not yet returned) an errors occur.
Instead I though something like this might work :
app.service('bookService', ['$http', function($http){
var someDataPromise = $http.get('Some URL');
var bookService = {};
bookService.getSomeData = function (){
someDataPromise.success(function (data){
// This should return the data contained by the promise to the caller
// of the bookService.getSomeData function.
});
};
return bookService;
}]);
The controller would be as before.
My experience with promises, and JavaScript as whole (especially concepts like callbacks and anonymous functions) are quite weak. My suspicion is that, as concept, this probably works, although probably not in the way I have showed here.
Can promises be used in the way that I have shown here, or am I lacking in understanding as to what is going on here?
You should consider changing your service to return a promise as well:
app.service('bookService', ['$http', function($http){
var bookService = {};
bookService.getSomeData = function (){
return $http.get('Some URL');
});
return bookService;
}]);
then your controller can use promises too:
app.controller('BookController', ['bookService', '$scope', function(bookService, $scope){
bookService.getSomeData().success(function(data) {
$scope.data = data;
});
}]);
Your service would usually do a bit more than just retrieve the data but hopefully this demonstrates the concept.
It's worth noting that Angular's data binding will pick up the data as soon as you set it on the scope.
I've got a service called MyArticles. Using $http GET, collects all articles for a given Category. The MyArticles Service is injected in ControllerA, ControllerB, ControllerC, that should run some commands after MyArticles Promise is resolved.
Works after first initialisation, but I don't know what to do if I need to reset() the loaded data for MyArticles, since the commands in the ControllerA, ControllerB, ControllerC only runs the first time, after the promise is resolved. For example, what if I'd like to get Articles from a different Category ?
I'm trying to find a pattern for this and I wrote the following, I guess it helps to understand what I'd like to achieve:
var MyApp = angular.module('MyApp', []);
MyApp.config(function($locationProvider){
$locationProvider.hashPrefix('!');
});
MyApp.factory('MyService', function($q,$timeout){
var deferred;
var loadData = function(){
deferred = $q.defer();
$timeout(function(){
deferred.resolve({
myData: "test"
});
}, 250);
};
return {
reset: function(){
loadData();
},
getPromise: function(){
return deferred.promise;
}
};
});
MyApp.controller('MyCtrl', function($scope,MyService){
$scope.foo = "Service share data";
MyService.reset();
MyService.getPromise().then(function(data){
console.log(data);
});
});
*the example is also available at: http://jsbin.com/OCaNehe/2/
I wonder what I can do, if I need a service - using promises - that should be able to refresh data and the service is being injected in different Controllers, etc ?
Thanks for looking!
Another alternative is to use watches. See: http://plnkr.co/edit/auovRxGPViyflUVPvG1A?p=preview. It might be easier to maintain compared to $emit/$broadcasts later on, especially if you end up having tons of services along with directives and controllers that use them.
Also you might want to think about using the natural scope inheritance angular has, but that depends on your application.