how to move one view to another view? - javascript

could you please tell me how to navigate from one page to another page in backbone .
I want to show second html on button click how it is possible
I so like that .I resister event like that
events: {
'click #click':'moveTonext'
},
moveTonext: function(){
alert('---')
},
I make second page like that
define([
'jquery',
'underscore',
'backbone',
'text!templates/second.html'
], function ($, _, Backbone, statsTemplate) {
'use strict';
var secondView = Backbone.View.extend({
// Instead of generating a new element, bind to the existing skeleton of
// the App already present in the HTML.
el: '#todoapp',
// Compile our stats template
template: _.template(statsTemplate),
// Delegated events for creating new items, and clearing completed ones.
events: {
},
// At initialization we bind to the relevant events on the `Todos`
// collection, when items are added or changed. Kick things off by
// loading any preexisting todos that might be saved in *localStorage*.
initialize: function () {
this.render();
},
serialize: function () {
return {
message: 'world'
};
},
// Re-rendering the App just means refreshing the statistics -- the rest
// of the app doesn't change.
render: function () {
this.$el.html(this.template());
}
// Add a single todo item to the list by creating a view for it, and
// appending its element to the `<ul>`.
});
return secondView;
})
Second html
<h1>second</h1>
here is my plunker
http://plnkr.co/edit/fCXwSrroJP1l6BppjpmD?p=preview

Basically your button should trigger navigation, so the click handler should look like this:
moveToNext: function () {
router.navigate("other/path", { trigger: true });
}
Then, in your router code you need to add a route handler for the above path:
routes: {
"other/path": "handleOtherPath"
},
handleOtherPath: function () {
new SecondView();
}
This is for the case when SecondView should replace FirstView. If it should be appended instead, the following mechanism can be used:
moveToNext: function () {
new SecondView({ el: this.$(secondViewContainerSelector) });
}
Here's a working Plunker sample.

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 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.

How can i attach a listener in a base object to the top/extending object

The goal : i want to attach a listener in a base class to the top-most instance of inheritance.
The problem : the listener is attaching to the base class; thus, with every new call, an additional listener is attached.
Details : as the code snippets below show, i'm using require and backbone to create my objects - one object per file. PrimaryObj extends midObj, which extends baseObj, which extends Backbone.Model.
In the view (template) are a field and a button which, when clicked, calls loadThing. The user can fill in the field with new values, then click on the button each time to load new data.
However, every time a new object is built, the attributes are correctly assigned, but it's like i get a reference to the baseObj (which already has a listener attached), instead of a new clean copy/clone (which would have no listeners attached ... yet).
I thought that the reference to this in baseObj would refer to the primary/extending object (primaryObj), not baseObj itself (which is where it seems like it's being attached).
How can i attach a listener in the base object (baseObj) to JUST the top object (primaryObj)?
Thanks!
main.js ---------------
require([ 'backbone', 'routers/router' ], function (Backbone, Router) {
window.DISPATCHER = _.clone(Backbone.Events);
var router = new Router();
Backbone.history.start();
});
baseObj.js ---------------
define([ 'backbone', 'underscore' ], function (Backbone, _) {
return Backbone.Model.extend({
initialize : function (p_thing) {
this.listenTo(window.DISPATCHER, "message", function (p_data) { /* some cool functionality */ });
}
});
});
midObj.js ---------------
define([ 'models/baseObj' ], function (BaseOBJ) {
return BaseOBJ.extend({
initialize : function () {
BaseOBJ.prototype.initialize.apply(this, arguments);
}
});
});
primaryObj.js ---------------
define([ 'models/midObj' ], function (MidOBJ) {
return MidOBJ.extend({
initialize : function (p_id) {
this.set({'ref' : p_id});
MidOBJ.prototype.initialize.apply(this, arguments);
}
});
});
myView.js ---------------
define([ 'jquery', 'underscore', 'backbone', 'primaryObj' ], function($, _, Backbone, MPrimary) {
return Backbone.View.extend({
events : { 'click .bLoad' : 'loadThing', },
initialize : function (p_options) {
this.listenTo(Backbone, 'obj:loaded', function () { this.render("device"); });
this.model = {};
},
loadThing : function (p_id) { this.model = new MDevice(p_id); },
render : function (p_target) { /* ... */ },
template : _.template(TModelTest)
});
});
In the end it was discovered that the problem, as posted, was not really the problem. The listener was being handled correctly but was never getting cleaned up; thus the 'old'/original object was getting garbage collected, but it's listener was (correctly) not.
As stated in the problem, this.listenTo(Backbone, ...) created a listener related to the object on the Backbone object, not the created object; the solution, then, was to explicitly remove the listener on the backbone object.

How does one "listen to the router" (respond to Router events in Views/Models) in Backbone.js?

In the Backbone.js documentation, in the entry for the Router.routes method, it is stated
When the visitor presses the back button, or enters a URL, and a particular route is matched,
the name of the action will be fired as an event, so that other objects can listen to the router,
and be notified.
I have attempted to implement this in this relatively simple example:
The relevant JS:
$(document).ready(function(){
// Thing model
window.Thing = Backbone.Model.extend({
defaults: {
text: 'THIS IS A THING'
}
});
// An individual Thing's View
window.ThingView = Backbone.View.extend({
el: '#thing',
initialize: function() {
this.on('route:showThing', this.anything);
},
anything: function() {
console.log("THIS DOESN'T WORK! WHY?");
},
render: function() {
$(this.el).html(_.template($('#thing-template').html(), {
text: this.model.get('text')
}));
return this;
}
});
// The Router for our App
window.ThingRouter = Backbone.Router.extend({
routes: {
"thing": "showThing"
},
showThing: function() {
console.log('THIS WORKS!');
}
});
// Modified from the code here (from Tim Branyen's boilerplate)
// http://stackoverflow.com/questions/9328513/backbone-js-and-pushstate
window.initializeRouter = function (router, root) {
Backbone.history.start({ pushState: true, root: root });
$(document).on('click', 'a:not([data-bypass])', function (evt) {
var href = $(this).attr('href');
var protocol = this.protocol + '//';
if (href.slice(protocol.length) !== protocol) {
evt.preventDefault();
router.navigate(href, true);
}
});
return router;
}
var myThingView = new ThingView({ model: new Thing() });
myThingView.render();
var myRouter = window.initializeRouter(new ThingRouter(), '/my/path/');
});
The relevant HTML:
<div id="thing"></div>
<!-- Thing Template -->
<script type="text/template" id="thing-template">
<a class='task' href="thing"><%= text %></a>
</script>
However, the router event referenced in the View's initialize function does not seem to get picked up (everything else works--I'm successfully calling the "showThing" method defined in the Router).
I believe I must have some misconception about what the documentation intended by this statement. Therefore, what I'm looking for in a response is: I'd love to have someone revise my code so that it works via a Router event getting picked up by the View, or, clearly explain what the Router documentation I listed above intends us to do, ideally with an alternative code sample (or using mine, modified).
Many thanks in advance for any assistance you can provide!
This is beacuse you are binding a listener to the wrong object. Try this in your View :
window.ThingView = Backbone.View.extend({
initialize: function() {
myRouter.on('route:showThing', this.anything);
},
...

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