Refresh page to see content - javascript

I've created a Rails app using Backbone, but I've run into a small but annoying problem. When a user goes to his profile and then back to the frontpage the frontpage is empty and I have to refresh it in order to show the content of the backbone view.
In my Movieseat.Routers.Movieseats I have:
routes:
'': 'index'
initialize: ->
#movieSeats = new Movieseat.Collections.Movieseats()
#movieSeats.fetch()
#searchResults = new Movieseat.Collections.Moviesearch()
index: ->
view = new Movieseat.Views.MovieseatsIndex(collection: #movieSeats)
$('#container').html(view.render().el)
Any idea on why users have to refresh to see the frontpage?

I don't know CoffeeScript but the fetch() method takes a success callback where you can ensure the collection has loaded. In your index you're creating a new view passing the #movieSeats collection but the collection may have not been loaded by that time. Try the following:
{
routes: {
'': 'index'
},
initialize: function() {
this.movieSeats = new Movieseat.Collections.Movieseats();
},
index: function() {
var self = this, view;
this.movieSeats.fetch({
success: function(collection) {
view = new Movieseat.Views.MovieseatsIndex({ collection: collection })
$('#container').html(view.render().el)
}
});
}
}
However, you might want to "bootstrap" your collection like is shown on the Backbone docs: http://documentcloud.github.io/backbone/#FAQ-bootstrap

Related

Redirect or render another view on event

I'm building a little CRUD app in Backbone, and I'm stuck a little with a need to redirect from one view to another. My app consists of a layout view, in which other views are rendered, and a router. Here it is:
var router = Backbone.Router.extend({
routes: {
'': 'home',
'resumes/:id': 'showResume'
},
home: function () {
// renders a index view with my collection
this.layout.render(new ResumeList({collection: resumes});
},
showResume: function () {
if (!this.fullResume) {
this.fullResume = new FullResume({model: new Resume()});
}
// allowing to navigate via url with model id
this.fullResume.model.set('id', id).fetch({
context: this,
success: function () {
this.layout.render(this.fullResume);
}
});
}
});
Then, in my FullResume view I've got a delete event, which destroys the model. Here it goes:
var FullResume = Backbone.View.extend({
// tagName and other stuff
events: {
// other events
'click #delete': 'deleteResume'
},
// initialize, render and other functions
deleteResume: function () {
this.model.destroy({
success: function (res) {
console.log('DELETE model' + res.toJSON().id);
},
error: function () {
console.log('Failed to DELETE');
}
});
}
});
The function above works perfectly and deletes the model, but after deleting the model it still remains on it's view until I navigate somewhere manually. I read a bit and tried to manage how to render the main view after this event or redirecting to it, but didn't succeed a much.
You are looking for the http://backbonejs.org/#Router-navigate function with the trigger option set to true.
Here's an example: http://jsfiddle.net/x3t7u5p0/
Clicking on "Home" or "About" links will change the view, however I've added a delayed programmatic view change, when the About view renders, it will switch back to Home after the delay
render: function () {
this.$el.html(this.template);
_.delay(function() {
appRouter.navigate('home', {trigger: true});
}, 500);
}

backbone fragment route is added to root when has :attributes

I'm quite new using Backbone and now I have found this new issue.
I use the route "jobprofile" to create a view which fetch the data from urlRoot= "job" (doing job/id using a default id) BUT if I add the :id to the route as "jobprofile/:id" which I need to type in the browser to be able to get the job.id view, then it stops to work and the url of the model change to: ".../jobprofile/job/id" which (obviously) give me 404 error.
Hope is clear. Thanks!
CODE:
I have a router.js
routes: {
...
"jobprofile/:id": "view", //without /:id works!
},
view:function(id){
console.log("view");
this.job = new Job();
this.job.setId(id); //This is set correctly
this.jobProfileView = new JobProfileView({
model: this.job,
el: $('.tab-content')
});
},
(View)JobProfileView.js:
...
initialize: function(){
var that = this;
this.model.fetch().done(function(){
console.log("fetch done!");
that.render();
});
},
...
(Model)Job.js:
urlRoot: 'job',
initialize: function () {
},
setId: function (job_id) {
this.set('id', job_id);
},
UPDATED:
Ok. So it looks that I "fix" the problem adding this.navigate('/jobprofile'); to the method view in router.js. I guess that the /:id which causes the problem is deleted from the route (actually when you see the browser its not there anymore) but I still keep the id in the method.
In any case, this is a really bad solution because when I try to go back it creates a bucle and it goes to jobprofile/id and navigate again to jobprofile. So if anyone has an idea it would be great...
Finally I understood what the problem was...
Basically there is a difference when in the url or urlRoot are set in the model. Thus, these two options appear:
url:'/foo'. In this case it will not take the base url.
example: www.web.com/foo
url:'foo'. In this case, it will take the base url
example: www.web.com/api/foo
My case is as follow:
(Model)Job.js:
urlRoot: '/job',
initialize: function () {
},
setId: function (job_id) {
this.set('id', job_id);
},

Backbone sub views definition - main view vs router

Here is how my Backbone Router looks like
define([
"jquery",
"underscore",
"backbone"
], function ($, _, Backbone) {
return Backbone.Router.extend({
routes: {
"overview": "overview"
},
overview: function () {
require([
"views/overview",
"models/user-collection",
"grid",
"spreadsheet"
], function (OverviewView, TestCollection, GridView, SpreadSheetView) {
// Data
var collection = new TestCollection();
// Main view
var view = new OverviewView({
el: "#page",
collection: collection
});
// Sub view #1
var gridView = new GridView({
el: "#backgridWrapper"
});
// Sub View #2
var spreadsheetView = new SpreadSheetView({
el: "#handsontableWrapper"
});
// Flow
collection.fetch({
success: function () {
view.render();
gridView.render();
spreadsheetView.render();
}
});
});
}
});
});
As you can see there are several views:
Main view
Sub view #1
Sub view #2
I've did a lot of searching on how to organize the views and sub-views in Backbone, however all of them supposed to create a new sub-view instance directly within a view definition, so that router only knows about Main view...
So the question is - is it a good idea to handle sub-views at a router, instead of directly at view constructor?
The router should be just handling routes and initializing stuff.
Things like fetching data should go in the view that uses it - The view displays the data or error messages (in case of a failure), so I think it's wise to let the view fetch the data rather than some router who's only interested in the routes and have no interest in the data.
and I prefer initializing the sub views, inside their parent view, rather than somewhere else. The parent - child relationship itself justifies that, you better keep the children with their parents than a stranger so they will be under better control and you can easily find them later as well :)
Mostly it's a matter of opinion, but the thing is if you don't, all your code will soon get cluttered in the router rather than being well organized.
Below is how I'll structure the same thing.
Note that I'm initializing child views as part of parent views render method. It could be done when the parent view is initialized, but I see no point in doing so unless the parent view successfully fetches data and is proceeding to render itself.
define([
"jquery",
"underscore",
"backbone"
], function($, _, Backbone) {
return Backbone.Router.extend({
routes: {
"overview": "overview"
},
overview: function() {
require(["views/overview"], function(OverviewView) {
// initialize Main view
var view = new OverviewView({
el: "#page"
});
});
}
});
});
define([
"jquery",
"underscore",
"backbone",
"models/user-collection",
"grid",
"spreadsheet"
], function($, _, Backbone, TestCollection, GridView, SpreadSheetView) {
return Backbone.View.extend({
initialize: function(options) {
this.collection = new TestCollection();
this.fetchData();
},
events: {},
render: function() {
// rendering subviews is part of rendering their parent view.
//I prefer to do that here
// Sub view #1
this.gridView = new GridView({
el: "#backgridWrapper"
});
// Sub View #2
this.spreadsheetView = new SpreadSheetView({
el: "#handsontableWrapper"
});
//Below lines can be handled while initializing the respective view
// (In their initialize() method, or after fetching some data etc
// or can be chained with the above initialization if their render() method returns a reference to itself (`return this`)
this.gridView.render();
this.spreadsheetView.render();
},
fetchData: function() {
var view = this;
this.collection.fetch({
success: function() {
view.render();
}
});
}
});
});
side note : I strongly suggest not to put a collection under models folder.

Backbone Boilerplate - fetch method don't refresh collection

is my first question here, so I please about some patience and forgive my english:)
When I type link in browser address bar, all is OK. But when I do this inside browser by clicking element, collection is empty. But the main problem is there is always the same response from server, but fetch "dont load" any items, so view render empty collection.
I use Backbone Boilerplate,
Browser.Views.Catalog - it is Backbone.View
Browser.Catalog - it is of Backbone.Collection
My router:
var Router = Backbone.Router.extend({
routes: {
'' : 'browse'
},
refreshCatalog: function(folder){
app.layout.setViews({
"#catalog" : new Browser.Views.Catalog({
collection: app.catalog
})
}).render();
},
browse: function(folder){
app.catalog = new Browser.Catalog();
app.folders.fetch({
error: function() { console.log(arguments); },
success: this.refreshFolders(folder),
data: $.param({folder: folder}),
cache:false
});
//app.catalog = new Browser.Catalog();
app.catalog.fetch({
error: function() { console.log(arguments); },
success: this.refreshCatalog(folder),
data: $.param({folder: folder}),
cache:false
});
},
I belive you should set the catalog in the initialize function
app.catalog = new Browser.Catalog();
should go in here ( add this function)
initialize: function (options) {
app.catalog = new Browser.Catalog();
}
the initialize function is called when the page is loaded so when browsing to #catelog it will have been set http://backbonejs.org/#Router-constructor

How do I bind an event to a model that isn't loaded yet?

So I've got a pretty simple backbone app with a model, a collection, and a couple of views. I'm fetching the actual data from the server by doing a collection.fetch() at page load.
My problem is that one of my views is a "detail" view, and I want to bind it to a particular model - but I don't have the model yet when the page loads. My code looks a lot like this:
window.App = {
Models: {},
Collections: {},
Views: {},
Routers: {}
}
App.Models.Person = Backbone.Model.extend({
urlRoot: '/api/people'
});
App.Collections.People = Backbone.Collection.extend({
model: App.Models.Person,
url: '/api/people'
});
people = new App.Collections.People()
App.Views.List = Backbone.View.extend({
initialize: function() {
this.collection.bind('reset', this.render());
},
render: function() {
$(this.el).html("We've got " + this.collection.length + " models." )
}
});
listView = new App.Views.List({collection: people})
App.Views.Detail = Backbone.View.extend({
initialize: function() {
this.model.bind('change', this.render());
},
render: function() {
$(this.el).html("Model goes here!")
}
});
App.Routers.Main = Backbone.Router.extend({
routes: {
'/people': 'list',
'/people/:id': 'detail'
},
list: function() {
listView.render();
},
detail: function(id) {
detailView = new App.Views.Detail({model: people.get(id)})
detailView.render()
}
})
main = new App.Routers.Main();
Backbone.history.start();
people.fetch();
But if I start with the detail route active, the people collection is empty, so people.get(id) doesn't return anything, so my new view has this.model undefined, and won't let me bind any events relating to it. The error is:
Uncaught TypeError: Cannot call method 'bind' of undefined
If I start with the list route active, then by the time I click on an item to bring up the detail view people is populated, so everything works.
What's the right way to bind model-related events for a "detail" view when you're fetching the data after page load?
You have a part of the answer here: Backbone.js Collections not applying Models (using Code Igniter)
Indeed, you need to wait that people.fetch finishes its ajax request before to call Backbone.history.start(); and trigger the actual route.
Your code should look like:
// [...]
main = new App.Routers.Main();
peoples.fetch({
success: function (collection, response) {
// The collection is filled, trigger the route
Backbone.history.start();
}
});
You can add a loader on the page and hide it when the collection is loaded.

Categories

Resources