I have this model called Project and whose members are rows in a table, more specifically, a table decorated by the jQuery DataTables plugin
Here's my project model in coffeescript
#= require vendor/angular.js
Project = angular.module('Project', ['ngResource'])
.value( 'csrf', $('meta[name="csrf-token"]').attr('content') )
.factory('Project', ['$resource','csrf', ($resource, csrf) ->
$resource '/projects/:project_id/:action', {authenticity_token: csrf, project_id:'#id'},
query:
method: 'GET'
isArray: yes
new:
method: 'GET'
params:
project_id: 'new'
edit:
method: 'GET'
update:
method: 'PUT'
])
Ene drawback of the DataTable plugin used alongside angular is that whenever I so a project.$update(), that project disappears from the view
So I have to sort of reload the DataTable by re-initializing it, I can live with that for now but I was wondering then, if there is a way that I can set a universal callback for $resource, where I can put this DataTables re-initialization call?
thanks!
I'm assuming that in the controller from whence you call the factory you have some sort of wrapper method.
lets call it scope.crudData
SomeCtrl = (scope, http, projectService)
scope.crudData(p_id, act, method) ->
meth = YOUR LOGIC FOR PARSING AN ACTUAL FUNCTION FROM THE METHOD NAME GOES HERE
projectService.meth
project_id: p_id
act: action
, (response) ->
THIS IS WHERE YOUR CALLBACK LIVES. KNOCK YOURSELF OUT. RELOAD THE DATA DO ANYTHING YOU WANT
If that's not good enough, you could I suppose register a listener in config for all ajax requests, verify the data, etc.
Let me know if that's what you are looking for and I'll put together some pseudocode for you.
Related
We will soon be refactoring our code against the Angular Style Guide. The guide itself is great (and can be found slightly modified all over the interwebs), but no one mentions how $resource fits into a factory, or any reasons why it might have been left out. One guide says to use $resource over $http where you can, but then doesn't add it into their style for factories :/.
I remember reading in lots of places that $resource was better and that's why I started to use it, but now I'm forgetting why and wondering if that is still true - especially given the resource object at the bottom of this post. There are some opinions (Papas own, and again) about $resource (not?) being great, but that's another issue that I'm re-checking.
So, assuming we want to use $resource and given this sample code below, where does $resource fit in so that it adheres to the reasoning behind the styles in the guide? Also, if your answer is "It doesn't. The style [subtly] recommends $http because bla, bla and bla.", then that would be a useful as well.
(function() {
'use strict';
angular
.module('myModule')
.factory('oneService', oneService);
predicateService.$inject = ['twoService', 'anotherService'];
/* #ngInject */
function oneService(twoService, anotherService) {
var service = {
doSomething: doSomething,
etc: etc
};
// pos 1 (it really only works here but can be LONG)
// var fancyResource = $resource('/path/to/thing', '...');
// Ideally, this should be kept close to the top, right?
return service;
// pos 2 (here or below ////// is cleaner, but doesn't work)
// var fancyResource = $resource('/path/to/thing', '...');
////////////////
function doSomething() {}
// rest of functions here etc...
}
})();
Now, the only place that we use $resource (and maybe this is also incorrect) is within methods like doSomething(). At various points in the past, and even in various places in our code today, fancyResource is made public by the service and used directly from the controller: oneService.fancyResource.get(). I'm thinking this may be the intended use for $resource, but I'm not sure anymore.
Also, consider that one service might be quite large (never mind the fact that some of this should/could be broken into multiple resources; let's just pretend a resource object this size is likely and many verbs are needed):
var userResource = $resource(baseApiPath + 'users', {}, {
get: {
method: 'GET',
headers: utilityService.getHeaders('sampling'),
isArray: true,
transformResponse: function(response){
response = JSON.parse(response);
if(response.result){
return response.result.users;
}
return response;
}
},
getUserDetails: {
method: 'GET',
url: baseApiPath+'users/:userId',
params: {
userId: '#userId'
},
headers: utilityService.getHeaders('sampling'),
transformResponse: function(response){
response = JSON.parse(response);
if(response.result){
return response.result.user;
}
return response;
}
},
getUserByRole: {
method: 'GET',
url: baseApiPath+'users/roles/:roleId',
params: {
roleId: '#roleId'
},
headers: utilityService.getHeaders('sampling'),
},
getLoggedInUserData: {
method: 'GET',
url: baseApiPath + 'users/userData',
headers: utilityService.getHeaders('sampling'),
},
getGrantedAuth: {
method: 'GET',
url: baseApiPath+'users/applicationPermissions/userId/:userId/:applicationId/',
params: {
applicationId: '#applicationId',
userId: '#userId'
},
headers: utilityService.getHeaders('sampling'),
}
});
So, I think I've found my answer based on a few thoughts.
Firstly, I now realize that using a $resource like this is totally incorrect for two reasons. The first is that I was creating additional actions that required their own unique path. The whole point of a $resource is to make doing GET, PUT, POST, DELETE on a single REST resource easier. I was basically combining my resources because they appeared to be unified. For example, /users and /users/roles/:roleId should have been two different resources (and probably put into two different services to maintain the single responsibility style).
The second way I was using $resource wrong is actually because I wasn't really using the query, save, or delete methods that it supplies me with. I would just create another custom action for whatever I wanted to do. Sometimes this also included a unique URL like /users/:userId/delete, and that was because the API wasn't always a REST API. $resource is specifically designed for REST compliant APIs. Because it wraps $http and it can pass parameters to it, it's easy to fall into this trap. $resource is not intended to be a configuration for multiple $http uses.
So, now with that out of the way, here is how I would propose to include $resource into a factory, and still follow the style guide.
First, $resource should only be used with a true REST API. One where you only have/need one path, and only/mostly HTTP methods are used to interact with it. Also, because a factory is intended to represent and manage one kind of 'thing', interacting with the 'thing API', there should really only be one $resource per service. Extending the example, there would be a users service and a roles service; each with one $resource. There could then be another userRoleService that utilizes them both, and doesn't actually do any $resource stuff on its own. Something like that, anyway.
This being the case, the $resource config would actually be significantly shorter than what I was originally posting. Since it's smaller, we can treat it more like a variable declaration and put it above the service object that we create.
(function() {
'use strict';
angular
.module('myModule')
.factory('oneService', oneService);
predicateService.$inject = ['anotherService', '$resource'];
/* #ngInject */
function oneService(anotherService, $resource) {
// this resource is unlikely to get longer than this
var userResource = $resource('http://api.com/users/:userId', {
userId: '#userId'
});
// and we're still able to see all bindables at the top
var service = {
doSomething: doSomething,
etc: etc
};
return service;
////////////////
function doSomething() {
// and in a service method, we can use the resource like this,
userResource.query().$promise
.then(function(response){...})
}
function doSomethingElse() {
// or we could use the $resource in a way that would make
// chainable with another .then() in the calling method.
var load = userResource.query();
// do some internal stuff when we get the response
load.$promise
.then(function(response){...});
// then return the $resource object, or load.$promise
// so that another .then can be chained afterwards.
return load;
}
// rest of functions here etc...
}
})();
Anyway, that's the answer that I came up with. I hope this helps some of you who came here looking for what I was looking for (and couldn't easily find).
I have the following scenario, a page that will show different widgets with different data, the back-end is ASp.NET Web API 2 with SQL Server and EF + Repository Pattern + Unit Of Work.
If I have to show quite some data, including user profile and other information on top of the widgets information, what will you recommend:
make one big $http.get request that will return a big json and bind that one to the UI
or
each controller (service) when it loads will make it's unique call to back-end and get's the data it needs to display, that means each widget will make a call to back-end and retrieve it's values.
I just want to know what do you recommend as a best practice.
IMHO the best way is to separate every request into single service methods that way you can reuse just a part of it and not make server calls to load to whole data, check the angular-resource $resource to have a clean reusable service of server calls and not a bunch of $https arround your code:
example:
A service that points some url of your backend server
.factory('ClientService', ['$resource', function($resource){
return $resource('http://some_url/:controller/:method', null, {
"agents": { method: 'GET', params: { controller: 'agent', method: 'search' }, cache: false },
"query": { method: 'GET', params: { controller: 'client', method: 'search' }, cache: false },
"save": { method: 'POST', params: { controller: 'client', method: 'save' } },
"delete": { method: 'POST', params: { controller: 'client', method: 'delete' } }
})
}])
The use in the controller (Injecting ClientService as dependency)
// If i want to query the agents into a scope element
// that will call the url = http://some_url/agent/search
$scope.agents = ClientService.agents();
// If i want to query a single client i cant send adtional params
// as is a get request it will call http://some_url/client/search?id=5
$scope.client = ClientService.query({id:5});
// and you can event manage callbacks if you want to
// This will send the client object to the url = http://some_url/client/save
ClientService.save($scope.client).$promise.then(function(response){ alert(response) })
As you can see this way you can access just the things you need from the backend server not having to do all the callback response if you dont need to and in a reusable cleaner way
Info Angular Resource Docs
I think it depends...
If performance might be a problem you should think about what is best for your User... Will the overhead of making 4 HTTP requests affect the user experience in anyway? Also, would a one big request take too much time to retrieve info from the database?
However if you want just to use a developer perspective of the problem, I'd prefer doing 1 generic API call then calling it 4 times in Angular with different parameters for each Widget.
It is likely that making 4 requests will actually be faster. Not to mention the data can start being populated on the screen as it comes back, instead of needing to wait for the slowest service.
For the max number of concurrent AJAX requehttp://www.coderanch.com/t/631345/blogs/Maximum-concurrent-connection-domain-browsers
I am using the following code:
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", "/Scripts/Pages/Home.js", false);
xmlhttp.setRequestHeader("X-Custom-Header", "My Values");
xmlhttp.send();
var m = document.createElement('script');
m.appendChild(document.createTextNode(xmlhttp.responseText));
document.getElementsByTagName('head')[0].appendChild(m);
Can someone advise me if it is possible to get a javascript with $http and show me how I can do it inside a function that returns a promise when it is completed. The reason I would like to use $http is that along with the request for the js I need to send a custom header for authorization.
Please note that this question is different from the one suggested as a duplicate in that I am also wanting to find out if I can get a javascript and add it to the page DOM in the same way as it was done with the .setRequestHeader. Thanks
Since $http is a implementation for XMLHttpRequest in Angular, you can of course make requests to get the contents of a JS file.
You can set additional headers with $http like this:
$http({
method: 'get',
url: 'some/js/file.js',
headers: {
"X-Custom-header": "foo"
}
}).then(function (data) {
// do something with the DOM here
});
So as you can see, you are actually able to do that.
I'm making a mobile app using ionic (based on the AngularJS framework) where I want to display data from a RESTfull API. I'm pretty new to AngularJS so I'm not familiar with the best practices. I want to get data from this API but with a weeknumber parameters in the URL so I can get data from specific weeks. I have looked especially to the example of the AngularJS website.
This is what I have in my services:
var eventServices = angular.module('starter.services', ['ngResource']);
eventServices.factory('EventServiceAPI',['$resource',
function($resource) {
return $resource('http://localhost:8080/schedulingAPI/plannedevents?weeknumber=:weeknumber', {}, {
query: { method: 'GET', params:{weeknumber:'weeknumber'}, isArray: true}
});
}]);
This is what I have in my controller to get the API data:
$scope.events = EventServiceAPI.get({weeknumber: 7});
However, I still keep getting the error:
Error: [$resource:badcfg] object
When I use the full API URL in services and $scope.events = EventServiceAPI.query() in my controller, the full API-data is displayed without error.
I also don't get where to put the parameters; in the brackets after the resource URL or the params option in the query method.
Edit:
The output of http://localhost:8080/schedulingAPI/plannedevents?weeknumber=:weeknumber
[{"id":2985,"event":{"eventId":589,"subject":"Masterproef","year":2014,"weekNumber":7,"dayNumber":6,"startHour":8,"startMinute":10,"endHour":12,"endMinute":45,"startTime":"2014-02-14T07:10:00Z","endTime":"2014-02-14T11:45:00Z","classgroups":[{"id":8,"name":"4ELICTI"},{"id":4,"name":"4ENAU"},{"id":10,"name":"4ELICTE"},{"id":1,"name":"4CHB"},{"id":3,"name":"4ENEL"},{"id":9,"name":"4EMEM"},{"id":2,"name":"4CHC"},[]],"teacher":null},"rooms":[{"id":24,"abbr":"D015"}]},{"id":4021,"event":{"eventId":604,"subject":"Bedrijfsbeleid 2 hc","year":2014,"weekNumber":7,"dayNumber":6,"startHour":8,"startMinute":10,"endHour":9,"endMinute":35,"startTime":"2014-02-14T07:10:00Z","endTime":"2014-02-14T08:35:00Z","classgroups":[{"id":6,"name":"4ELICT"},[]],"teacher":null},"rooms":[{"id":44,"abbr":"G120"}]}]
Replace params:{weeknumber:'weeknumber'} with params:{weeknumber:'#weeknumber'}, notice the #.
If the parameter value is prefixed with # then the value of that
parameter is extracted from the data object (useful for non-GET
operations).
From angular documentation
And call your function with query:
$scope.events = EventServiceAPI.query({weeknumber: 7});
Actually, you can simplify your resource declaration like this:
eventServices.factory('EventServiceAPI',['$resource',
function($resource) {
return $resource('http://localhost:8080/schedulingAPI/plannedevents');
});
}]);
Angular will automatically append the query string for you
When I instantiate the following code in an AngularJS app, I get weird data in the transformResponse function (bottom of code). I'm not calling the $resource function from any controller, just loading the script in a browser. The data variable (see code) contains the HTML of the current partial, when loading the app.
This seems odd. Is this the way it's supposed to be?
var buddyServices = angular
.module('buddyServices', ['ng','ngResource'])
.factory('Buddy',
function ($resource) { console.log('resource');
return $resource('http://webservice.buddyplatform.com/v1/:service',
{service:'', BuddyApplicationName: 'xxx',
BuddyApplicationPassword: 'yyy'}
);
}
)
.config(function($httpProvider){
delete $httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.transformResponse = function(data) {
console.log(data);
return 'TEST: '+data;
};
});
=== EDIT ===
It just daunted on me: $httpProvider handles all http requests, so a page load is one of those. I'm guessing a bit now, but it seems probable. So, the question then becomes: Is there an "easy" way to constrain the data in the code above to only those requests performed by my service?
transformResponse takes another parameter headersGetter. You can use this to get the headers send with the response. Look for Content-Type header header. It should contain application/json