I develop the first backbone RESTfull application. I have the following view:
var app = app || {};
app.AssessorsCollectionView = Backbone.View.extend({
el: '#assessors',
tagName: 'table',
events:{
"submit form": "searchAssessors"
},
initialize: function() {
this.collection = new app.AssessorsCollection();
this.collection.fetch({reset: true, data: $.param({ online: false}) });
this.render();
this.listenTo( this.collection, 'reset', this.render );
},
render: function() { /// },
renderRegion: function( item ) { /// },
searchAssessors: function( e ) {
e.preventDefault();
this.collection.remove();
console.log("EVENT");
}
});
This is my HTML Form:
<form id="form-search-assessors" class="form-horizontal" role="form">
.........
<input id="search-assessors-button" class="btn btn-lg btn-success btn-block" type="submit" value="Search" />
<!--<button id="search-assessors-button" class="btn btn-lg btn-success btn-block" type="submit">Search</button>-->
</form>
I put breakpoint to searchAssessors, but it does not work. Also, I don't see any messages in console.
Try changing the event to "submit #form-search-assessors": "searchAssessors".
Did you initialize the view ?, Since is just a fragment from your code I removed a couple of things and comments, this should work.
Thanks
HTML
<div id="assessors">
<form id="form-search-assessors" class="form-horizontal" role="form">
<input id="search-assessors-button" class="btn btn-lg btn-success btn-block" type="submit" value="Search" />
</form>
<div>
JS
AssessorsCollectionView = Backbone.View.extend({
el: '#assessors',
tagName: 'div',
events:{
'submit form': 'searchAssessors'
},
initialize: function() {
this.render();
},
render: function() { },
searchAssessors: function( e ) {
e.preventDefault();
console.log('EVENT');
}
});
view = new AssessorsCollectionView();
The solution of my problem:
$( "form#form-search-assessors" ).submit($.proxy(function(e) {
e.preventDefault();
console.log(this);
this.collection.fetch({reset: true, data: $.param({ online: true}) });
this.render();
}, this));
The sourse is: https://stackoverflow.com/a/9813760/1756750
Related
I have a view created with Backbone.js and inserted into a div element - it shows for about a second and then disappears.
Here is my code for the view:
var addPlayerView = Backbone.View.extend({
tagName: "div",
model: Player,
id: 'addPlayerDiv',
initialize: function() {
console.log('addPlayerView has been created');
},
render: function (){
this.$el.html('<p>show this puppy</p>');
return this;
}
});
here is the model:
var Player = Backbone.Model.extend({
defaults: {
ID: "",
firstName: '',
lastName: ''
},
idAttribute: "ID"
});
and here is the HTML:
<form onsubmit="addNewPlayer();">
<input type="submit" value="Add New Player New"/>
</form>
<p>
<div id="addPlayerDiv"></div>
</p>
<script>
function addNewPlayer() {
var player = new Player({});
var newPlayerView = new addPlayerView({el: $("#addPlayerDiv"), model: player});
newPlayerView.render();
};
</script>
The addNewPlayer() function is being called correctly and the newPlayerView is rendering on the page, but only for a second, then it disappears on the page.
No idea what to do. Anyone have this problem before?
You need cancel the default action (in our case onsubmit tries send data to server)
<form onsubmit="addNewPlayer(); return false;">
or
<form onsubmit="addNewPlayer(event);">
function addNewPlayer(e) {
e.preventDefault();
.....
}
Example
I have problem with my groupedBy collections because is not rendered after second event click. I think there is problem with fetch my collection...but I don't know how it fix. There is my code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Backbone test</title>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/css/bootstrap.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
ul.items{list-style: none;width: 100%;margin: 0px;-webkit-margin-before: 0em;-webkit-padding-start: 0px;}
</style>
</head>
<body>
<header>
</header>
<content id="content">
<div id="persons"></div>
<div id="items"></div>
<div id="orders"></div>
</content>
<footer>
</footer>
<script id="itemsTemlate" type="text/template">
<div class="jumbotron">
<div class="container">
<h1>My Items!</h1>
</div>
<button class="open-orders" style="float:left;">Orders</button>
<button class="open-persons" style="float:right;">Persons</button>
</div>
<% _.each( data, function( category, i ){ %>
<h3 class="category-name"><%= i %></h3>
<div><% _.each( category, function( item ){ %>
<li class="product"><%= item.title %><p style="float:right;" class="cccc">-</p><p style="float:right;" class="cccc">+</p></li>
<% }) %>
</div>
<% }) %>
</script>
<script id="ordersTemlate" type="text/template">
<div class="jumbotron">
<div class="container">
<h1>My Orders!</h1>
</div>
<button class="open-items" style="float:left;">Items</button>
<button class="open-persons" style="float:right;">Persons</button>
</div>
<% _.each( data, function( category, i ){ %>
<h3 class="category-name"><%= i %></h3>
<div><% _.each( category, function( order ){ %>
<li class="order"><%= order.title %><p style="float:right;" class="cccc">X</p></li>
<% }) %>
</div>
<% }) %>
</script>
<script id="personsTemlate" type="text/template">
<div class="jumbotron">
<div class="container">
<h1>My Persons!</h1>
</div>
<button class="open-items" style="float:left;">Items</button>
<button class="open-orders" style="float:right;">Orders</button>
</div>
<% _.each( data, function( category, i ){ %>
<h3 class="category-name"><%= i %></h3>
<div><% _.each( category, function( person ){ %>
<li class="product"><%= person.title %><p style="float:right;" class="cccc">X</p></li>
<% }) %>
</div>
<% }) %>
</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.1/underscore-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone-min.js"></script>
<script>
(function() {
window.App = {
Models: {},
Collections: {},
Views: {},
Router: {}
};
window.vent = _.extend({}, Backbone.Events);
})();
// !models.js
App.Models.Item = Backbone.Model.extend({});
App.Models.Person = Backbone.Model.extend({});
App.Models.Order = Backbone.Model.extend({});
// !collections.js
App.Collections.Items = Backbone.Collection.extend({
model: App.Models.Item,
url: 'api/items.json'
});
App.Collections.Persons = Backbone.Collection.extend({
model: App.Models.Person,
url: 'api/persons.json'
});
App.Collections.Orders = Backbone.Collection.extend({
model: App.Models.Order,
url: 'api/orders.json'
});
// !views.js
App.Views.Items = Backbone.View.extend({
el: '#items',
events: {
'click button.open-orders':'openOrders',
'click button.open-persons':'openPersons',
},
openOrders: function() {
this.remove();
this.unbind();
App.myRouter.navigate("orders", {trigger: true, replace: true});
console.log("openOrders");
},
openPersons: function() {
this.remove();
this.unbind();
App.myRouter.navigate("persons", {trigger: true, replace: true});
console.log("openPersons");
},
initialize: function() {
this.listenTo( this.collection, "change", this.render );
this.template = _.template( document.getElementById('itemsTemlate').innerHTML );
this.render();
this.$el;
},
getGroups : function(){
return _.groupBy(this.collection.toJSON(), 'category');
},
render: function() {
this.el.innerHTML = this.template({ data : this.getGroups() });
},
});
App.Views.Persons = Backbone.View.extend({
el: '#persons',
events: {
'click button.open-items':'openItems',
'click button.open-orders':'openOrders',
},
openItems: function() {
this.remove();
this.unbind();
App.myRouter.navigate("items", {trigger: true, replace: true});
},
openOrders: function() {
this.remove();
this.unbind();
App.myRouter.navigate("orders", {trigger: true, replace: true});
},
initialize: function() {
this.listenTo( this.collection, "change", this.render );
this.template = _.template( document.getElementById('personsTemlate').innerHTML );
this.render();
this.$el;
},
getGroups : function(){
return _.groupBy(this.collection.toJSON(), 'category');
},
render: function() {
this.el.innerHTML = this.template({ data : this.getGroups() });
},
});
App.Views.Orders = Backbone.View.extend({
el: '#orders',
events: {
'click button.open-items':'openItems',
'click button.open-persons':'openPersons',
},
openItems: function() {
this.remove();
this.unbind();
App.myRouter.navigate("items", {trigger: true, replace: true});
},
openPersons: function() {
this.remove();
this.unbind();
App.myRouter.navigate("persons", {trigger: true, replace: true});
},
initialize: function() {
this.listenTo( this.collection, "change", this.render );
this.template = _.template( document.getElementById('ordersTemlate').innerHTML );
this.render();
this.$el;
},
getGroups : function(){
return _.groupBy(this.collection.toJSON(), 'category');
},
render: function() {
this.el.innerHTML = this.template({ data : this.getGroups() });
},
});
// !router.js
App.Router = Backbone.Router.extend({
routes: {
'':'persons',
'persons':'persons',
'items':'items',
'orders':'orders'
},
persons: function() {
App.persons = new App.Collections.Persons;
App.persons.fetch().then(function() {
new App.Views.Persons({ collection: App.persons });
});
console.log('persons page !');
},
items: function() {
App.items = new App.Collections.Items;
App.items.fetch().then(function() {
new App.Views.Items({ collection: App.items });
});
console.log('items page !');
},
orders: function() {
App.orders = new App.Collections.Orders;
App.orders.fetch().then(function() {
new App.Views.Orders({ collection: App.orders });
});
console.log('orders page !');
},
});
App.myRouter = new App.Router();
Backbone.history.start();
</script>
</body>
</html>
Sounds like you could use an itemView. This is when marionette comes in very handy.
check out marionette
I'm at a loss as to what's going on here. I think it's because I'm loading the form later on in the view, but I'm honestly not sure. Here's my view.
define([
'jquery',
'underscore',
'backbone',
'serializeForm',
'backboneForms',
'text!templates/services/ServicesTemplate.html',
'models/ServiceModel',
'forms/NewServiceForm',
'text!templates/forms/ServiceFormTemplate.html',
'collections/RegionsCollection',
'collections/UsersCollection'
], function($, _, Backbone, serializeForm, backboneForms, ServicesTemplate, ServiceModel,
NewServiceForm, ServiceFormTemplate, RegionsCollection, UsersCollection){
// The form
var form;
// What's gonna be clicked
var clicked;
// Get the user's credentials
if($.cookie('UserInfo'))
var userCreds = JSON.parse($.cookie('UserInfo'));
var ServicesView = Backbone.View.extend({
el: '.body',
render: function() {
// Load everything
var servicesTemplate = _.template(ServicesTemplate);
this.$el.html(servicesTemplate);
},
events: {
'click .btn': 'loadForm',
'submit': 'addService'
},
loadForm: function(ev) {
// Save what was clicked
clicked = $(ev.target).html();
// Save the scope
var that = this;
// Create the regions collection
var regionsCollection = new RegionsCollection();
var serviceModel = new ServiceModel();
var serviceFormTemplate = _.template(ServiceFormTemplate);
// Create the form
form = new NewServiceForm({
template: serviceFormTemplate,
model: serviceModel
}).render();
$("#form").html(form.el);
$('.body').on('submit', 'form', function() {
alert( "submit firing" );
});
},
addService: function(ev) {
var errors = form.commit();
if(!errors) {
var newService = $(ev.currentTarget).serializeForm();
newService.cluster = clicked;
console.log(newService);
} else {
$.each(errors, function(key, value) {
$("[name='" + key + "']").closest(".control-group").addClass("error");
$("[name='" + key + "']").closest(".control-group").find(".text-error").html("<small class='control-group error'>" + value.message + "</small>");
});
}
return false;
}
});
return ServicesView;
});
I've tried to bind the event to just the form that gets generated and, as you can see above, I've tried just catching any submit event. Any help on this is greatly appreciated.
EDIT: Here's what my index page looks like
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Backbone Auth</title>
<?php include('includes/head_includes.php'); ?>
</head>
<body>
<div class="header"></div>
<div class="container body">
</div>
<?php include('includes/js_files.php'); ?>
</body>
</html>
And here's roughly what my form looks like. The .control-groups all get populated with fields using the backbone-forms extension.
<form id="addservice" accept-charset="UTF-8">
<div id="error" class="alert alert-error" style="display:none;"></div>
<fieldset>
<div class="control-group">
<div class="controls">
<label>Region</label>
<div class="text-error"></div>
<div data-editors="region"></div>
</div>
</div>
<div class="control-group">
<div class="controls">
<label>Stage</label>
<div class="text-error"></div>
<div data-editors="stage"></div>
</div>
</div>
<div class="control-group">
<div class="controls">
<label>Description</label>
<div class="text-error"></div>
<div data-editors="description"></div>
</div>
</div>
<div class="control-group">
<div class="controls">
<label>Primary Contact</label>
<div class="text-error"></div>
<div data-editors="contact"></div>
</div>
</div>
<input id="AddService" class="btn btn-primary" type="submit" name="submit" value="Add Service" />
</fieldset>
</form>
EDIT2: Here is my NewServiceForm
define([
'jquery',
'underscore',
'backbone',
'backboneForms'
], function($, _, Backbone, backboneForms){
var NewServiceForm = Backbone.Form.extend({
schema: {
region: {
type: 'Select',
title: 'Disaster Region',
options: [],
validators: [
'required'
]
},
stage: {
type: 'Select',
title: 'Stage',
options: [
{val: "", label: "Select One"},
{val: "Assessment", label: "Assessment"},
{val: "Planned", label: "Planned"},
{val: "Commenced", label: "Commenced"}
],
validators: [
'required'
]
},
description: {
type: 'TextArea',
title: 'Description',
editorAttrs: {
maxlength: 140
},
validators: [
'required'
]
},
contact: {
type: 'Select',
title: 'Primary Contact',
options: [],
validators: [
'required'
]
}
}
});
return NewServiceForm;
});
WOOP!! Finally figured this out! I went ahead and moved the rendering of the form into the render function of the view. So now render looks like this:
render: function() {
var serviceModel = new ServiceModel();
var serviceFormTemplate = _.template(ServiceFormTemplate);
// Create the form
form = new NewServiceForm({
template: serviceFormTemplate,
model: serviceModel
}).render();
// Load everything
var servicesTemplate = _.template(ServicesTemplate);
this.$el.html(servicesTemplate);
}
Everything else stays the same. And now it works!
I have some sort of ViewCollection which renders all sub views.
define(
['jquery', 'underscore', 'backbone', 'text!templates/main/layout.html', 'text!templates/main/index.html', 'views/security/login', 'views/security/registration'],
function($, _, Backbone, LayoutTemplate, ContentTemplate, LoginView, RegistrationView) {
var IndexView = Backbone.View.extend({
tagName: "div",
className: "container",
template: LayoutTemplate,
render: function() {
this.$el.html(LayoutTemplate);
this.$('div.content').html(ContentTemplate);
this.$('div.sidebar').append(new LoginView().render().el);
this.$('div.sidebar').append(new RegistrationView().render().el);
return this;
}
});
return IndexView;
});
This works quite good!
Unfortunatly I have noticed that for example the LoginView can't handle events anymore.
define(
['jquery', 'underscore', 'backbone', 'text!templates/security/login.html'],
function($, _, Backbone, LoginTemplate){
var LoginView = Backbone.View.extend({
tagName: "form",
className: "login well",
template: LoginTemplate,
events: {
"submit form.login": "submit"
},
render: function(){
this.$el.html(this.template);
return this;
},
submit: function(e){
e.preventDefault();
var credentials = {
'username': $(e.target[1]).val()
, 'password': $(e.target[2]).val()
};
console.log(JSON.stringify(credentials));
$.ajax({
url: '/session'
, type: 'POST'
, contentType: 'application/json'
, data: JSON.stringify(credentials)
, success: function() {
window.Router.navigate('node', { trigger: true });
}
, error: function(xhr, status, error) {
console.log([xhr, status, error]);
$(e.target[1]).closest('div.control-group').addClass('error');
$(e.target[2]).closest('div.control-group').addClass('error');
}
});
}
});
return LoginView;
});
Instead of sending an ajax call the browser tries to submit the form data url-encoded as GET request that is a sign that the view doesn't listen on any views anymore...
Is there a option how I can rebind the view events to the element?
login.html
<fieldset>
<legend>login</legend>
<div class="control-group">
<label class="control-label" for="_session_username">username:</label>
<div class="controls">
<input type="text" id="_session_username" name="session[username]" placeholder="george78" />
</div>
</div>
<div class="control-group">
<label class="control-label" for="_session_password">password:</label>
<div class="controls">
<input type="password" id="_session_password" name="session[password]" placeholder="••••••" />
</div>
</div>
<button type="submit" class="btn btn-primary">login</button>
</fieldset>
the form tags are defined in the view also the .login class attribute.
I found the solution.
We have a scoping problem. Because I defined the form directly in the view (tagName: "form") the topest object is "form.login". But this we can't address through the selector anymore. We only can select child objects. (See Backbone internals).
Before:
events: {
'keyup form.login input': 'checkInput'
, 'submit form.login': 'submit'
},
After:
events: {
'keyup input': 'checkInput'
, 'submit': 'submit'
},
window.User = Backbone.Model.extend({
defaults: {
name: 'Jane',
friends: []
},
urlRoot: "users",
initialize: function(){
this.fetch();
}
});
var HomeView = Backbone.View.extend({
el: '#container',
template: _.template($("#home-template").html()),
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
home: function() {
var user = new User({id: 1});
this.homeView = new HomeView({
model: user
});
this.homeView.render();
},
The model data is being queried and the root level attributes work fine, but the attribute that contains an array of other objects doesn't seem to show up.
Template:
<script id="home-template" type="text/template">
<div id="id">
<div class="name"><%= name %></div>
<br />
<h3> Single Friends</h3>
<br />
<ul data-role="listview" data-inset="true", data-filter="true">
<% _.each(friends, function(friend) { %>
<li>
<a href="/profile?id=<%= friend.id %>", data-ajax="false">
<div class="picture"><img src="http://graph.facebook.com/<%= friend.fb_user_id %>/picture"></div>
<div class="name"><%= friend.name %></div>
</a>
</li>
<% }); %>
</ul>
</div>
</script>
Return JSON:
{"name":"John Smith","friends":[{"id":"1234","name":"Joe Thompson","fb_user_id":"4564"},{"id":"1235","name":"Jane Doe","fb_user_id":"4564"}]}
It almost seems like it's not seeing the .friends attribute at all because it's taking the defaults of the model ([]).
Any suggestions?
You are calling render() before fetch() has returned the data from the server.
Try this?
window.User = Backbone.Model.extend({
defaults: {
name: 'Jane',
friends: []
},
urlRoot: "users"
});
var HomeView = Backbone.View.extend({
el: '#container',
template: _.template($("#home-template").html()),
initialize: function() {
this.model.fetch();
this.model.bind('change', this.render, this);
}
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});