Pre loading backbone collections - backbone.js

So what I'm wanting to do is have Backbone fetch all my collections when the router starts, and then keep all the collections and not have to re-fetch and reload all the collections while moving to different routes in the router. Does anyone know a way to do this?

From Backbone.js docs:
Note that fetch should not be used to populate collections on page
load — all models needed at load time should already be bootstrapped
in to place. fetch is intended for lazily-loading models for
interfaces that are not needed immediately: for example, documents
with collections of notes that may be toggled open and closed.
This is what I meant in my comment:
<script>
define("data", function() {
return <?php echo json here ?>;
});
</script>
Then you can have var data = require("data"); and use it to init Backbone models/collections. I'm not sure this is the right way of doing it.

Well, backbone does this by default. Just add code to your router to create an instance of each collection and call fetch() once on each collection. Then make sure the rest of your app just uses those same collection instances and does NOT call fetch() on them again. It's really that simple.
However, I presume you want other bits of your application to be able to call fetch() and have this silently use cached data if needed. This a considered a hard problem to do correctly, but a naive and simple implementation would be to simply store an isCached flag as a property of your collection and check that in your overridden fetch() method and just return without doing anything if your collection data is already loaded.

Related

Backbone best practices where to fetch models/collections

I've inherited a codebase that follows the format of: a router sets a controller, the controller fetches the collection/model needed, then the controller set/passes the view the collection/model.
The current View I'm working on loads a collection, and now I need to build in a feature where I fetch a single model after the view has rendered, based on an id clicked (note the model is from a different collection).
I only want to load the model when/if they click a button. So my question is, can I setup the model/fetch in the View, or should I be doing that in the controller? Is there a backbone best practice when adopting a controller/view setup like this?
I primarily ask because it seems easier for me to add this new feature right in the View. But is that a bad practice? I thought so, so I started down the path of triggering an event in the View for the controller to the fetch the model, and then somehow pass that back to the View (but I'm not sure really how to even do that)...it seems like a lot of unnecessary hoop jumping?
Its OK to fetch collection via views. As 'plain' backbone does not Controller, View in charge of it responsibilities.
But imho fetch collections via controller is better practice, its easier to scale and support and test.
The only difficulty is to set communication between Controller and View context event. One of the approach is trigger Message Bus event on context event like
events: {
'click .some': function() {
this.trigger('someHappend', { some: data })
}
}
and listen to this event in Controller
this.on(someViewInstance, 'someHappend', function() {
// do something in controller
})
If you already inherited code with structure you described you'd better follow it. Also you might be interested in MarionetteJS as significant improvement. Also highly recommend you to checkout BackboneRails, screencasts not free but very usefull, especially in large scale app maintain

pass data between controllers

I'm stating to learn AngularJS, coming from a lot of different MV* frameworks.
I like the framework, however I'm having trouble with passing data between Controllers.
Suppose I have a screen with some input (input.html) and a controller, let's say InputCtrl.
There's a button on this view which takes you to another screen, let's say approve (approve.html) with a controller ApproveCtrl.
This ApproveCtrl needs data from the InputCtrl. This seems like a very common scenario in bigger applications.
In my previous MV* frameworks, this would be handled like (pseudo-code):
var self = this;
onClick = function() {
var approveCtrl = DI.resolve(ApproveCtrl);
approveCtrl.property1 = self.property1;
approveCtrl.property1 = self.property2;
self.router.show(approveCtrl);
}
It would work like Controller- first.
You create the controller first, having a chance to put it in the right state; afterwards the View gets created.
Now, in AngularJS, I'm handling this like:
var self = this;
onClick = function(){
self.$locationService.path('approve');
}
This works like View-first.
You say to which view / route to navigate, the Controller gets created by the framework.
I find it hard to control the state of the created Controller and pass data to it.
I've seen and tried following approaches, but all have it's own issues in my opinion:
Inject a shared service into InputCtrl & ApproveCtrl and put all data to be shared on this service
This looks like a dirty work-around; the state in the shared service becomes global state, while I just need it to pass data to the ApproveCtrl
The lifetime of this shared service is way longer than what I need it for - just to pass data to the ApproveCtrl
Pass the data in $routeParams
This gets quite messy when having the pass a lot of parameters
Use $scope events
Conceptually, this is not something I would use events for - I just need to pass data to the ApproveCtrl, nothing event-ish
This is quite cumbersome; I have to send an event to the parent first, that would then broadcast it to it's children
Am I missing something here? Am I creating too many small Controllers?
Am I trying to hold on to habits from other frameworks too much here?
In terms of structure AngularJS is more Modular than MVC one.
Classic MVC describes 3 simple layers which interact with each other in such way that Controller stitches Model with View (and Model shouldn't rather work with View directly or vice versa).
In Angular you can have multiple, some completely optional, entities which can interact between each other in multiple ways, for example:
That's why there are multiple ways of communicating your data between different entities. You can:
Send messages directly between controllers using difference between this and $scope
Send messages using events
Send messages using shared system (Note: same link as above, answer shows both techniques)
or
Send messages using AJAX backend
Send messages using external system (such as MQ)
...and a lot more. Due to its diversity Angular allows developer/designer to choose way they are most comfortable with and carry on. I recommend reading AngularJS Developer Guide where you can find blessed solutions to some common problems.
If your intent is to simply share data between two views, a service is probably the way to go. If you are interested in persisting to a data store, you may want to consider some sort of back-end service such as a REST API. Take a look at the $http service for this.
Even if XLII gave a complete response, I found this tutorial using a service. It's very interesting and a simple way for sharing data between controlers using the 2 ways binding property : https://egghead.io/lessons/angularjs-sharing-data-between-controllers
I still havn't used it for now.
Otherwise there is also this other way, based on events : http://www.objectpartners.com/2013/08/21/using-services-and-messages-to-share-data-between-controllers-in-angularjs/
If you wish to pass simple string data from one page (page1) to another page (page2), one solution is to use traditional url parameters. Invoke the page2 route url with parameter like "/page2/param1/param2". The controller of page2 will receive the parameters in "routeParams". You will be able to access parameteres as routeParams.param1 and routeParams.param2. The code below is adopted from: How to get the url parameters using angular js
Invoke the page2 route from page1's controller(js) or a url in its html with parameters as:
"/page2/param1/param2"
Page2 route:
$routeProvider.when('/page2/:param1/:param2', {
templateUrl: 'pages/page2.html',
controller: 'Page2Ctrl'
});
And the controller:
.controller('Page2Ctrl', ['$scope','$routeParams', function($scope, $routeParams) {
$scope.param1 = $routeParams.param1;
$scope.param2 = $routeParams.param2;
...
}]);
Now you can access the parameters (param1 and param2) values in your page2's html/template as well.

How to refresh local data fetched using $resource service in AngularJS

I have AngularJS application that use $resource service to retrieve data using query() method and create new data using model.$save() method. This works fine exactly as the docs say it should.
My question is how to update my local data fetched using MyService.query() in the first place after I've changed it?
I took the most simple approach for now and I simply call the query() method again. I know this is the worst way efficiency-wise but it's the simplest one.
In my server-side I return the whole state-representation of the new model. How can I add the newly created model to the array of the local data?
UPDATE
I've end up simply pushing the model return from the server but I'll still be happy to know if that's the way to go. From what I can understand from the source code the return array is plan-old-javascript-array that I can manipulate myself.
This is the code I used
$scope.save = function () {
var newComment = new CommentsDataSource();
newComment.Content = $scope.todoText;
newComment.$save({ id: "1" }, function (savedComment) {
$scope.comments.push(savedComment);
});
}
I would simply get the whole list again, to be able to see the modifications brought to the list by other users.
But if the solution you're using suits you, then use it. It's corrrect. Angular uses bare-bones JavaScript objects. Adding a new instance to a list in the scope will refresh the list displayed on the page.

Backbone Marionette modules as Widgets similar to Twitter Flight

I'm reading up in choosing the correct client-side framework to segment/modularize my frontend code in Widgets.
Basically what I have/want is:
a complex website with multiple pagetypes, so no single-page application.
all pages are able to render a complete page WITHOUT the use of javascript. IOW: javascript is used as enrichment only.
Lots of pages have a very dynamic way in which widgets can be shown on screen. To overcome complexity at the server-side I've modularized my code into widgets (composite pattern), where each widget is responsible for it's own:
server-side controller code
server-side templating (using hogan/mustache)
routing endpoints, should it need to be called from the client
structural css (css converning the structure of the widget as opposed to the look&feel)
a server-side RegionManager ultimately decides which widgets are rendered and where they are rendered on screen. Endresults is that the RegionManager spits out the entire html (server-generated) as the composite of the rendering of all of it's widgets.
Now, some of these widgets DO have client-side logic and need rerendering on the client. Take a searchpage for instance, which needs to be able to update through ajax. (I've described this process, which uses DRY templating on client and server, here)
What I ultimately want is that, given I already use the composite pattern on the server, to extend this to the client somehow so that a Widget (1 particular logic block on the screen) contains all mentioned server-side code, plus all needed client-side code.
I hope this makes sense.
Would Marionette be suited to be used as a client side framework in this scenario? I'm asking since I'm not 100% sure if the concept of a Marionette Module is what I describe as being a Widget in above scenario. (I'm mentioning Twitter Flight in my question, since I believe this would be a fit, but it currently is so new that I'm hesitant to go with it at the moment_
I think basically what I'm asking is if anybody has some experience doing something along these lines.
I think just using Backbone.js is perfect for this type of application you are describing. You have probably already read this, but most of the backbone literature is focused around your views having associated server generated JSON models and collections, then using the View's render function to generate (on the client) the HTML UI that represents the model/collection.
However it doesn't have to be used this way. In fact there is nothing stopping you attaching views to existing elements that contain content already, which gives you all of the benefits of Backbone's modularity, events system and so on. I often use views that have no model or collection, purely because I like the conformity of style. I have also used an approach like I describe below in the cases where I have had to work with older, existing applications that have not yet got, or never will have a nice REST API, but they do provide content in HTML.
Firstly, lets assume the following HTML represents one of your widgets:
<div id="widget">
<div class="widget-title"></div>
<div class="widget-body">
<!-- assume lots more html is in here -->
Do something!
</div>
</div>
In this case, you could use backbone with a single Widget Model. This would be a very simple model, like this:
App.WidgetModel = Backbone.Model.extend({
intialize: function () {
this.url = this.options.url;
}
});
Take note of the fact the Widget receives a URL as a parameter to its constructor/initialize function. This widget model would represent many of your widgets (and of course you could adopt this general approach with more complicated models and pluck different data from the rendered HTML). So next for your views. As you probably know, normally you pass most views a model or collection when you instantiate them. However in this case, you could create the Widget model in your View's initialize Function and pass it a URL from the pre-rendered HTML as follows:
App.WidgetView = App.View.ComboboxView = Backbone.View.extend({
initialize: function () {
this.model = new App.WidgetModel({}, { url: this.$("a").attr("href") });
}
// rest of the view code
});
So instantiating the view would be something like:
new App.WidgetView({el: $("#widget")})'
By doing all of the above you can do pretty much everything else that backbone offers you and its modular and encapsulated nicely, which is what you are after.
The end result of this whole approach is:
You have rendered the Widget UI as pure HTML which (I assume) is functional without JavaScript.
You attach a View to the existing HTML.
You pass into the View as options, content by extracted (such as a URL) from the rendered HTML with jQuery.
The View is responsible for instantiating the Model passing on the relevant options the model needs (such as a URL).
This means all dynamic server side content is intially contained in the rendered HTML and your View is a modular JavaScript component that can do stuff to it, which I think is the end result you're after.
So you mentioned that you would like to have AJAX functionality for your widgets and that fine with this approach too. Using this approach, you can now use the standard Backbone fetch and save functions on the Widget model to get new content. In this example it is from the URL retrieved from the rendered HTML. When you get the response, you can use the view's, render function, or other finer grained functions to update the HTML on the page as required.
A few points:
The only thing to look out for is that you'll need to change the content type of the fetch and save functions to "text/html" if that's what the server is providing. For example:
this.model.fetch({
type: "POST",
contentType: "text/html"
});
Lastly, the model I have proposed is instantiated with no content. However if your ajax calls are a content type of "text/html", you may need to play around with you model so it can store this content in its attributes collection properly. See this answer for more information.

Who is supposed fetch a model in a backbone application?

In a backbone application what is the best practice regarding when a model is fetched? I can see the following possibilities.
View calls the model fetch method
Some other JavaScript code calls the fetch model? if so when and what structure would this code have? is this the missing controller concept in Backbone?
A few best practives:
1 Collections and models that are necessary from the very first milliseconds of the app's life should be 'bootstrapped' in place (so there shouldn't be need to fetch them to gain access to vital data)
So when the user is served the correct pages from the server, the models and collections should be already in place (nice example from backbone.js docs)
var ExampleCollection = new Backbone.Collection();
ExampleCollection.reset(<%= #your_collection_data.to_json() %>); // Or whatever your server-side language requires you to do, this is a ruby example
2 The rest can be fetched just in time
The models and collections that aren't needed at the moment your app is initialized can be fetched whenever you feel like it, but I think that the logical time to do that is when the user expresses intent to use those models. E.g. user presses a button to open a view that needs some model/collection -> fetch that collection, user wants to clear unsaved changes from a model -> fetch that model from the server to get the last saved status of the model, and so forth. Usually the place where the fetching is bound to happen is the view that 'owns' the model/collection, because it relays the users actions to the model and displays the model's state to the user.
But like it was said, Backbone.js isn't strict about when a model or collection should be fetched. It can be done anywhere in the app, just make sure you do it only when it's necessary.
Hope this helps!
If you want to be standard, your view must render one time when initialize and listen for the change event of the Model and re render the view every time that model changes, that is all. (regarding what does View needs to do when fetch is completed)
And for call the model.fetch() if you follow the standard that I said, no matters where the fetch is called your view will be updated.
Some people could have a module named load in the view where do something like this:
load : function(){
this.model.fetch();
}
Others could do external fetch call, like this:
var myModel = new YourModel();
var myView = new SomeView( {model : model} );
//Probably you could render with the default data in the while model is fetched
myView.render();
model.fetch();

Resources