Backbone: Click event re-renders child view - javascript

var LandingView = Backbone.View.extend({
initialize: function() {
console.log('Landing View has been initialized');
this.render();
},
template: Handlebars.compile($('#landingPage').html()),
render: function() {
this.$el.html(this.template);
},
events: {
// I want to render the subview on click
'click .btn-login' : 'renderlogin',
},
renderlogin: function() {
// Is this the right way to instantiate a subview?
var loginpage = new LoginView({ el: $('#modal-content') });
}
});
And my next view, which basically just empties the $('#modal-content') element...
var LoginView = Backbone.View.extend({
initialize: function() {
this.render();
console.log("login view initialized");
},
template: Handlebars.compile($('#loginPage').html()),
render: function() {
this.delegateEvents();
this.$el.html(this.template);
},
events: {
// this is where things get super confusing...
// Upon clicking, LoginView gets re-initialized and
// subsequent clicks are called for each number of times
// the view is initialized.
'click .js-btn-login' : 'login'
},
login: function(e) {
e.preventDefault();
var self = this;
console.log($(this.el).find('#userSignIn #userEmail').val());
console.log($(this.el).find('#userSignIn #userPassword').val());
}
});
My templates:
LANDING PAGE:
<script type="text/x-handlebars-template" id="landingPage">
<div>
<div class="auth-wrapper">
<div class="logo">
<img src="img/login/logo-landing.png"/>
</div>
<div class="to-auth-buttons-wrapper">
<a class="btn-to-auth btn-signup" href="#">Sign Up</a>
<a class="btn-to-auth btn-login" href="#">Log In</a>
</div>
</div>
</div>
</script>
LOGINPAGE:
<script type="text/x-handlebars-template" id="loginPage">
<div>
<div class="header">
Back
</div>
<div class="auth-wrapper">
<div class="logo">
<img src="img/login/logo-landing.png"/>
</div>
<form method="post" id="userSignIn">
<input class="form-control input-signin" type="text" name="useremail" placeholder="Email" id="userEmail" value="tester">
<input class="form-control input-signin" type="password" name="userpass" placeholder="Password" id="userPassword">
<button class="btn-to-auth btn-login js-btn-login">Log In</button>
</form>
</div>
</div>
</script>
My goal:
From within LandingView, upon clicking .btn-login, render LoginView.
From within LoginView, upon clicking .js-btn-login, console.log
contents of form
Problems:
In LoginView, upon clicking .js-btn-login, I see that the initialize function is called again.
In LoginView, I can't use jquery to get the values inside of $('#userSignIn #userEmail').val() and $('#userSignIn #userEmail').val() because they aren't there on render. I see the initial hardcoded value ( input[value="tester"]) but this is all it sees.
My question:
How do I get the view to stop reinitializing on an event firing and how do I get the values in my DOM after rendering?

Related

Template isn't rendered with Django

I'm having issues rendering a modal popup dialog as a template with Underscore and Backbone on a Django website, otherwise it works as a standalone single page app. Why would that be?
Here is a snippet of the template:
<script type="text/template" id="popup-template">
<div class="modal sector-popup" style="background-color:#f5f5f5; visibility:visible;">
<div class="modal-header" style="background-color:#FFFFFF">
<input type="image" class="pull-right" id="closebtn" src="../media/x-button.png" data-dismiss="modal">
...
</div>
</script>
And here is where it is used in the website:
window.FormPopup = Backbone.View.extend({
template: _.template($('#popup-template').html()),
events: {
"dblclick .sector-label" : "edit",
"keypress .sector-label-edit input" : "setLabel"
},
render: function() {
var view = this;
this.$el.html(this.template(this.model.toJSON()));
...
},
});

Right way for backbone view code

I have a side view list and when one of the item in the list is clicked I show the corresponding view
My code is as follows:
VIEW
app.View.BrandSidePanelView = Backbone.View.extend({
tagName: 'div',
template: _.template($('#brand-side-panel').html()),
template_brand: _.template($('#brand-create').html()),
template_offer: _.template($('#offer-create').html()),
initialize: function() {
this.render();
},
events: {
'click .bra-main': 'showBrandCreateView',
'click .bra-off': 'showOfferCreate',
'click .bra-cmgn': 'showCampaignCreate'
},
showBrandCreateView: function(e) {
e.preventDefault();
this.reset();
$('.crt-cnt').html(this.template_brand());
},
showOfferCreate: function(e){
e.preventDefault();
this.reset();
$('.crt-cnt').html(this.template_offer());
},
render: function() {
$('.crt-cnt').html(this.template_brand());
var $el = $(this.el);
$el.html(this.template());
return $(this.el);
},
reset: function(){
$('.crt-cnt').empty();
}
});
HTML
<div class="list-group">
<div class="list-group-item bra-main"><i class="fa fa-angle-double-right"></i> <fmt:message key="brand" /></div>
<div class="list-group-item bra-off"><i class="fa fa-angle-double-right"></i> <fmt:message key="brand.offer" /></div>
<div class="list-group-item bra-cmgn"><i class="fa fa-angle-double-right"></i> <fmt:message key="brand.campaign" /></div>
</div>
Everything here is static. Nothing is fetched from the server. I am very new to backbone. The current code works but wants to know if I am doing it the right way.
If your side view list won't change in time, it's ok to do it like this, it's just a sort of static menu.
But if the elements in the list could change, you must wrap it in a View and fetch the elements from the server

Render Backbone View on click (via Router push state)

I've got an app with 3 pages which I'm trying to render on click. The first page is a landing page. The other two should be rendered upon clicking a link. They are all contained inside of the same container div#modal-content
My HTML is as follows:
<script type="text/x-handlebars-template" id="landingPage">
<div>
<div class="auth-wrapper">
<div class="logo">
<img src="img/login/logo-landing.png"/>
</div>
<div class="to-auth-buttons-wrapper">
<a class="btn-to-auth btn-login" href="#signup-page">Sign Up</a>
<a class="btn-to-auth btn-signup" href="#login-page">Log In</a>
</div>
</div>
</div>
</script>
My router function is as follows:
var Approuter = Backbone.Router.extend({
initialize: function() {
console.log('router initialized');
Backbone.history.start({ pushState: true });
},
routes: {
'': 'main',
'signup-page' : 'signup',
'login-page' : 'login'
},
main: function() {
this.landing = new LandingView({ el: $('#modal-content') });
slider.slidePage(this.landing.$el);
},
signup: function() {
this.signuppage = new SignUpView({ el: $('#modal-content') });
console.log("LANDING VIEW: Signup clicked");
},
login: function() {
this.loginpage = new LoginView({ el: $('#modal-content') });
console.log("LANDING VIEW: Login clicked");
}
});
And the view files are as follows:
var SignUpView = Backbone.View.extend({
initialize: function() {
this.render();
},
render: function() {
var template = Handlebars.compile($('#signUpPage').html());
this.$el.html(template);
},
});
and
var LoginView = Backbone.View.extend({
initialize: function() {
this.render();
},
render: function() {
var template = Handlebars.compile($('#loginPage').html());
this.$el.html(template);
},
});
Additionally, here are my templates:
<div id="modal-content">
<script type="text/x-handlebars-template" id="landingPage">
<div>
<div class="auth-wrapper">
<div class="logo">
<img src="img/login/logo-landing.png"/>
</div>
<div class="to-auth-buttons-wrapper">
<a class="btn-to-auth btn-login" href="#/signup-page">Sign Up</a>
<a class="btn-to-auth btn-signup" href="#/login-page">Log In</a>
</div>
</div>
</div>
</script>
<script type="text/x-handlebars-template" id="signUpPage">
<div>
<div class="header">
Back
</div>
<div class="auth-wrapper">
<div class="logo">
<img src="img/login/logo-landing.png"/>
</div>
<form method="post" id="userSignUp">
<input class="form-control input-signin" type="text" name="username" placeholder="Name" id="userName">
<input class="form-control input-signin" type="text" name="useremail" placeholder="Email" id="userEmail">
<input class="form-control input-signin" type="text" name="userpass" placeholder="Password" id="userPassword">
<a class="btn-to-auth btn-login js-btn-login">Sign Up</a>
</form>
</div>
</div>
</script>
<script type="text/x-handlebars-template" id="loginPage">
<div>
<div class="header">
Back
</div>
<div class="auth-wrapper">
<div class="logo">
<img src="img/login/logo-landing.png"/>
</div>
<form method="post" id="userSignIn">
<input class="form-control input-signin" type="text" name="useremail" placeholder="Email" id="userEmail">
<input class="form-control input-signin" type="password" name="userpass" placeholder="Password" id="userPassword">
<a class="btn-to-auth btn-login js-btn-login">Log In</a>
</form>
</div>
</div>
</script>
</div>
My Problem
Upon clicking the a#signup-page or a#login-page links, I can see the url change to "localhost/#signup-page", but the view is not being rendered.
BUT
When I refresh the page at localhost/#signup-page or localhost/#login-page, I see the views are rendered.
Where am I going wrong?
Please take a look at the code above:
<html>
<body>
<div class="action">
<a name="routeOne" href="#routeTwo">routeOne</a>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<a name="routeTwo" href="#routeOne">routeTwo</a>
</div>
<div class="output"></div>
<script type="text/javascript" src="lib/jquery.js"></script>
<script type="text/javascript" src="lib/underscore.js"></script>
<script type="text/javascript" src="lib/backbone.js"></script>
<script type="text/javascript">
var Approuter = Backbone.Router.extend({
initialize: function() {
console.log('router initialized');
Backbone.history.start({pushState:true});
},
routes: {
'': 'main',
'routeOne' : 'routeOne',
'routeTwo' : 'routeTwo'
},
main: function() {
console.log("main");
},
routeOne: function() {
console.log("routeOne");
},
routeTwo: function() {
console.log("routeTwo");
}
});
var routes = new Approuter();
</script>
</body>
</html>
Edit1: Differences between Routes and pushState
Backbone Routes enable you to monitoring hasChange history events (changes in url) and trigger some js code when found some change at url (http://localhost/backbone-test/#someRoute), That's amazing because we can trigger some complex actions done by user at your web site, just calling an url.
pushState enable you to hide this '#' hash and turn your url more readable, but as the backbone documentation said
"if you have a route of /documents/100, your web server must be able
to serve that page, if the browser visits that URL directly."
Then, if you use pushState:true your url become more readable, http://localhost/backbone-test/#someRoute to http://localhost/backbone-test/someRoute but you need to create a back-end to answer directly access to you readable url.
When pushState is true and you call href="#someRoute" the browser understand this as a html anchor.

Why is extending an extended view not working in ember.js?

I am trying to create a modal view and have a base class that all modals need and then extending it for more specific functionality.
PlanSource.Modal = Ember.View.extend({
isShowing: false,
hide: function() {
this.set("isShowing", false);
},
close: function() {
this.set("isShowing", false);
},
show: function() {
this.set("isShowing", true);
}
});
PlanSource.AddJobModal = PlanSource.Modal.extend({
templateName: "modals/add_job",
createJob: function() {
var container = $("#new-job-name"),
name = container.val();
if (!name || name == "") return;
var job = PlanSource.Job.createRecord({
"name": name
});
job.save();
container.val("");
this.send("hide");
}
});
I render it with
{{view PlanSource.AddJobModal}}
And have the view template
<a class="button button-green" {{action show target=view}}>+ Add Job</a>
{{#if view.isShowing}}
<div class="modal-wrapper">
<div class="overlay"></div>
<div class="dialog box box-border">
<div class="header">
<p class="title">Enter a job name.</p>
</div>
<div class="body">
<p>Enter a name for your new job.</p>
<input type="text" id="new-job-name" placeholder="Job name">
</div>
<div class="footer">
<div class="buttons">
<a class="button button-blue" {{action createJob target=view}} >Create</a>
<a class="button" {{action close target=view}}>No</a>
</div>
</div>
</div>
</div>
{{/if}}
The problem is that when I click the button on the modal dialog, it gives me an "action createJob" can not be found. Am I extending the objects incorrectly because it works if I put the createJob in the base Modal class.
Fixed
There was an issue somewhere else in my code. The name got copied and so it was redefining it and making the method not exist.

Backbone.js View Event Not Firing

I am having problems getting my backbone.js view events to fire. When I click #login-button nothing is happening.
I am also using iCanHaz (http://icanhazjs.com/) to load the templates.
Here is my javascript:
$(function() {
var router = new App.Router;
Backbone.history.start();
});
App.Router = Backbone.Router.extend({
routes: {
"login": "login"
},
login: function() {
var view = new App.Views.LoginView;
}
});
App.Views.LoginView = Backbone.View.extend({
el: '#login',
initialize: function() {
_.bindAll(this, 'render', 'validateLogin');
this.render();
},
render: function() {
App.Stage.html(ich.loginView());
},
events: {
'click button#login-button' : 'validateLogin'
},
validateLogin: function(event) {
console.log("validateLogin fired.");
}
});
Here is my markup (using ICanHaz.js):
<script id="loginView" type="text/html">
<div data-role="page" id="login" class="container">
<div class="hero-unit">
<div class="row-fluid">
<div class="span12">
<div class="form-horizontal">
<fieldset>
<legend>Please login to continue</legend>
<div class="control-group">
<label class="control-label" for="username">Username</label>
<div class="controls">
<input type="text" class="input-xlarge" id="username">
</div>
</div>
<div class="control-group">
<label class="control-label" for="password">Password</label>
<div class="controls">
<input type="password" class="input-xlarge" id="password">
</div>
</div>
<div class="form-actions">
<button id="login-button" class="btn btn-primary btn-large">Login</button>
</div>
</fieldset>
</div>
</div>
</div>
</div>
</div>
</script>
I figured it out. Backbone was binding the events to the el before it was created. In the callback for the asynchronous iCanHaz call, I used this.setElement('#login') and it worked perfectly. From the backbone.js documentation:
setElement [view.setElement(element)]
"If you'd like to apply a Backbone view to a different DOM element, use setElement, which will also create the cached $el reference and move the view's delegated events from the old element to the new one."
I believe you aren't using the View's 'el' property correctly. Try having the property reference the jQuery DOM object instead of just the ID, as follows:
App.Views.LoginView = Backbone.View.extend({
el: $('#login')
...

Categories