Ok, so I have this service that is dependent on another service value that the user can change in the app interface. Something like this:
app.service('Applications', ['$resource', 'URL',
function ($resource, URL) {
var applicationsResource = $resource(URL + '/applications/:id', { id: '#id' }, {
query: {
method: 'GET',
isArray: true,
transformResponse: function(body, header) {
var response = angular.fromJson(body);
return response.data.applications;
}
}
});
var applications = applicationsResource.query(function() {
applications.current = applications[0];
});
return applications;
}
]);
app.service('Users', ['$resource', 'URL', 'Applications',
function ($resource, URL, Applications) {
return $resource(URL + '/users/:id', { id: '#id' }, {
query: {
method: 'GET',
isArray: true,
headers: {
'User': Applications.current.username,
'Pass': Applications.current.password
},
transformResponse: function(body, header) {
var response = angular.fromJson(body);
return response.data.users;
}
}
});
}
]);
Example of working controller code:
app.controller('usersController', ['$scope', '$resource', 'URL', 'Applications',
function ($scope, $resource, URL, Applications) {
$scope.users = [];
$scope.reload = function() {
$scope.loading = true;
var usersResource = $resource(URL + '/users/:id', { id: '#id' }, {
query: {
method: 'GET',
isArray: true,
headers: {
'User': Applications.current.username,
'Pass': Applications.current.password
},
transformResponse: function(body, header) {
var response = angular.fromJson(body);
return response.data.users;
}
}
});
$scope.users = usersResource.query(function() {
$scope.loading = false;
});
/*
// after injecting Users, this is what I want to do, instead of what's above
$scope.users = Users.query(function() {
$scope.userTable.reload();
$scope.loading = false;
});
*/
};
$scope.$watch('Applications.current', function (newApplication, oldApplication, scope) {
if (newApplication && newApplication !== oldApplication) {
scope.reload();
}
});
}
]);
I want to replace that usersResource with my Users service, but that's where I'm stuck now.
The issue is that no matter what I do, the Applications.current on the Users service is always null. (I only make use of this service after making sure that Applications.current is not null on the controller)
If I move the resource directly to the controller, it works, but I want to move these away from the controllers.
Any tips on how to fix or improve this?
You should know that $resource is async and you call Users service before actually you got response from server and populated applications.current. This a reason why Applications.current is null into Users service.
In your case I would use Uses service into Applications:
app.service('Applications', ['$resource', 'URL', 'Users',
function ($resource, URL, Users) {
var applicationsResource = $resource(URL + '/applications/:id', { id: '#id' }, {
query: {
method: 'GET',
isArray: true,
transformResponse: function(body, header) {
var response = angular.fromJson(body);
return response.data.applications;
}
}
});
var applications = applicationsResource.query(function() {
applications.current = applications[0];
// call the Users
Users.query(applications.current) /**/
return /* ... */;
});
return applications;
}
]);
Related
I am trying to make an update to an existing object but get the following error $scope.entry.update is not a function.
I created a service called 'budgetResource'
"use strict";
angular.module("common.services").factory("budgetResource", ["$resource", "appSettings", budgetResource])
function budgetResource($resource, appSettings) {
return $resource(appSettings.serverPath + "api/budget/:id", null,
{
'update': { method: 'PUT', isArray: true },
'delete': { method: 'DELETE', isArray: true },
'save': { method: 'POST', isArray: true }
});
}
Herewith the function in my controller where budgetResource service is injected with the function $scope.updateBudgetAmount being called.
$scope.updateBudgetAmount = function (categoryId) {
$scope.entry = new budgetResource();
$scope.entry = {
"budgetAmount": $scope.budgetAmount,
"categoryId": categoryId
}
$scope.entry.update({ id: categoryId },
function (data) {
$scope.categories = data;
$scope.category = "";
},
function (error) {
$scope.message = error.statusText;
});
}
which in turn calls the webapi method
public IHttpActionResult Put(int id, [FromBody]Category cat)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
BudgetRepository repo = new BudgetRepository();
var categories = repo.SaveCategory(cat);
return Ok(categories);
}
How can modify this so that it is dine correctly?
After you do $scope.entry = {...},$scope.entry becomes a plain javascript object, so $scope.entry.update is not exist.
I have this service :
(function() {
'use strict';
angular
.module('myApp')
.factory('Consultant', Consultant);
Consultant.$inject = ['$resource', 'DateUtils'];
function Consultant ($resource, DateUtils) {
var resourceUrl = 'api/consultants/:id';
return $resource(resourceUrl, {}, {
'query': { method: 'GET', isArray: true},
'get': {
method: 'GET',
transformResponse: function (data) {
data = angular.fromJson(data);
return data;
}
},
'update': {
method: 'PUT',
transformRequest: function (data) {
return angular.toJson(data);
},
transformResponse: function (data) {
data = angular.fromJson(data);
return data;
}
},
'save': {
method: 'POST',
transformRequest: function (data) {
return angular.toJson(data);
},
transformResponse: function (data) {
data = angular.fromJson(data);
return data;
}
}
});
}
})();
What if I want to add the url api/consultantscustom/:id (with its query/get/update methods) to this service?
As it seems that this file can contain only one factory, is there a way to do that or do I need to create another file with a new factory?
Maybe I am totally misunderstanding what you are asking, but you can have as many factories in one file as you like (or as seems useful):
(function() {
'use strict';
angular.module('myApp')
.factory('Consultant', Consultant)
.factory('Custom', Custom);
Consultant.$inject = ['$resource', 'DateUtils'];
Custom.$inject = ['$resource'];
function Consultant ($resource, DateUtils) {
var resourceUrl = 'api/consultants/:id';
...
}
function Custom ($resource) {
var resourceUrl = 'api/consultantscustom/:id';
...
}
Or, if you want to re-use the same factory to access different URLs, you could add methods to set the URL and then re-use the call to the generic resource.
function Consultant ($resource, DateUtils) {
function consultants () {
_call_resource('api/consultants/:id');
}
function custom () {
_call_resource('api/consultantscustom/:id');
}
function _call_resource (resourceUrl) {
return $resource(resourceUrl, {}, {
...
}
}
this.consultants = consultants;
this.custom = custom;
return this;
}
I'm attemping to call a RESTful method via $resource as following:
Resource:
angular.module('secure',['ngResource']).factory('Vehicle', function ($resource) {
return $resource('/secure/vehicle/index', { id: '#id' }, {
query: {
method: 'GET',
isArray: true
},
delete: {
method: 'DELETE',
isArray: false,
url: '/secure/vehicle/delete/:id'
}
});
});
Then, from other service I inject the above factory and I call DELETE method in this way:
factory.delete = function (procedureId) {
var vehicle = new Vehicle();
vehicle.$delete({id: procedureId}, function () {
//success
deferred.resolve();
}, function (errResponse) {
// fail
console.log(errResponse);
});
return deferred.promise;
};
(Don't pay attention to deferred things, it doesn't work with or without it)
Unfortunately, I always get the same answer:
Remote Address:127.0.0.1:8080
Request URL:http://localhost:8080/secure/vehicle/delete/21
Request Method:DELETE
Status Code:422 Unprocessable Entity
The call itself is set up properly (secure/vehicle/delete/21). In fact, if I do the same, but instead of using $resource variable, using $http, everything works!
$http({
'method': 'DELETE',
'url': '/secure/vehicle/delete/' + procedureId,
'headers': {
'Content-Type': 'application/json'
},
'data': ""
})
.success(function () {
// success
})
.error(function (data, status) {
console.log(data.errors);
});
So, I guess something is missing in $resource-way, but what? Any help, it will be appreciated!
EDIT:
It seems it's a backend problem when it reads the entire url call. If I call DELETE resource, using $http, adding the data: "" as I show above, the backend initializes itself properly.
But if I try the $resource-way, the required params are pre-configured and that doesn't like to the backend, so I need to find the way to say to $resource how to add something like data: "", any ideas?
Test proof that it works:
angular.module('secure', ['ngResource']).factory('Vehicle', function($resource) {
return $resource('/secure/vehicle/index', {
id: '#id'
}, {
query: {
method: 'GET',
isArray: true
},
delete: {
method: 'DELETE',
isArray: false,
url: '/secure/vehicle/delete/:id'
}
});
});
angular.module('secure').factory('VehicleFactory', function(Vehicle, $q) {
var factory = {}
factory.delete = function(procedureId) {
var deferred = $q.defer();
var vehicle = new Vehicle();
vehicle.$delete({
id: procedureId
}, function(r) {
deferred.resolve(r);
}, function(errResponse) {
console.log(errResponse);
});
return deferred.promise;
};
return factory;
});
describe('VehicleFactory', function() {
var $httpBackend, VehicleFactory
beforeEach(module('secure'));
beforeEach(inject(function(_$httpBackend_, _VehicleFactory_) {
$httpBackend = _$httpBackend_
VehicleFactory = _VehicleFactory_
}))
it('deletes vehicle - vehicle.$delete()', function() {
var r = {
data: 'correct response'
}
$httpBackend.when('DELETE', '/secure/vehicle/delete/123').respond(r)
VehicleFactory.delete(123).then(function(response) {
expect(response.data).toBe(r.data)
})
$httpBackend.flush();
});
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation()
$httpBackend.verifyNoOutstandingRequest()
})
})
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-resource.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>
Some much cleaner way to write delete functionality without $q:
angular.module('secure').factory('VehicleFactory', function(Vehicle) {
var factory = {}
factory.delete = function(procedureId) {
return (new Vehicle()).$delete({
id: procedureId
})
}
return factory;
});
I have this code in my post.serv.js and in my controller I want to execute the function delete.
"use strict";
app.factory('JnttPost', function ($resource) {
var PostResource = $resource('/api/post/:_id', {
_id: "#id"
}, {
update: {
method: 'PUT',
isArray: false
}
}, {
delete: {
method: 'DELETE',
isArray: false
}
});
return PostResource;
});
I already know how to get and update a post, for example in my createpost.serv.js
"use stric";
app.factory('JnttCreatePost', function ($http, $q, JnttPost) {
return {
createPost: function (newPostData) {
var newPost = new JnttPost(newPostData);
var dfd = $q.defer();
newPost.$save().then(function () {
dfd.resolve();
}, function (response) {
dfd.reject(response.data.reason);
});
return dfd.promise;
}
};
});
and in my newpost.ctrl.js
"use strict";
app.controller('CtrlNewPost',
function ($scope, $location, JnttIdentity, JnttNotifier, JnttCreatePost) {
var email = ...;
$scope.newPost = function () {
var newPostData = {...};
JnttCreatePost.createPost(newPostData).then(function () {
JnttNotifier.notify('success', 'The post has been created');
$location.path('/');
}, function (reason) {
JnttNotifier.notify('error', reason);
});
};
});
I can't realize how to perform the delete request, I can do with a $http
In my new controller for do deletePost() function I have this:
$scope.deletePost = function () {
var pwd = JnttIdentity.currentUser.hashed_pwd;
var postidd = {
password: pwd,
id: $scope.post._id
};
var config = {
method: "DELETE",
url: '/api/post/',
data: postidd,
headers: {
"Content-Type": "application/json;charset=utf-8"
}
};
$http(config);
$location.path('/');
};
This actually already do this stuff but I want to do this without the $http like the create request, How I can do this? How do I can edit this code below for do the request?
createPost: function (newPostData) {
var newPost = new JnttPost(newPostData);
var dfd = $q.defer();
newPost.$save().then(function () {
dfd.resolve();
}, function (response) {
dfd.reject(response.data.reason);
});
return dfd.promise;
}
In my routes.js in express I have this route:
app.delete('/api/post/', posts.deletePost);
You can either call delete on the $resource class you create (JnttPost) or call $delete on a post that's returned from the $resource class.
The $resource class already has get/save/query/remove/delete functions included so you don't need to add the delete (save is create/POST, so you need to include update with PUT).
Here's a sample of using your $resource class to call delete:
angular.module('test', ['ngResource'])
.factory('JnttPost', function ($resource) {
var PostResource = $resource('/api/post/:_id', {
_id: "#id"
}, {
update: {
method: 'PUT',
isArray: false
}
});
return PostResource;
})
.run(function(JnttPost){
JnttPost.delete({id: 123123123});
});
I have a HTTP based RESTful APIs
When i connect for example to www.domain.com/chiamate/ELSENWZ i got this result:
{
"TICKET": "155112-I",
"TICKET_2": "ATRE6463",
"ACCOUNT_NAME": "PIPPO",
"CUSTOMER_NUMBER": "AG5",
"PROBLEM_TYPE": "H",
"VENDOR": "ITALWARE-CON",
"DESCR": "HP 6300 PRO SFF",
}
I have implemented into AngularJS a service to use the rest api in this way:
var services = angular.module('ngdemo.services', ['ngResource']);
services.factory('ChiamataFactory', function ($resource) {
return $resource('/chiamate/:id', {}, {
show: { method: 'GET',
isArray: false, // <- not returning an array
transformResponse: function(data, headers){
var wrapped = angular.fromJson(data);
alert(JSON.stringify(wrapped, null, 4));
angular.forEach(wrapped.items, function(item, idx) {
wrapped.items[idx] = new Post(item); //<-- replace each item with an instance of the resource object
});
return wrapped;
} },
create: { method: 'POST' },
update: { method: 'PUT', params: {id: '#id'} },
})
});
because i want that when the controller use the service,
$scope.chiamata = ChiamataFactory.show({id: 'ELSENWZ'});
into result i need to add some extra properties.
The problem is that the service don't use the transformResponse
It is not possible to use the transformResponse to decorate the data with data from an asynchronous service
Try this pseudo-code
angular.module('myApp').service('MyService', function($q, $resource) {
var getResult = function() {
var fullResult = $q.defer();
$resource('url').get().$promise.then(function(data) {
var partialPromises = [];
for (var i = 0; i < data.elements.length; i++) {
var ires = $q.defer();
partialPromisses.push(ires);
$resource('url2').get().$promise.then(function(data2) {
//do whatever you want with data
ires.resolve(data2);
});
$q.all(partialPromisses).then(function() {
fullResult.resolve(data);
});
return fullResult.promise; // or just fullResult
}
});
};
return {
getResult: getResult
};
});
or you can use transformResponce with $http as described in the documentation
angular $http documentation