i am wanting to change my service from a hardcoded static array to an array returned from a $http call. i have attempted to do this below however it does not work.
I can confirm the data returned from http is working and returns the correct data( i have taken the link out for the purpose of the question).
i am not getting an error message and therefore cannot give any further information at this point however what i would like to know is if i am doing this in the correct way.
banging my head against a wall for such a simple task at the moment....
hardcorded array:
.factory('Cards', function($http){
var cardTypes = [
{id: 1, USECASE_NAME: "Frank", USECASE_IMAGE: 'img/Frank.png', USECASE_DESC:"This is frank the bank card, He helps people all over the world make millions of transactions each year!", done: true },
{id: 2, USECASE_NAME: "John Lewis", USECASE_IMAGE: 'img/JohnLewis.png', USECASE_DESC:"John Lewis is one of the biggest retailers in the United Kingdom with a very proud reputation", done: true },
{id: 3, USECASE_NAME: "Generali", USECASE_IMAGE: 'img/Generali.png', USECASE_DESC:"Generali is the largest insurance company in Italy and arguably one of the biggest in Europe", done: true },
];
return {
all: function() {
return cardTypes;
}
}
});
$http callback
.factory('Cards', function($http) {
var cardTypes = {};
$http.post("http://url", {
"auth": "cats",
"name": "Adam",
"uuid": "fasfA"
}).
success(function(data, status, headers, config) {
cardTypes = data;
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
return {
all: function() {
return cardTypes;
}
}
});
Remember that .http() is an asynchronous call. So calling .all() directly after initalizing the factory will result in returning an empty object, because the post request is probably still pending when .all() is called.
I would recommend using a service (a factory is possible as well, but imo a service would fit better here) and returning the promise like:
this.getAll = function() {
return $http.post("http://url",{"auth":"cats","name":"Adam","uuid": "fasfA" });
}
Then in your controller you can do this:
Cards.getAll().then(function(c){
$scope.cards = c;
})
Related
I'm having a hardtime to create a Test with the Controller that uses promise when doing initialization. here's my angularjs scirpt.
Javascript.js
var appModule = angular.module('appModule', []);
appModule.controller('c0001Controller', function($http, $window) {
var user = {};
this.c0001Data = user;
this.submitForm = function() {
if(!angular.isUndefined(this.c0001Data.user_id) && !angular.isUndefined(this.c0001Data.password))
{
var promise = $http.post('C0001Login', user);
promise.success(function(data, status, headers, config) {
if(data.message == 'error'){
alert('Invalid Username/Password');
} else {
$window.location.href = data.url + "#/c0003";
}
});
promise.error(function(data, status, headers, config) {
alert("Invalid Username/Password");
});
}
else {
alert ("Invalid/ Username/password");
}
};
});
Using $httpBackend is more like setting up a fake call to intercept the original call of your $http service in your test cases.
Say you in your controller/service you have an $http get that gets from the request url 'api/employees'. In your test you would like to do something like this before the actual call to your function that calls $http:
$httpBackend.expectGET('api/employees').and.return(200, [
{ id: 1, name: 'sample' },
{ id: 2, name: 'sample 2' }
]);
(JasmineJS) In this way the original $http get request to your url 'api/employees' will not be called instead, the $httpBackend's setup/expected call will be called, and a http status code of 200 along with the array data will be returned.
This would work well on expecting a POST with data parameters. You should always know the request url, the data and other configs used in your original $http calls.
P.S. Always return appropriate HTTP status codes when using $httpBackend. Say, returning an HTTP status code 400 will trigger your catch block in the code you're testing.
I have simple RESTful server with Flask and I like to make a simple client with AngularJS using ngResource. The idea is make a GET to the server, and obtain a json file.
This is my services.js
var IpZapServices = angular.module('IpZapServices', ['ngResource']);
IpZapServices.factory('Plug', ['$resource', function($resource) {
return $resource('http://localhost:8003/api/plugs/:id',
{id : "#id"}, {
query: {method: 'GET', params: {}, isArray: false}
});
}]);
And the controllers.js
var IpZapControllers = angular.module('IpZapControllers', []);
IpZapControllers.controller('PlugListCtrl', ['$scope', 'Plug', function($scope, Plug) {
$scope.plugs = Plug.query();
console.log($scope.plugs);
}]);
But, I don't get the json file, get this:
Object { $promise: Object, $resolved: false }
Why? What's I do wrong? Thanks!
EDIT:
This is the raw response that I receipt from the server.
{"plugs": [
{"alarm": [],
"id": 0,
"name": "Plug 0",
"state": false},
.
.
.
{"alarm": [],
"id": 3,
"name": "Plug 3",
"state": false}
]}
EDIT 2: Solution
The problem is in the server. Simply add to the server Flask-CORS and work!
from flask.ext.cors import CORS
app = Flask(__name__)
cors = CORS(app, resources={r"/api/*": {"origins": "*"}})
The solution is from this question
You need to resolve the promise you have created with Plug.query()
Try this:
Plug.query().$promise.then(function (response) {
console.log(response)
$scope.plugs = response;
});
More information on this can be found in the docs on angularjs.org:
https://docs.angularjs.org/api/ngResource/service/$resource
Try this:
var IpZapControllers = angular.module('IpZapControllers', []);
IpZapControllers.controller('PlugListCtrl', ['$scope', 'Plug', function($scope, Plug) {
$scope.plugs = Plug.query();
$scope.plugs.$promise.then(function (result) {
console.log(result);
$scope.plugs = result;
});
}]);
Explanation:
Your call to query will not return the data immediately. It instead returns a promise object that will eventually get your data once the HTTP request is complete (notice that the console message said resolved: false).
Read More:
Promises in AngularJS, Explained as a Cartoon
It seems that you have a promise there.
Don't assign the return value of the function, you need to wait till the promise resolves or rejects
Try
Plug.query().$promise
.then(function (response) {
// success code here
})
.catch(function (err) {})
Whwn connecting to a webapi from the controller, use the success and error promise from the method call.
Ctrl.$inject = ["$scope", "Service"];
function Ctrl($scope, Service) {
Service.getJson().success(function (data) {
$scope.json = data;
})
.error(function (data) {
// data should show the error
});
}
Simple problem with hopefully simple enough solution.
I have defined multiple services to perform CRUD operations with tags.
myApp.factory('GetTags', ['$resource', function ($resource) {
return $resource('/myApp/API/Service/GetTagList', {}, {
query: { method: 'GET', params: { groupId: 'groupId' }, }, isArray: true,
});
}]);
myApp.factory('GetTag', ['$resource', function ($resource) {
return $resource('/myApp/API/Service/GetTag', {}, {
query: { method: 'GET', params: { tagId: 'tagId' }, }, isArray: true,
});
}]);
myApp.factory('SaveTag', ['$resource', function ($resource) {
return $resource('/myApp/API/Service/CreateTag', {}, {
query: { method: 'POST', params: {/*createObj*/}, }, isArray: true,
});
}]);
myApp.factory('UpdateTag', ['$resource', function ($resource) {
return $resource('/myApp/API/Service/UpdateTag', {}, {
query: { method: 'POST', params: {/*updateObj*/}, }, isArray: true,
});
}]);
Later on in my controller I want to do something like this in my tags function:
myApp.controller('myCtrl', ['$scope', '$routeParams', 'GetTags', 'GetTag', 'SaveTag', function ($scope, $routeParams, GetTags, GetTag, SaveTag) {
...
// the goal of this function to keep a copy of
// tags collection in client memory that mimics database
// as well as adding selected tags to forms object
// eg: myForm = {... Tags: [], ...}
$scope.addtag = function (tag, subTag){
...
if (!($scope.tags.length > 0)) {
// skip checking and just add tag
SaveTag.save({ Name: tag, Desc: "", ParentId: null, GroupId: 12, }, function (data) {
console.log('save tag: ', data);
//todo: should wait for data of save operation to comeback
// before moving on to requesting a full object
GetTag.get({ tagId: data.Id, groupId: 12, }, function (data) {
console.log(' get tag: ', data);
tagObj = data.tag;
});
});
// push newly created tag into tags collection
$scope.tags.push(tagObj);
...
};
...
});
I know a skipped a lot of details from my controller and function in question but basically the reason why I am calling save followed by get is because of the tag + subTag scenario. I didn't want to complicate the processing logic if I was to pass a complicated object to the server for processing. Say if I had to create a tag followed by a subTag the javascript logic would look like this:
...
// skip checking and just add tag and subTag
SaveTag.save({ Name: tag, Desc: "", ParentId: null, GroupId: 12, }, function (data) {
//todo: should wait for data of save operation to comeback
// before moving on to requesting a full object
GetTag.get({ tagId: data.Id, groupId: 12, }, function (data) {
tagObj = data.tag;
});
});
// push newly created tag into tags collection
$scope.tags.push(tagObj);
SaveTag.save({ Name: subTag, Desc: "", ParentId: tagObj.ParentId, GroupId: 12, }, function (data) {
//todo: should wait for data of save operation to comeback
// before moving on to requesting a full object
GetTag.get({ tagId: data.Id, groupId: 12, }, function (data) {
tagObj = data.tag;
});
});
// push newly created sub tag into tags collection
//todo: find parent index
$scope.tags[parent_index]["Elements"].push(tagObj);
...
But in case you wonder, yes, I could return full object from save operation which I probably will in the near future. It's better to reduce number of async calls because they impact overall performance.
But for now I have a few questions:
At the moment I have four different services declared as per Angular documentation. But it would be more efficient if it was a single factory with multiple functions. Can someone point me in the right direction here please.
Is it possible to somehow stop and wait for data.$resolved property to turn true when I call save service so that I can then call get services with returned value? Or perhaps there is an alternative method of doing this?
I am digging into $q of Angular Documentation to see if I can utilise something from here.
Just in case people wonder I have come across a few examples where people utilised resolve property with $routeProvider. Unfortunately my scenario is done in real time during user interactions.
Any help and all advice is greatly appreciated.
References:
AngularJs Docs - $q
Very good explanation and examples - Using and chaining promises in AngularJS
AngularJs Docs - $resource (last example shows use of $promise)
Update:
It seem that my hunch was right. While I haven't got it to work yet I feel the answer lies with $q and chaining promises. Now I just need to get it to work.
I hate to answer my own question but I have found the solution that works.
Update:
OK, after digging about and scratching my head I came up with this code:
// skip checking and just add tag
SaveTag.save({ Name: tag, Desc: "", ParentId: null, GroupId: 12, }).$promise.then(function (data) {
console.log('save tag: ', data);
return data;
}).then(function (data) {
console.log('data: ', data.Id);
GetTag.get({ tagId: data.Id, groupId: 12, }).$promise.then(function (data) {
console.log(' get tag: ', data);
return data;
}).then(function (data) {
console.log('data: ', data.tag);
$scope.tags.push(data.tag);
tagObj = data.tag;
//todo: check for duplicate records
// because repeater will complain and
// it's pointless to have duplicate tags
// on an item to begin with
$scope.myForm.Tags.push(tagObj);
});
});
Notice the $promise after each service call. $promise exposes the raw $http promise that is returned by $resource. This allows me to use .then() to chain additional logic.
So, in the end all I ended up doing is change the way I call my services. That is all.
I am configuring an AngularJS app and am having a little trouble ensuring that my promises are firing before the controllers are loading. My understanding is that this can be done. A bit of code:
First, here's the router code:
$routeProvider
.when('/apage', {
controller: 'APageController',
templateUrl: 'app/apage/apage.view.html',
requiresLogin: true,
resolve: {
"data": function($q, data1Service, data2Service) {
var data1 = data1Service.getData();
var data2 = data2Service.getData();
return $q.all({
data1: data1.$promise,
data2: data2.$promise});
}
}
})
...
Here's one of the service functions (both are similar)...
function getData() {
var deferred = $q.defer();
$http(req)
.success(function(data, status, headers, config) {
// store data ...
deferred.resolve(1); // just a flag to say it worked
$rootScope.$apply();
})
.error(function(data, status, headers, config) {
deferred.resolve(0);
$rootScope.$apply();
});
return deferred.promise;
}
And here's the controller code...
angular
.module('app')
.controller('APageController', APageController);
APageController.$inject = [... 'data'];
function APageController(... data) {
var data1 = data.data1;
var data2 = data.data2;
...
All three are in different files but part of the same module. There must be some concept I'm overlooking. Is there anything apparent here to explain why my resolve promises are not firing?
Thank you.
If you pass in object literal to q.all it will resolve immediately. Instead pass array of promises so that it waits for it to resolve reject. Also .$promise is not needed in your case because you are already returning a promise from your service(based on the displayed code). $promise is generally attached by the object returned by $resource
i.e
return $q.all([data1Service.getData(),
data2Service.getData()]);
and expect data to be array of data resolved from above 2 calls.
or create 2 resolves:
resolve: {
"data": function($q, data1Service, data2Service) {
return data1Service.getData();
},
"data2": function($q, data1Service, data2Service) {
return data2Service.getData();
}
}
and inject data and data2.
otherwise, chain through and change the response format to expect the way you were originally trying to get.
resolve: {
"data": function($q, data1Service, data2Service) {
return $q.all([data1Service.getData(),data2Service.getData()])
.then(function(response){
return {
data1:response[0],
data2:response[1]
};
});
}
}
Do not place rootScope.apply inside your service, it will cause digest already in progress error (since angular will ). $http will automatically resolve it.
You just need this.
function getData() {
return $http(req);
}
I'm trying to write a factory which exposes a simple users API. I'm new to AngularJS and I'm a bit confused about factories and how to use them. I've seen other topics but none that are a good match to my use case.
For the sake of simplicity, the only functionality I'd like to achieve is getting all users in an array and then pass them to a controller through the injected factory.
I stored the users in a json file (for now I only want to read that file, without modifying the data)
users.json:
[
{
"id": 1,
"name": "user1",
"email": "a#b.c"
},
{
"id": 2,
"name": "user2",
"email": "b#b.c"
}
]
The factory I'm trying to write should be something like this:
UsersFactory:
app.factory('usersFactory', ['$http', function ($http) {
return {
getAllUsers: function() {
return $http.get('users.json').then(
function(result) {
return result.data;
},
function(error) {
console.log(error);
}
);
}
};
}]);
And finally, the controller call would be like this:
UsersController
app.controller('UsersCtrl', ['$scope', 'usersFactory', function($scope, usersFactory){
usersFactory.getAllUsers().then(function (result) {
$scope.users = result;
});
}]);
I've searched the web and it seems like it is not really a good practice to use factories this way, and if I'd like to achieve some more functionality like adding/removing a new user to/from the data source, or somehow store the array within the factory, that wouldn't be the way to do it. I've seen some places where the use of the factory is something like new UsersFactory().
What would be the correct way to use factories when trying to consume APIs?
Is it possible to initialize the factory with an object containing the $http.get() result and then use it from the controller this way?
var usersFactory = new UsersFactory(); // at this point the factory should already contain the data consumed by the API
usersFactory.someMoreFunctionality();
I don't see anything wrong with your factory. If I understand correctly you want to add functionality. A few small changes would make this possible. Here's what I'd do (note that calling getAllUsers wipes out any changes):
app.factory('usersFactory', ['$http', function ($http) {
var users = [];
return {
getAllUsers: function() {
return $http.get('users.json').then(
function(result) {
users = result.data;
return users;
},
function(error) {
users = [];
console.log(error);
}
);
},
add: function(user) {
users.push(user);
},
remove: function(user) {
for(var i = 0; i < users.length; i++) {
if(users[i].id === user.id) { // use whatever you want to determine equality
users.splice(i, 1);
return;
}
}
}
};
}]);
Typically the add and remove calls would be http requests (but that's not what you're asking for in the question). If the request succeeds you know that your UI can add/remove the user from the view.
I like my API factories to return objects instead of only one endpoint:
app.factory('usersFactory', ['$http', function ($http) {
return {
getAllUsers: getAllUsers,
getUser: getUser,
updateUser: updateUser
};
function getAllUsers() {
return $http.get('users.json');
}
function getUser() {
...
}
function updateUser() {
...
}
}]);
That way if you have any other user-related endpoints you can consume them all in one factory. Also, my preference is to just return the $http promise directory and consume the then() in the controller or where ever you're injecting the factory.
I'm really a fan of route resolve promises. Here is John Papa's example. I will explain afterwards how to apply this to what you're doing:
// route-config.js
angular
.module('app')
.config(config);
function config($routeProvider) {
$routeProvider
.when('/avengers', {
templateUrl: 'avengers.html',
controller: 'Avengers',
controllerAs: 'vm',
resolve: {
moviesPrepService: moviesPrepService
}
});
}
function moviesPrepService(movieService) {
return movieService.getMovies();
}
// avengers.js
angular
.module('app')
.controller('Avengers', Avengers);
Avengers.$inject = ['moviesPrepService'];
function Avengers(moviesPrepService) {
var vm = this;
vm.movies = moviesPrepService.movies;
}
Basically, before your route loads, you get the request data you need (in your case, your "users" JSON.) You have several options from here... You can store all that data in a Users factory (by the way, your factory looks fine), and then in your controller, just call Users.getAll, which can just return the array of users. Or, you can just pass in users from the route resolve promise, much like John Papa does in his example. I can't do it as much justice as the article he wrote, so I would seriously recommend reading it. It is a very elegant approach, IMHO.
I typically use a factory something like this:
.factory('usersFactory', ['$resource',
function($resource){
return $resource('http://someresource.com/users.json', {}, {
query: {
method:'GET',
isArray:true
}
})
}])
Which you could call with:
usersFactory.query();
As this is a promise you can still use the .then method with it too
$http is a promise that means you have to check whether your get call worked or not.
so try to implement this type of architecture in your controller
$http.get('users.json')
.success(function(response) {
// if the call succeed
$scope.users = result;
})
.error(function(){console.log("error");})
.then(function(){
//anything you want to do after the call
});