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
Related
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.
I have an AngularJS application that has a lot of directives which are populated from nested nodes in AJAX requests. For example I might get a response like:
{
name: "Blog post title",
comment_ids: [1, 2, 3]
}
Currently in my controller, I will make a request to load data for each of the child nodes comments and populate them onto the scope. Something like:
_.each(comment_ids, function(id) {
scope.comment_data[id] = $http.get(id); // psuedocode for the $get
});
I then use the data on the page like:
<comment data="comment_data.1"></comment>
This works great but I'm thinking about converting my directives to take an id instead of an object and to handle loading the data themselves. The problem I face is that I might get multiple requests to the same endpoint if the same directive is present on the page multiple times, e.g.
<comment id="1"></comment>
<comment id="1"></comment>
<comment id="1"></comment>
This is going to result in three calls to the comments endpoint. Given this:
Will the browser batch multiple calls to the same HTTP endpoint into one request if it occurs in small space of time, or should I write an interceptor to handle doing it myself?
I would move the $http calls to their own service. The benefit is you can use it in a service or controller as needed and as many times as necessary--this is especially useful if you use any kind of JS-controlled caching.
I'm developing an AngularJS oData service for SharePoint 2010/2013 REST calls that uses localStorage to cache results and only fetches updates from the server. This works especially well when I'm calling the same data over and over, it is just fetched from localStorage without ever making the HTTP call.
// Pseudo code to put in a service
var persistentCache = function (query) {
return localStorage[query] ? {
'then': function (callback) {
callback(JSON.parse(localStorage[query]));
}
} : $http(query).then(function (resp) {
return localStorage[query] = JSON.stringify(resp);
});
};
Alternatively, you could use a module design pattern to cache your calls just for the life of the app and only fetch if it is not in the app cache.
// Pseudo code to put in a service
var appOnlyCache = (function () {
var _cache = {};
return function (query) {
return _cache[query] ? {
'then': function (callback) {
callback(JSON.parse(_cache[query]));
}
} : $http(query).then(function (resp) {
return _cache[query] = JSON.stringify(resp);
});
};
}());
Both examples with more robunst implementation with compression & error handling can be found in the AngularSharepoint service I've working on for an app right now: AngularSharepoint
This problem is now solved quite elegantly with: https://github.com/facebook/dataloader
DataLoader is a generic utility to be used as part of your
application's data fetching layer to provide a consistent API over
various backends and reduce requests to those backends via batching
and caching.
I'm fairly new to AngularJS and I realise this question has been asked before (so hopefully I wont get voted down) but I've been unable to find a question matching my situation.
I want to pass search data (about 4 fields) from a search page (My HomeCtrl) to a ListingCtrl. The listingCtrl already uses a factory/service to get info based on whats passed from an API (I've hardcoded the data at present).
Now everything I read says uses another server/facotry to pass the data between the search page and the listing page but (and forgive me if I'm missing something) why not just pass these additional params as routeParams??
Incase you need to know - I'm using partials and route params to navigate around the app and pass a few simple IDs between pages.
I suggest to use a service.
If you want stock persistant data, i suggest to use this https://github.com/Zmetser/localstorageservice
This module uses localStorage (documentation) (or cookies if you use IE)
Untested example:
The service
myApp.service('controllerSharingData', function() {
var __variables = {};
return {
get: function(varname) {
return (typeof __variables[varname] !== 'undefined') ? __variables[varname] : false;
},
set: function(varname, value) {
__variables[varname] = value;
}
};
});
Controllers
myApp.controller('IndexCtrl', function($scope, controllerSharingData) {
controllerSharingData.set('toto', 'hello world');
});
myApp.controller('ListCtrl', function($scope, controllerSharingData) {
alert(controllerSharingData.get('toto'));
});
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.
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.