Backbone collection/model best practice - backbone.js

In my application we are using RequireJs and Backbone
So a typical model might look like the following in a separate file so we can attempt to modularize this application better:
define([
'Require statements here if needed'
],
function() {
var toDo = Backbone.Model.extend({
// Model Service Url
url: function () {
var base = 'apps/dashboard/todo';
return (this.isNew()) ? base : base + "/" + this.id;
},
// Other functions here
});
return toDo;
});
Right now we keep each model and collection in its own file and return the Model/Collection as above. The bigger the application gets the harder it is to keep the files and naming convention straight. I would like to combine similar collections/models together into 1 file and maintain the modularity.
What is a good way to achieve this? Or should I stick with them in separate files and get a better naming convention? If so, what do you use for your naming convention between similar Collections/Models?

This is the way I structure my application :
I have a javascript path, which I'm minifying on demand by the server when client access "/javascript", so I have only one script line in my index.html :
<script src='/javascript'></script>
My directory structure of /javascript is the following :
application.js
router.js
lib/
lib/jquery.js
lib/underscore.js
lib/backbone.js
lib/require.js
lib/other_libraries.js
views/
views/navigation.js
views/overlay.js
views/content.js
views/page
views/page/constructor.js
views/page/elements
views/page/elements/constructor.js
views/page/elements/table.js
views/page/elements/form.js
views/page/elements/actions.js
collections/
collections/paginated.js
All those files are minified and loaded from client in the first request. By doing this I have a lot of my code already loaded in my browser before the application makes any requests with RequireJS.
On my server I have a directory, which is also public, but is for dynamic javascript loading and templates ( it is accessed by demand from the application at any given time ). The directory looks like this :
dynamic/
dynamic/javascript
dynamic/javascript/pages
dynamic/javascript/pages/articles.js
dynamic/templates
dynamic/templates/pages
dynamic/templates/pages/articles.hb
dynamic/templates/pages/items.hb
When my server requests "/templates/pages/articles.hb" the server returns JSON object which looks like this :
{ html : "<div class='page' id='articles'>blah blah blah</div>", javascript : "javascript/pages/articles.js" }
And when my client app receives "javascript" property in the returned JSON object it triggers a RequireJS request
if ( typeof json.javascript === string ) {
require([ json.javascript ], function(constructor) {
window.app.page = new constructor();
});
}
In the dynamic/javascript/pages/articles.js I have something like :
define(function() {
var Model = Backbone.Model.extend({});
// Collections.Paginated is in fact the Collection defined by /javascript/collection/paginated.js
// it is already loaded via the initial javascript loading
var Collection = Collections.Paginated.extend({
url : '/api/articles'
});
// Views.Page is in fact the View defined by /javascript/views/page/constructor.js
// it is also already loaded via the initial javascript loading
var articles = Views.Page.extend({
collection : Collection,
initialize: function(options) {
this.collection = new Collection();
});
});
return articles;
});
Pretty much that's it. I have minimum requests with RequireJS, because every time you hit require('something.js') it makes a request to the server, which is not good for your application speed.
So the exact answer of your question ( in my opinion ) is : You should make your initial loaded files separated as much as possible, but later loaded files with requireJS should be as small as possible to save traffic.

Related

How can I de-cache AngularJS templates when they change on the server?

We have an Angular project where the templates have changed numerous times thanks to our "Agile" environment. Browsers seem to strongly cache the templates because of the html file type. This means that when business goes to our dev site after an update, they occasionally see the old templates. How can we make sure that when changes are made to the templates, the user downloads the new template instead of loading from the cache?
We use Jade and to prevent caching, we have a variable based on the time that gets appended to the end of our JS/CSS includes (style.css?v=2012881). Since we already have an 'appVersion' via this variable, I chose to expose that variable using an angular module and constant:
script.
angular.module('appVersion',[]).constant('appVersion',#{curDate});
In my main Angular module I have:
.config(['$httpProvider','appVersion',function($httpProvider,appVersion){
$httpProvider.interceptors.push(function() {
return {
'request': function(config) {
if(!config.cached && config.url.indexOf('.html') > -1){
if(config.url.indexOf("?") > -1){
config.url = config.url.replace("?","?v="+appVersion+"&");
}
else{
config.url += "?v="+appVersion;
}
}
return config;
}
};
});
}])
Since the templates are loaded using $http.get, I added an interceptor that detects if a request is a request for a template and appends the appVersion to the request if it is. That way we have the same versioning for the CSS, JS, and HTML.
Use tools like grunt-filerev (https://github.com/yeoman/grunt-filerev) for static revisioning. They basically add a file content hash, so that caching becomes impossible.

AngularJS best practice REST / CRUD

What is the best practice of doing CRUD operations via REST with AngularJS?
Specially what is the Angular-Way here. By this I mean the way using the least code and the most default angular settings to achive this.
I know $resource and it's default operations. Where I'm not sure is how to implement/name the endpoints and which controllers to use.
For this example I would like to implement a simple user-management system which creates / updates /deletes / lists users. Since I'm implementing the Server-Endpoints by myself I'm completely free in doing it in the most angular friendly way.
What I like as answer is something like:
Server-Endpoints:
GET /service/users -> array of users
GET /service/user/new -> return an empty user with default values which has no id
POST /service/user/new -> store a new user and create an id. return the saved user.
POST /service/user/:ID -> save an existing user. Return the saved user
DELETE /service/user/:ID -> delete an existing user
Angular-Services:
.factory( 'User', [ '$resource', function( $resource ){
return $resource( '/service/user/:userId', { userId: '#id' } )
[...]
}])
Routing:
.when( '/users', {
templateUrl: BASE + 'partials/user-list.html',
controller: 'UserListCtrl' } )
.when( '/user/new', {
templateUrl: BASE + 'partials/user-edit.html',
controller: 'UserNewCtrl' } )
.when( '/user/:userId', {
templateUrl: BASE + 'partials/user-edit.html',
controller: 'UserEditCtrl' } )
...
Controllers:
UserListCtrl:
$scope.data = User.get(...)
UserNewCtrl:
$scope.user = User.get( { userId: "new" } )
...
Note that I'm not interessted in opinion what is the best (tm) way to do this but I'd like to know what is the Angular intended way (which I think should produce the least code because it can use the most default).
EDIT:
I'm looking for the whole picture. What I would love would be an answer like e.g.: "You can do this using online 3 Endpoints [...], 2 routes [...] and 2 controllers [...] if you do it this way using that defaults ..."
There is no Angular prescribed way for what you are asking. It's up to you to determine the implementation detail.
Typically I only use two controllers and templates per resource:
ListController
FormController
The Form controller is used for both Edit and Create operations. Use the resolve option in your route definitions to pass in either User.get() or User.new() and a flag indicating if this is an edit or create operation. This flag can then be used inside your FormController to decide which save method to call. Here's a simple example:
.when( '/users', {
templateUrl: BASE + 'partials/user-list.html',
controller: 'UserListCtrl' } )
.when( '/user/new', {
templateUrl: BASE + 'partials/user-form.html',
resolve: {
data: ['User', function(User) { return User.new(); }],
operation: 'create'
}
controller: 'UserFormCtrl' } )
.when( '/user/:userId', {
templateUrl: BASE + 'partials/user-form.html',
resolve: {
data: ['User', '$route', function(User, $route) { return User.get($route.current.params.userId); }],
operation: 'edit'
}
controller: 'UserFormCtrl' } )
And your form controller:
app.controller('UserFormCtrl', ['$scope', 'data', 'operation', function($scope, data, operation){
$scope.data = data;
$scope.save = function() {
if (operation === 'edit') {
// Do you edit save stuff
} else {
// Do you create save stuff
}
}
}]);
You can go a step further and create a base list and form controller to move stuff like error handling, server-side validation notifications etc. In fact for the majority of CRUD operations you can even move the save logic to this base controller.
My research into a similar quest has lead me to this project "angular-schema-form" https://github.com/Textalk/angular-schema-form.
For this approach...
You make a JSON-Schema that describes your data. Then augment it with another little JSON-struct that describes a "form" (ie. view specific info that does not belong in the data schema) and it makes a UI (form) for you.
One cool advantage is that the Schema is also useful in validating the data (client and server side), so that is a bonus.
You have to figure out what events should fire off GET/POST/... back to your API. but that would be your preference, eg. Hit the API for every key stroke OR the classic [Submit] button POST back style OR something in between with a timed Auto Save.
To keep this idea going, I think that it is possible to use StrongLoop to make a quick API, which (again) uses your data's schema (augmented with some storage details) to define the API.
no <3 uses of that schema, see [http://json-schema.org/] which is central to this approach.
(read: no less than three :)
You maybe mixing things up. CRUD operations at API level are done using $resource and these may or may not map to UI.
So using $resouce if you define resource as
var r = $resource('/users/:id',null, {'update': { method:'PUT' }});
r.query() //does GET on /users and gets all users
r.get({id:1}) // does GET on /users/1 and gets a specific user
r.save(userObject) // does a POST to /users to save the user
r.update({ id:1 }, userObject) // Not defined by default but does PUT to /users/1 with user object.
As you see the API is resource full but is in no way linked to any UI view.
For view you can use the convention you have defined, but nothing specific is provided by Angular.
I think what you are looking for can be found in http://www.synthjs.com/
Easily create new RESTful API resources by just creating folders and
naming functions a certain way.
Return data or promises from these
functions and they'll be rendered to the client as JSON.
Throw an
error, and it'll be logged. If running in dev mode, the error will
also be returned to the client.
Preload angular model data on page load (saving an extra
roundtrip).
Preload html view on page load (saving another extra
roundtrip!)
A simplified project structure
where front-end code (angular code, html, css, bower packages, etc)
is in the 'front' folder and back-end code (node code and node
packages) are in the 'back' folder.
A command-line tool for
installing third party packages, using npm + bower, that auto-updates
manifest files.
Auto compilation of assets on request for dev, and
pre-compilation for prod (including minification and ngmin).
Auto-restarts the server when changes are detected.
Support for
various back-end and front-end templates to help get a new project
going quickly.

Loading relative templateUrl

I've been trying to find the best way to create a modular, scalable angular application. I really like the structure of projects like angular-boilerplate, angular-app, where all the related files are grouped together by feature for partials and directives.
project
|-- partial
| |-- partial.js
| |-- partial.html
| |-- partial.css
| |-- partial.spec.js
However, in all these examples, the template URL is loaded relative to the base url, not relative to the current file:
angular.module('account', [])
.config(function($stateProvider) {
$stateProvider.state('account', {
url: '/account',
templateUrl: 'main/account/account.tpl.html', // this is not very modular
controller: 'AccountCtrl',
});
})
This is not very modular, and could become difficult to maintain in large projects. I would need to remember to change the templateUrl path every time I moved any of these modules. It would be nice if there was some way to load the template relative to the current file like:
templateUrl: './account.tpl.html'
Is there any way to do something like this in angular?
The best way to do this now is using a module loader like browserify, webpack, or typescript. There are plenty of others as well. Since requires can be made from the relative location of the file, and the added bonus of being able to import templates via transforms or loaders like partialify, you don't even have to use template urls anymore. Just simply inline the Template via a require.
My old answered is still available below:
I wrote a post on exactly this subject and spoke on it at our local Angular Meetup. Most of us are now using it in production.
It is quite simple as long as your file structure is represented effectively in your modules. Here is a quick preview of the solution. Full article link follows.
var module = angular.module('myApp.things', []);
var all = angular.module('myApp.things.all', [
'myApp.things',
'things.someThing',
'things.someOtherThing',
'things.someOtherOtherThing',
]);
module.paths = {
root: '/path/to/this/thing/',
partials: '/path/to/this/thing/partials/',
sub: '/path/to/this/thing/sub/',
};
module.constant('THINGS_ROOT', module.paths.root);
module.constant('THINGS_PARTIALS', module.paths.partials);
module.constant('THINGS_SUB', module.paths.sub);
module.config(function(stateHelperProvider, THINGS_PARTIALS) {
stateHelperProvider.setNestedState({
name: 'things',
url: '/things',
templateUrl: THINGS_PARTIALS + 'things.html',
});
});
And then any sub modules or "relative" modules look like this:
var module = angular.module('things.someThing', ['myApp.things']);
var parent = angular.module('myApp.things');
module.paths = {
root: parent.paths.sub + '/someThing/',
sub: parent.paths.sub + '/someThing/sub/',
partials: parent.paths.sub + '/someThing/module/partials/',
};
module.constant('SOMETHING_ROOT', module.paths.root);
module.constant('SOMETHING_PARTIALS', module.paths.partials);
module.constant('SOMETHING_SUB', module.paths.sub);
module.config(function(stateHelperProvider, SOMETHING_PARTIALS) {
stateHelperProvider.setNestedState({
name: 'things.someThing',
url: "/someThing",
templateUrl: SOMETHING_PARTIALS + 'someThing.html',
});
});
Hope this helps!
Full Article: Relative AngularJS Modules
Cheers!
I think you'll eventually find that maintaining the paths relative to the js file will be harder, if even possible. When it comes time to ship, you are most likely going to want to concatenate all of your javascript files to one file, in which case you are going to want the templates to be relative to the baseUrl. Also, if you are fetching the templates via ajax, which Angular does by default unless you pre-package them in the $templateCache, you are definitely going to want them relative to the baseUrl, so the server knows where to find them once your js file has already been sent to the browser.
Perhaps the reason that you don't like having them relative to the baseUrl in development is because you aren't running a server locally? If that's the case, I would change that. It will make your life much easier, especially if you are going to work with routes. I would check out Grunt, it has a very simple server that you can run locally to mimic a production setup called Grunt Connect. You could also checkout a project like Yeoman, which provides a pre-packaged front end development environment using Grunt, so you don't have to spend a lot of time getting setup. The Angular Seed project is a good example of a Grunt setup for local development as well.
I've been chewing on this issue for a while now. I use gulp to package up my templates for production, but I was struggling to find a solution that I was happy with for development.
This is where I ended up. The snippet below allows any url to be rewired as you see fit.
angular.module('rm').config(function($httpProvider) {
//this map can be defined however you like
//one option is to loop through script tags and create it automatically
var templateMap = {
"relative.tpl.html":"/full/path/to/relative.tpl.html",
etc...
};
//register an http interceptor to transform your template urls
$httpProvider.interceptors.push(function () {
return {
'request': function (config) {
var url = config.url;
config.url = templateMap[url] || url;
return config;
}
};
});
});
Currenly it is possible to do what you want using systemJs modules loader.
import usersTemplate from './users.tpl';
var directive = {
templateUrl: usersTemplate.name
}
You can check good example here https://github.com/swimlane-contrib/angular1-systemjs-seed
I had been using templateUrl: './templateFile.tpl.html but updated something and it broke. So I threw this in there.
I've been using this in my Gruntfile.js html2js object:
html2js: {
/**
* These are the templates from `src/app`.
*/
app: {
options: {
base: '<%= conf.app %>',
rename: function( templateName ) {
return templateName.substr( templateName.lastIndexOf('/') );
}
},
src: [ '<%= conf.app %>/**/{,*/}<%= conf.templatePattern %>' ],
dest: '.tmp/templates/templates-app.js'
}
}
I know this can lead to conflicts, but that is a smaller problem to me than having to edit /path/to/modules/widgets/wigdetName/widgetTemplate.tpl.html in every file, every time, I include it in another project.

Service or provider with cached data

On a server side I have a json file in a dictionary form:
{
"term1": "definition1",
"term2": "definition2",
"term3": "definition3"
}
I'm trying to create a service or provider (one of them is sufficient) which will have a cache of a data from this json file and will be able to use it.
The structures look like:
myApp.service('translateSrv', function() {
this.dictionaryData; // how to populate
this.translate = function(input) {
return this.dictionaryData[input];
};
});
myApp.provider('translateProvider', function() {
this.dictionaryData; // how to populate
this.$get = function() {
return {
translate: function() {
return this.dictionaryData[input];
}
}
};
});
My question is how to populate a dictionary data in this service or provider before the first call of translate() method (in a time of module creation/configuration)? I can't do it asynchronously while first method call.
I want to use one of this structure, among others, in a filter:
myApp.filter('translate', ['translateProvider', function(translateProvider) {
return function(input) {
return translateProvider.translate(input);
}
}]);
I've started recently my work with Angular so maybe my approach is wrong. I will appreciate every hint.
Provider name:
Do not suffix your provider name with 'Provider' since the name you will use to inject the provider in config functions will already be suffixed with 'Provider'
myApp.provider('translate', /*...*/);
// -> injectable provider is 'translateProvider'
// -> injectable instance is 'translate'
Populate the provider in a config function:
myApp.config(['translateProvider', function(translateProvider) {
translateProvider.dictionaryData = { /*...*/ };
});
Advice for performance!
If your translations are static per page view, please consider pre translating your templates.
If you really need it, prefer writing the whole translation js object in an inline script in the document
1 XHR less
no lazy loading deferring application load
Lazy loading
If you really have to lazy load those translations:
Either defer application loading with an external XHR before application load, while keeping the translationData at the provider configuration level,
Or take advantage of the "resolve" part of angular rooting or ui-router, while setting the translationData object on the instance (not on the provider)
How to choose between both?
The first option is quite easy and you won't have to couple application routing with your lazy load constraint.
For the second choice, prefer ui-router and declare an abstract state responsible for handling the lazy loaded data in the "resolve" state property and make other states be children of this abstract state, so that you won't have to add resolve constraints on each state that have a dependency on translations.

Using a Backbone Collection without a data source (no url)

First time posting here... looking forward to see how it all works, but have really appreciated reading other's questions & answers.
I am using backbone for a small app and have found it helpful to use a collection to store some information that is only required during the current session. (I have a number of collections and all the others connect to my API to store/retrieve data).
I read here (in backbone.js can a Model be without any url?) that it is possible, and even good to use a collection without providing a url.
Now I would like to add a row of data to the collection... simple:
myCollection.create(data);
but of course that now throws an error:
Uncaught Error: A "url" property or function must be specified
Is there any way to use a Backbone collection, be able to add new rows of data (models) to it, but not sync to any sort of data source. Or can you suggest another solution.
I guess I could just use an object to hold the data, but I was enjoying the consistency and functionality.
I am using Backbone.Marionette if that has any impact.
Thanks in advance.
One thing you could do is override the Backbone.Model methods that communicate with the server, i.e. sync, fetch, and save... for example:
var App = {
Models: {},
Collections: {}
};
App.Models.NoUrlModel = Backbone.Model.extend({});
App.Models.NoUrlModel.prototype.sync = function() { return null; };
App.Models.NoUrlModel.prototype.fetch = function() { return null; };
App.Models.NoUrlModel.prototype.save = function() { return null; };
App.Collections.NoUrlModels = Backbone.Collection.extend({
model: App.Models.NoUrlModel,
initialize: function(){}
});
var noUrlModels = new App.Collections.NoUrlModels();
noUrlModels.create({'foo': 'bar'}); // no error
// noUrlModels.models[0].attributes == {'foo': 'bar'};
See Demo

Resources