Can angular resource do a bulk restful operation? - angularjs

Say I have a todo application and clicking a checkbox on any individual Todo marks it as complete and does a PUT operation.
Then there is a checkbox to 'mark all complete' or 'mark all incomplete'. This should mark every todo as completed/incomplete, regardless of what its individual status is.
When using angular-resource, what is the best practice way to update all the items. Is it possible to do it in a single bulk request and have all the items updated? Or would I be better off just updating each one individually?

You could extend your Angular resource by providing a custom action, for example:
var Todo = $resource('api/todo/:todo_id', {todo_id: '#id'}, {
markAllComplete: { method: 'POST', params: { complete: true }, isArray: true }
}
and then in your controller doing:
// Assuming your todos have been fetched and are stored
// in the $scope.todos variable...
Todo.markAllComplete($scope.todos);
The only thing (and arguably the hardest thing) left to do would be to code your backend to accept a POST to 'api/todo' and mark all the referenced todos as completed.

Related

ngResource query with composite key parameter

I have a resource, Answer, which has a composite key made of QuestionnaireId and QuestionId. The ngResource code is as follows:
function answerResource($resource) {
return $resource("/api/answers/:questionnaireId/:questionId",
{
questionnaireId: "#questionnaireId",
questionId: "#questionId"
}
);
}
I want to query this resource with the the questionnaire Id and get back all the answers. If I use:
answerResource.query(
{
questionnaireId: questionnaireId
}
);
Then the requested url is:
/api/answers/123
When I want it to be:
/api/answers?questionnaireId=123
Otherwise I have two routes that I need to handle for the query search - one with the Id in the querystring, the other with the Id as part of the url path. (I also have queries with search text where the questionnaire Id might not be present, that would use urls like /api/answers?q=sometext).
Surely any .query parameters should be passed as querystrings, not as part of the route. How do I get the desired behaviour?
The best option I can come up with, is to create a new search method on the resource, which doesn't have the parameters in the url:
// normal resource definition here...
,{
search: {
method: "GET",
url: "/api/answers",
isArray: true
}
}
Calling that with the composite key values will append them as querystring parameters and not as part of the url, e.g. /api/answers?questionnaireId=123
Faced a similar issue, but I'm messing with composite key (item, sequence).
1- html.erb: Borrowed option 2 from https://spin.atomicobject.com/2013/11/22/pass-rails-data-angularjs/
That way I could grab inside the .js script the item from the .erb form I already have (I'm doing some kind of "manage details -- crud" on request with angularjs)
2- in the controller .js file I grab the value from the custom data in the html element using document.getElementById and getAttribute, concatenating that to the request: '/items?personId='+div.getAttribute("data-personId")+'&format=json'
3- in the items_controller index action, by default rails g expects no argument and grabs all items, then I had to ask
if params[:personId].present?
#items = Item.where(personId: params[:personId])
else
grab-all-items
end
This works, but I'm not sure if it's the best approach because I'm a real newbie for angularjs.
Just messing with this issue, cannot figure yet how to remove a record.
$resource crete path instead of querystring because you define placeholders :
return $resource("/api/answers/:questionnaireId/:questionId"..
If you delete them from resource-level and then call $resource.query({param: value}) you can make querystring, also you can specify more actions for single $resource to perform different request overriding action url property.
I created a simple (and very fast) example might help you understand:
working example :https://jsfiddle.net/Nedev_so/b71feyc6/19/
EDIT :
after your comment i understand what you need, so
resource factory :
//only for example purpose
var answersBaseUrl = "https://example.com/api/answers";
var answerTemplateUri = commentsBaseUrl + '/:questionnaireId/:questionId'
var params = {questionnaireId: '#_questionnaireId',questionId: '#_questionId'};
var res = $resource(answerTemplateUri, params,{
one :{
method: "GET",
},
all: {
method: "GET",
url: answersBaseUrl,
isArray: true
}
});
return res;
});
and then in your controller :
//get answers by questionnaire
//GET /answers?questionnaireId=123
$scope.answersByQuestionnaire = answers.all({questionnaireId: 123});
//get answers by question
//GET /answers?questionId=123
$scope.answersByQuestion = answers.all({questionId:123});
//get single answer by questionnaire and question
//GET /answers/123/123
$scope.answer = answers.one({questionnaireId: 123,questionId:123 });
(check network logs and you can see expected behaviour)

How to handle one-to-many relationships in Flux stores

I'm just starting to use flux (with redux for now) and am wondering how relationships are supposed to be handled.
For an example we can use Trello that has boards with columns that contains cards.
One approach would be to have one store/reducer for boards and have all the data in it there but that means some very fat stores since they would have to contain all the actions for columns and cards as well.
Another approach i've seen is separating nested resources into for example BoardStore, ColumnStore and CardStore and use their ids as reference.
Here's an example of where I am a bit confused: you could have an action creator called addCard that does a request to the server to create a card with all the data. If you are doing optimistic update, you would have created a card object in one of your store before but you can't know the id it will have until you get back the request.
So in short:
Firing addCard
addCard does a request, in the meantime you return an action of type ADD_CARD_TEMP
you get the request and return an action of type ADD_CARD where the store/reducer changes the id.
Is there a recommended way to deal with this case? Nested store/reducers look a bit silly to me but otherwise you end up with very complex stores so it looks like a compromise really.
Yes, using ids across multiple stores much like a relational database is the way to do it right.
In your example, let's say you want to optimistically put a new card in a particular column, and that a card can only be in one column (one column to many cards).
The cards in your CardStore might look like this:
_cards: {
'CARD_1': {
id: 'CARD_1',
columnID: 'COLUMN_3',
title: 'Go to sleep',
text: 'Be healthy and go to sleep on time.',
},
'CARD_2': {
id: 'CARD_2',
columnID: 'COLUMN_3',
title: 'Eat green vegetables',
text: 'They taste better with onions.',
},
}
Note that I can refer to a card by the id, and I can also retrieve the id within the object. This allows me to have methods like getCard(id) and also be able to retrieve the id of a particular card within the view layer. Thus I can have a method deleteCard(id) that is called in response to an action, because I know the id in the view.
Within the card store, you would have getCardsByColumn(columnID), which would be a simple map over the card objects, and this would produce an array of cards that you could use to render the contents of the column.
Regarding the mechanics of optimistic updates, and how the use of ids affects it:
You can use a client-side id that is established within the same closure that will handle the XHR response, and clear the client-side id when the response comes back as successful, or instead roll back on error. The closure allows you to hold on to the client-side id until the response comes back.
Many people will create a WebAPIUtils module that will contain all the methods related to the closure retaining the client-side id and the request/response. The action creator (or the store) can call this WebAPIUtils module to initiate the request.
So you have three actions:
initiate request
handle success
handle response
In response to the action that initiates the request, your store receives the client-side id and creates the record.
In response to success/error, your store again receives the client-side id and either modifies the record to be a confirmed record with a real id, or instead rolls back the record. You would also want to create a good UX around that error, like letting your user try again.
Example code:
// Within MyAppActions
cardAdded: function(columnID, title, text) {
var clientID = this.createUUID();
MyDispatcher.dispatch({
type: MyAppActions.types.CARD_ADDED,
id: clientID,
columnID: columnID,
title: title,
text: text,
});
WebAPIUtils.getRequestFunction(clientID, "http://example.com", {
columnID: columnID,
title: title,
text: text,
})();
},
// Within WebAPIUtils
getRequestFunction: function(clientID, uri, data) {
var xhrOptions = {
uri: uri,
data: data,
success: function(response) {
MyAppActions.requestSucceeded(clientID, response);
},
error: function(error) {
MyAppActions.requestErrored(clientID, error);
},
};
return function() {
post(xhrOptions);
};
},
// Within CardStore
switch (action.type) {
case MyAppActions.types.CARD_ADDED:
this._cards[action.id] = {
id: action.id,
title: action.title,
text: action.text,
columnID: action.columnID,
});
this._emitChange();
break;
case MyAppActions.types.REQUEST_SUCCEEDED:
var tempCard = this._cards[action.clientID];
this._cards[action.id] = {
id: action.id,
columnID: tempCard.columnID,
title: tempCard.title,
text: tempCard.text,
});
delete this._cards[action.clientID];
break;
case MyAppActions.types.REQUEST_ERRORED:
// ...
}
Please don't get too caught up on the details of the names and the specifics of this implementation (there are probably typos or other errors). This is just example code to explain the pattern.

AngularJS load data from server

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

getting Backbone collection size?

I've a Backbone Collection
var users = Backbone.Collection.extend({
url: 'https://localhost:2222/users',
model: User
});
In Controller I need to get no of users.
var usersList = new Users();
usersList .fetch({
beforeSend: CommonUtils.sendAuthentication,
data: $.param({ group: group.id})
});
I tried usersList.length it returns 0 but console shows some data in 'usersList'
I logged
console.log(usersList);
console.log(usersList.length);
in 'child' object length shows as 0 but inside that length is 4.
Are you sure you are counting the Length after the fetch has come in? Remember that fetching takes some time and your javascript will not wait for it.
By default Backbone .fetch() makes asynchronous HTTP (Ajax) request which does not finish right away, that is why you have to use success and error callback functions which will be fired accordingly after .fetch() returns a response from the server.
Both success and error receives fetched collection, server response and option objects.
Simple example:
usersList.fetch({
success: function(collection, response, options){
console.log(collection.length);
// Method which would use your fetched colletion
myFancyMethodUsingCollection(collection);
},
error: function(collection, response, options){
}
});
To expand, Backbone collection.fetch([options]) is based on Backbone.sync(method, model, [options]) which is based on jQuery.ajax which by default performs asynchronous requests.
Behavior of .fetch() can be modified by passing options to the method.
Options may contain settings for Backbone (for ex.: silent fetch which does not fire collection changed event and so on.) or jQuery.ajax settings.
Note that using async: false option is discouraged by jQuery.
Synchronous requests may temporarily lock the browser, disabling any
actions while the request is active. As of jQuery 1.8, the use of
async: false with jqXHR ($.Deferred) is deprecated; you must use the
success/error/complete callback options instead.
Backbone.Collection.fetch()
Backbone.sync()
jQuery.ajax()

backbone.js bulk update

So I have a page in which a user is selecting from a list of available options via checkboxes.
I have 3 collections: One for possible list of options, one for the currently saved options, and a clone of the saved options. I am using the clone list to add/remove options as they click on checkboxes.
var possibleOptions = new Backbone.Collection("/options");
var currentUserOptions = new Backbone.Collection("/useroptions", { id: 2 });
var clonedUserOptions = new Backbone.Collection(currentUserOptions.toJSON());
The reason this approach was taken is that a user can cancel out of the options page mid edit so want the options persisted upon clicking a save button. The clonedOptions are updated correctly as options are checked/unchecked. However, when I try to update the real list nothing seems to happen.
currentUserOptions.update(clonedUserOptions.toJSON());
My expectation was that backbone would trigger post request for the new models and deletes for each missing model according to the docs (http://documentcloud.github.com/backbone/#Collection-update). Please let me know if I am misunderstanding how this is suppose to work. A simple working example of the right approach would be greatly appreciated.
Thanks,
CF
It is my understand that you have to update the server with individual model calls when the collection changes (as people have mentioned). There are events to cover these changes. However, you can send the entire collection using sync. When each model is added or removed you can flag it as new and deleted and when you are ready to save send the entire collection. Your server methods will have to take care of determining the appropriate add or delete operation then when the collection is sent.
Here is an example of doing a bulk sync. Instead of using this.collection.remove() you may just want to flag as deleted as I mentioned. This is pretty much the only way the server would know what to delete. Technically you could delete everything and then just add what is sent in the bulk update :) Also you might have to do another get or return a collection from the server when you save to update what has actually been removed. I am not sure this helps in your case, but I have used it for pages that "save when done" one button concept.
var one = new model({ id: this.collection.length + 1});
var two = new model({ id: this.collection.length + 2 });
var three = new model({ id: this.collection.length + 3 });
var four = new model({ id: this.collection.length + 4 });
var five = new model({ id: this.collection.length + 5 });
this.collection.add(one.toJSON());
this.collection.add(two.toJSON());
this.collection.add(three.toJSON());
this.collection.add(four.toJSON());
this.collection.add(five.toJSON());
this.collection.sync('update', this.collection, { success: function () { console.log(this.collection.length); } });
this.collection.remove(one);
this.collection.remove(two);
this.collection.sync('update', this.collection, { success: function () { console.log(this.collection.length); } });
And in your case:
currentUserOptions.sync('update', currentUserOptions, { success: function () { //do something } });

Resources