What is the AngularJS equivalent to Backbone's Collections? - javascript

Is there an equivalent to Backbone's Collection or Ext JS's Store in Angular JS? I'm learning about $resource, but not quite getting this aspect.
Controller
// This is the "collection" I'm interested in.
$scope.foos = [];
// Foo is a $resource.
Foo.query(function (foos) {
// This works, but is there a smarter "collection" object?
$scope.foos = foos;
});
$scope.createFoo = function (data) {
var foo = new Foo(data);
foo.$save(function (shinyNewFoo) {
$scope.foos.unshift(shinyNewFoo);
});
};
Explanation
In $scope.createFoo I'm making a new Foo, persisting it, then adding it to my poor man's collection, an array. This works and the view is updated correctly, but I'm too lazy for this. Ideally, I'd like to have a "collection" I can add to and remove from that will automatically POST/DELETE. Maybe something like the following.
Pretend Implementation
$scope.fooCollection = new CoolCollection({
// specify the type of resources this collection will contain
itemsResource: Foo
});
// Creates new Foo, saves, adds to collection.
$scope.fooCollection.add(data);
Does something like this exist? The $resource docs mention a collection, but I didn't really get it. This answer seemed promising, but didn't explain the collection idea. Am I missing something or just not thinking about this the Angular way?
Addendum
In the MEAN.io boilerplate, the article controller suggests that the "collection" is managed manually (see the splice below).
$scope.remove = function(article) {
if (article) {
article.$remove();
for (var i in $scope.articles) {
if ($scope.articles[i] === article) {
$scope.articles.splice(i, 1);
}
}
} else {
$scope.article.$remove();
$location.path('articles');
}
};
If this elusive "collection" existed, I suppose they would have used it. Instead, they're managing an array manually.

AngularJS does not define any kind of structured data model. This part is entirely up to you.
The $resource service is just a wrapper on top of the lower-level $http service; it defines a high level way to fetch your data from the server and has little to do with how you structure your data on the frontend.
If a more sophisticated frontend data model is required by your application, you should investigate other JS libraries or roll your own. However, angular's singleton services and powerful two-way data binding make this unnecessary for most small/medium applications.

Related

What is the best practice for using $resource and sharing the results across multiple controllers?

So, I've looked all over the place and can't find the answer I'm after.
I've recently learned a few new things and switched from having my services get and store shared data like this:
angular.module('myModule')
.service('stuffService', function(){
var thisService = {
stuff: {},
getSomething: function(){
var onSuccess = function(response){
thisService.stuff = response.data;
};
var onError = function(response){
alert('Doh!');
};
var promise = $http.get(<url>).then(onSuccess, onError);
return promise;
}
};
return thisService;
});
angular.module('myModule')
.controller('myController',function($scope, stuffService){
$scope.stuff = stuffService.stuff;
});
to this:
angular.module('myModule')
.factory('stuffService', function(){
var stuffResource = $resource(<stuff_url>, <params>, <etc>);
return {
stuff: stuffResource
}
});
angular.module('myModule')
.controller('myController', function($scope, stuffService){
$scope.stuff = stuffService.stuff.get(...);
});
This has been a really beneficial change in terms of most of the operation of my application; however, I'm running into a pickle as far as how would I share the data that the stuffResource returns.
Previously, I could just always reference stuffService.stuff from any controller that had stuffService injected into it. But now with $resource, I'm not sure how I would store this data. Using $scope.stuff or even $scope.stuff.get() would always run a query, and not act like an actual collection of "stuff".
At first, I tried to have a parent controller that did $scope.stuff = myService.stuff.get(...) and that actually worked really well... until I needed that same stuff outside of the parent controller. Now I need to figure out a way to store that data so that I'm not making the same API call over and over consecutively as the app loads up.
I've thought about turning on caching, which seems to be a pretty intuitive (duh!) approach but I'm not sure if that's the best course of action. Some data needs to be refreshed more often then others, so it would have to be caching with a cache limit of varying size/length and I'd be worried that some components would have different/newer data then components that loaded earlier. Maybe it feels like too simple of a solution, not sure. It would be nice if this was the answer, and I just used `stuffService.stuff.get()' just as if it was a stored object from the olden days.
I've also thought about using an interceptor to store this data similar to what my onSuccess method was doing previously. This seems ok too, but feels a little dirty/hacky. I would just need a little push to get this one working if I was on to a good idea.
My latest thought was to create a sort of "data service", that has the other services (like stuffService) injected into it. This "dataService" would have an object that it loads up with data from all these other services and then can be shared as dataService.data.stuff. This is nice in that my data can be a single source that's shared throughout the app, but it's also yet another service that needs to be injected because I'll probably still need other methods from stuffService. This could work better if I had one stuffService with nonCRUD methods for stuff, and another crudStuffService which was basically a wrapper for $resource. I'd really prefer to have anything that related to stuff under the same service. When I was doing that before, it was really helpful to have things abstracted out like stuffService.getStuff() to make http requests or stuffService.findStuffWithSpecialConditions() to do loops and conditions, etc. on stored/shared data.
So, anyone got any ideas on how to get and store data using $resource within the same service? Is this how it should be done? Am I missing something?
Thanks!
For what you are looking is – flyweight patterrn
The most important part of its implementations will be:
.factory('stuffService', function(){
var cache = {};
var stuffResource = function(<stuff_url>, <params>, <etc>) {
if(!cache[<stuff_url>+<params>+<etc>]) {
cache[<stuff_url>+<params>+<etc>] = $resource(<stuff_url>, <params>, <etc>);
}
return cache[<stuff_url>+<params>+<etc>];
}
return {
stuff: stuffResource
}
});

Is this architecture still valid under Angular 1.2 and above

There are many code examples using $resource out there. I came across this one, and the code is quite clearly laid out: https://github.com/apotry/stockwatch
I like this example because:
It interacts with a Rails backend, which is how I am using Angular
It uses $resource
It is using a non-rest route (ohlc)
The code is quite clean
To call the functions defined is quite simple as shown in the controller code below, but is it a good idea to embed the save in the factory?
My question is: now that Angular 1.2+ includes promises, is this type of code still valid and considered a good practice? How would this code respond to an error state?
Here the resources are defined
app.factory('Stock', ['$resource', function($resource) {
function Stock() {
this.service = $resource('/api/stocks/:stockId', {stockId: '#id'}, {'update': { method: 'PUT' }});
};
Stock.prototype.all = function() {
return this.service.query();
};
Stock.prototype.delete = function(stId) {
return this.service.remove({stockId: stId});
};
Stock.prototype.create = function(attr) {
return this.service.save(attr);
};
Stock.prototype.update = function(attr) {
return this.service.update(attr);
};
Stock.prototype.ohlc = function(stId) {
return $resource('/api/stocks/:stockId/ohlc', {stockId: '#id'}).get({stockId: stId});
}
return new Stock;
}]);
Here is an example of the delete, create and a custom route (ohlc):
$scope.requestOHLC = function (stockid) {
return Stock.ohlc(stockid);
}
$scope.createStock = function() {
$scope.fetchYahooFinanceData($filter('uppercase') ($scope.newCompany["symbol"])).then(function(result) {
$scope.error = false;
$scope.stocks.push(Stock.create(result));
$scope.newCompany = '';
}, function(error) {
$scope.error = true;
});
};
$scope.deleteStock = function(id, idx) {
Stock.delete(id);
$scope.stocks.splice(idx, 1);
};
EDIT
I am trying to work out a simple and clear practice for using $resource based rest routes in angular.
Different from the above code, but based on it. Assume the code below uses a service which is basically the same as the factory above. In this example I call one REST resource to create a new resource (rails table entry), and then pass the newly created id to another call. Note that createPanelHeader references the $scope.selector.paneldata.primer3_parameter_id scope variable. Not sure if this is a good practice either.
I found this would not work unless I uses $promise.then but this seemed a bit convoluted. Am I on the right track?
// Section: Create Panel header
createPrimer3Parameter = function() {
primer3_parameter = Primer3Parameter.create().$promise.then(function(primer3_parameter){
$scope.selector.paneldata.primer3_parameter_id = primer3_parameter.id;
createPanelHeader();
}, function() {
alert('Error creating primer3parameter');
})
};
COMMENT
I am really just trying to work out a simple method for accessing REST resources from a Rails API, with at most 1 level of nesting. I think I am missing something as it seems remarkably difficult.
What I am hearing so far is not to use $resource, even under 1.2. That I should instead use raw $http or Restangular.
Also, there seem to be some 1.2 changes that affect Restangular. The solution feels a bit like a hack to me:
https://github.com/mgonto/restangular#using-values-directly-in-templates
UPDATE
I didn't really come away 100% clear, so I have posted a Bounty: https://bountify.co/write-an-angular-service-for-these-rails-routes-using-restangular
is this type of code still valid and considered a good practice?
This code is valid, but considered deprecated as of 1.2.0-rc3. It will work in all version of angular 1.2 and 1.3 up to but not including 1.3.0-beta10, where automatic promise unwrapping has been removed.
$scope.stocks.push(Stock.create(result));
In the line above you've created an array of promise objects on $scope. Then in index.html.erb you are referencing the promise directly via the stock iterator:
<li ng-repeat="stock in stocks">
<div id='symbol'>
{{stock.symbol}}
</div>
The non-deprecated way of handling promises does not allow you to bind directly to the promise like this.
How would this code respond to an error state?
The app is handling errors here:
}, function(error) {
$scope.error = true;
});
and here:
<div ng-show="error">
There was a problem with the Yahoo Finance API. Please try again later.
</div>
For error handling, you're not binding with a promise directly so this works fine in all versions of angular.
A better way?
First, shelf the javascript directory structure in the stockwatch example. Then, follow this directory structure instead. Finally, integrate Restangular into your project. Create a stockModel factory that internally instantiates a Restangular object, but returns the object that will be populated later after the promise resolves (model). Instead of binding a promise in your partial, bind the unpopulated result object.
.factory('stocksModel', function (Restangular) {
var model = {};
var rest_stocks = Restangular.all('stocks');
model.doSomethingRESTful = function (...) {
// return a promise in case the controller needs it
return rest_carts.some_restangular_method(...)
.then(function() {
model.some_data_which_was_acquired_RESTfully = ...;
});
};
return model;
});
In your controller:
$scope.stocks = stocksModel;
In your partial:
{{stocks.some_data_which_was_acquired_RESTfully}}
To be blunt
No. If possible, I would improve the code so that is it more maintainable and update to date with Angular. Let me explain why it is a good idea. . .
Why are you making your life so hard?
Angular is a wonderful framework that lets you tackle fairly complex problems with simple solutions. The dark side of Angular is it is easy to over engineer a solution if you go off the beaten path. Angular exposes a lot of the internals so it is tempting (and fun!) to muck around with it instead of going for the simple route.
Growing pains
I believe what you are dealing with is due to the growing pains Angular has had as it matured as project. A lot of side projects sprung up to fix the leaky holes that have since been plugged.
Rails and Angular, fast friends
So I am basing this off of how I have grown to use Rails with Angular. The examples are pulled from a pet project that is not going anywhere. Unfortunately, this is all in coffeescript, so hopefully that wont cause you problems. It is structured how I found works best with Rails, while keeping the spirit and fun of Angular.
* app/assets/javascripts
* app
* project_name_app.js.coffee.erb
* controllers
* controllers.js
* directives
* directives.js
* filters
* filters.js
* resources
* resources.js
* services
* services.js
Rails' asset pipeline wraps everything up using the application.js with the include:
//= require app/project_name_app
In the app/project_name_app.js.coffee.erb, it loads all of the directories with the includes
#= require_self
#= require app/controllers/controllers
#= require app/directives/directives
#= require app/filters/filters
#= require app/resources/resources
#= require app/services/services
Last, each of the sub directories js (controllers.js, directives.js, filters.js, resources.js, services.js) simply loads everything in that directory:
//= require_tree .
With this setup, the project_name_app.js.coffee.erb is loaded first, setting up Angular, the dependencies, and the app configuration. Then the controller, directives, filters, etc are loaded. One of the perks is new javascript added to a sub directory is automatically included thanks to the require_tree.
Keeping the $resource simple
The best $resources are RESTFUL. Meaning that it is the sample URL and the functionality changes based on the http method of the request. The downside is, if you have different URLs, you will probably need multiple resources. An example from the users_resource.js.coffee:
angular.module("DeployerApp.resources").factory "Users", ($resource) ->
$resource "/users.json", {},
index:
method: "GET"
isArray: true
type: "User"
angular.module("DeployerApp.resources").factory "User", ($resource) ->
$resource "/users/:user_id.json", {},
show:
method: "GET"
update:
method: "PUT"
angular.module("DeployerApp.resources").factory "CurrentUser", ($resource) ->
$resource "/users/current.json", {},
show:
method: "GET"
To get all users, you call Users.index(). To get a single user, you call User.show( user_id: 1 ). Last, a convenience $resource I that often use, to get the current authenticated user, CurrentUser.show().
Pretty straight forward and easy to read, avoiding have to have thick models. Each of the User $resources can be tested separately.
Angular is ready to do the work
You really only need to start mucking with $promise if you have some complicated juggling act when dealing with multiple responses. I have found it is simpler to just pass in the success and error callbacks to the $resource, for example:
CurrentUser.show success = (user) ->
$scope.currentUser = user
, error = (data,status) ->
# redirect to login
The variable names help make the code more readable, without them the coffeescript function definitions kind of blend together.
Simpler is always better
You do not need to worry about the $promise.then of the $resource, allowing you to tidying things up.

Is it possible to have a mapping for Angular model property names?

I'm currently working on an app whose database schema changes frequently. This rapid change creates a big problem for my front-end Angular code which consumes the backend JSON API (which I don't have much control over) via Restangular; take the following code for example:
<ul>
<li ng-repeat="item in items">
<h2>{{item.label}}</h2>
</li>
</ul>
There will be a lot of template tags like {{item.label}} scattered everywhere in the front-end code, so whenever the property name changes from, say "label" to "item_label", I'll need to remember where those tags are and change all of them. Of course, I could do a project wide search and replace, but that's not really ideal from an DRY stand point and it'll also be a maintenance nightmare.
My question is, does Angular (or Restangular) provide a way to map model property names to custom ones like this in Backbone?
That way, I can just have something like this
{
label: model.item_label
}
then next time when the "item_label" is changed to something else, I can just update it in this configuration object and not worry about all the references in the templates.
Thanks.
The idea with angular is that you can do whatever you want with the model. While this doesn't point you in any specific direction it does give you the opportunity to implement it in your own OO manner. Say you have an app that has a data object called ...Task a model for tasks might look like..
function Task(initJson){
this.name = initJson._name || 'New Task';
this.completed = initJson.is_completed || false;
this.doneDateTime = initJson.datetime || null;
}
Task.prototype = {
save: function(){
//do stuff with this and $http.put/post...
}
create: function(){
//do stuff with this and $http.put/post
}
//....etc
}
All of this might be wrapped up in a factory.
myApp.factory('TaskFactory',function($http){
var Tasks = []; //Or {};
//above constructor function...
//other helper methods...etc
return {
instance: Task,
collection: Tasks,
init: function(){} // get all tasks? run them through the constructor (Task), populate collection
};
})
You could then edit properties on your constructor (one place (for each data type), the only place). Although this isn't ideal if your using things like Restangular or $resource as they not equipped to be a large backing store but they just assume the properties that come across the wire, which for large, changing applications can sometimes be difficult to manage.
I ended up going with Restangular's setResponseExtractor config property based on this FAQ answer.
It looks like this:
Restangular.setResponseExtractor(function(response, operation, what, url) {
var newResponse = response;
angular.forEach(newResponse.items, function(item) {
item.label = item.item_label;
}
return newResponse;
}

how to model my data is angular js? Does something like Backbone.Model will fit?

I am using angular-js and I am looking for a way to model my data. I am inspired by Backbone and started to write something similar to Backbone model. In Backbone model there are get() and set() methods for accessing model's members.
What is the advantages of using get() and set() methods over using model.attributes.__?
If I don't need my angular models to trigger changes events, does it worth to create get() and set() methods that are similar to Backbone.Model's get() and set()?
EDIT
To be more clear, I am asking if there is advantage to make a set() and get() methods over direct access to the fields. If there is no any advantage, why does Backbone people decided to go that way?
You just need to create a service which would hold the data, you can think of services like instances of models or collections. For example, you have some domain class like Book, what you can do is to create a service 'BookListService' which would be responsible for CRUD and/or business logic.
Something like this
function Book(title, author){
this.title = title;
this.author = author;
this.isRead = false;
}
Book.prototype.markAsRead = function(){
this.isRead = true
}
angular.module('someModule', []).factory('BookListService', function($http){
var service {
books: [],
load: function(){
$http.get('/books').then(function(res){
angular.forEach(res.data, function(book){
service.books.push(new Book(book.title, book.author))
})
})
}
};
return service;
})
Angular's 'models' are any object in javascript.
If you check out the tutorials on angularjs.org,
http://jsfiddle.net/api/post/library/pure/
The data is defined in the Controller. So make an array, just do
$scope.todos = [
{text:'learn angular', done:true},
{text:'build an angular app', done:false}];
To get/set is just normal javascript:
get length: $scope.todos.length;
set to empty: $scope.todos = [];
In the view (html), just call the variable if its under the same Controller
<div ng-controller="TodoCtrl">
<span>{{todos.length}}</span>
</div>
I had some trouble with this at first because Angular seems overwhelming, so I was expecting a big answer for model implementation. The answer is simply to use JS objects. Angular has all of the tools you need to easily create a model yourself without the use of Backbone (which I have also read is not really the way to go with Angular). If you look into $emit, $broadcast, $on, you have your events for model updates. If you want to create instances of the objects rather than singletons, then have your factory return an instance. A good place to start I think is angular-app. Apologies if this isn't specific enough.
If you are looking for a package to use, try BreezeJS.

AngularJS: Creating Objects that map to REST Resources (ORM-Style)

I'm pretty new to AngularJS, but I'm pretty unclear on how to tie it up to my Server's REST Api backend.
For example, say I have an "image" resource that I get by GET-ing: myApi/image/1/. This returns a json object with various fields. Let's say something like:
{url: "some/url", date_created: 1235845}
Now, I want some kind of representation in my AngularJS app of this "Image" object. This representation is more than just a mapping of the fields - I want to add "helper" functions, for example a function that converts the date_create field into something human-readable.
I know about the $resource service, but I'm unclear what I need to do to create a basic "class" in Angular, that uses Resource to get the JSON object, but then enhances it by adding various helper functions.
Bonus points:
I'm also unclear how to add "relationships" between models. For example, I might have a "user" resource that has embedded inside it an "image" resource, and I'll want to grab the User resource, but be able to call "Image" helper functions on the "Image" part of the model.
JSData
A project which started as angular-data is now "a framework-agnostic data store built for ease of use and peace of mind."
It is has excellent documentation and has support for relations, multiple backends (http, localStorage, firebase), validation and of course angular integration.
http://www.js-data.io/
BreezeJS
The AngularJS YouTube channel features this video using BreezeJS
Which is an advanced ORM which even supports client-side filtering and other cool stuff.
It best suited for backend that support OData, but can be made to work on other types of backends.
ngResource
Another option is to use the ngResource, here is an example on how to extend it with your own functions:
module.factory('Task', function ($resource) {
var Task = $resource(WEBROOT + 'api/tasks/:id', {id: '#id'}, {update: { method: 'PUT'}});
angular.extend(Task.prototype, {
anExampleMethod: function () {
return 4;
},
/**
* Backbone-style save() that inserts or updated the record based on the presence of an id.
*/
save: function (values) {
if (values) {
angular.extend(this, values);
}
if (this.id) {
return this.$update();
}
return this.$save();
}
});
return Task;
});
I found ngResource to be very limited, even compared to Backbone.Model which has:
Custom JSON parsing via Model.parse
Possible to extend a BaseModel (No the baseUrl in ngResource)
Other hooks like Backbone.sync, which enables LocalStorage, etc.
Restangular
"AngularJS service to handle Rest API Restful Resources properly and easily"
https://github.com/mgonto/restangular
Or try some of the other ORM's
https://stackoverflow.com/questions/6786307/which-javascript-orm-to-use
I'm creator of Restangular so my opinion can be biased.
But as Bob said, you can use Restangular for it.
Restangular uses your Restful API Resources to go over the tree. You can also add new methods to this.
This is coding example: https://github.com/mgonto/restangular#lets-code
And this way you can add new methods to your object (The bonus points :)) https://github.com/mgonto/restangular#creating-new-restangular-methods
Hope this works out for you :).
Otherwise, you can also use ngResource ($resource) for this but in my opinion, it needs some "love" and "sugar".
Bests
For simple interaction you can use Angular-Resource (http://docs.angularjs.org/api/ngResource.$resource) which can be quite handy for simple REST interaction (to download it go to http://code.angularjs.org/1.0.6/)
Sadly you only get limited control when using angular resource, and for anything more advanced you will need to create your own services based on Angularjs $http service - http://docs.angularjs.org/api/ng.$http.
Hope that helps.
After lots of research, here is a comprehensive list of all the solutions available:
Restmod
Modelizer
ModelCore
angular-watch-resource
angular-restful
ngResource
angularjs-rails-resource
angular-nested-resource
Aar.js
Angular Activerecord
Angular-Data
ngActiveResource
restangular
BreezeJS
ng-backbone
but honestly I wasn't very happy, so I decided to add to the list my own solution haha. Check it out here: $modelFactory.
Your end-result code ends up looking something like:
var module = angular.module('services.zoo', ['modelFactory']);
module.factory('AnimalModel', function($modelFactory){
return $modelFactory('api/zoo');
});
return module;
I believe this is a better solution over the rest because mainly the model definition closely resembles Angular's ngResource, adding just low-level features one needs that it lacks. Its super lightweight (1.45k gzip/min) and has only a few small dependencies ( no lodash, jquery, etc ).
ModelCore ( https://github.com/klederson/ModelCore ) works pretty much like this, and is very very easy to implement:
var ExampleApp = angular.module('ExampleApp', ['ModelCore']); //injecting ModelCore
ExampleApp.factory("Users",function(ModelCore) {
return ModelCore.instance({
$type : "Users", //Define the Object type
$pkField : "idUser", //Define the Object primary key
$settings : {
urls : {
base : "http://myapi.com/users/:idUser",
}
},
$myCustomMethod : function(info) { //yes you can create and apply your own custom methods
console.log(info);
}
});
});
//Controller
function MainCrtl($scope, Users) {
//Setup a model to example a $find() call
$scope.AllUsers = new Users();
//Get All Users from the API
$scope.AllUsers.$find();
//Setup a model to example a $get(id) call
$scope.OneUser = new Users();
//Hey look there are promisses =)
//Get the user with idUser 1 - look at $pkField
$scope.OneUser.$get(1).success(function() {
console.log("Done!",$scope.OneUser.$fetch());
});
Angular Rest-Mod is another good option for Angular-based Models / ORM.
Restmod creates objects that you can use from within Angular to interact with your RESTful API. It also supports collections, relations, lifecycle hooks, attribute renaming and much more.
One more example of helper for ngResource. This relies on fact that the vast majority of services is something like that:
http://host/api/posts
http://host/api/posts/123
http://host/api/posts/123/comments
http://host/api/posts/123/comments/456
So, the task is to make a helper that create AngularJS resource objects that maps on such services. Here it is:
'use strict';
var api = angular.module('api', ['ngResource']);
// RESTful API helper
api.addService = function (serviceNameComponents) {
var serviceName = "";
var resource = "/api"; // Root for REST services
var params = {};
serviceNameComponents.forEach(function (serviceNameComponent) {
serviceName += serviceNameComponent;
var lowerCaseServiceNameComponent = serviceNameComponent.toLowerCase();
var collection = lowerCaseServiceNameComponent + 's';
var id = lowerCaseServiceNameComponent + 'Id';
resource += "/" + collection + "/:" + id;
params[id] = '#' + id;
});
this.factory(serviceName, ['$resource',
function ($resource) {
return $resource(resource, {}, {
query: {
method: 'GET',
params: params,
isArray: true
},
save: {
method: 'POST',
},
update: {
method: 'PUT',
params: params,
},
remove: {
method: 'DELETE',
params: params,
}
}
);
}
]);
}
So, to use it simply call this helper
api.addService(["Post"]);
api.addService(["Post", "Comment"]);
And then you can use Post and PostComment in code with needed params like :post_id

Categories