Validate models with Backbone.js - javascript

with help of books and tutorials im trying to do my own Todo list app. My problem is that when you add ah todo my model should validate if the input come with some text before rendering. i created the validate methis but it doesnt work and i dont know how to track the error, i'll appreciate if you guys could help me finding my error
Model:
var Task = Backbone.Model.extend({
validate: function(attrs){
if(!attrs.title){
return "The task has no title";
}
}
});
var task = new Task;
Collection:
var Tasks = Backbone.Collection.extend({
model: Task
});
var tasks = new Tasks([
{
title: 'my task'
},
{
title: 'my task 1'
},
{
title: 'my task 2'
},
{
title: 'my task 3'
}
]);
Views:
var TaskView = Backbone.View.extend({
tagName: "li",
template: _.template( $('#task').html() ),
initialize: function(){
this.model.on("change", this.render, this);
},
render: function(){
var template = this.template( this.model.toJSON() );
this.$el.html( template );
return this;
},
events: {
'click .icon-checkbox': 'toggleState'
},
toggleState: function(e){
var $checkbox = $(e.target);
$checkbox.toggleClass('icon-checkbox-unchecked');
}
});
var TasksView = Backbone.View.extend({
el: '#tasks',
initialize: function(){
this.render();
this.collection.on('add', this.addOne, this);
},
render: function(){
this.collection.each(this.addOne, this);
return this;
},
addOne: function(task){
var taskView = new TaskView({ model: task });
this.$el.append( taskView.render().el );
}
});
var AddTask = Backbone.View.extend({
el: '#todos',
events:{
'click #add': 'addTask'
},
addTask: function(){
var taskTitle = $('#inputTask').val();
$('#inputTask').val("");
var task = new Task( {title: taskTitle} ); // create the task model
this.collection.add(task); //add the model to the collection
}
});
var tasksView = new TasksView( {collection: tasks} );
var addTask = new AddTask( {collection: tasks} );
Edit:
I just realized that the validate methos its on ly called when you set or save info in the model, but actually what i want to do is check when the user submit a todo that if it comes with text or ini order to create and render the model.

You never actually call the validate() method. Have you looked into the Backbone Validation plugin? Works pretty slick.
https://github.com/thedersen/backbone.validation

So i came up with this solution, I am not sure if this is a good practice or not or it the view should to this but what i did is: Once you push on the add task button the addTask methos first check if the input has text or not, if it doesnt have text it just only change the placeholder to: "Todo's can not be empty" and doesnt create a model and render its view. But, if the input has text it creates the model append it to the colelction and render the view!.
Here is the new code for the view, i hope someone could tellme if this is good or if the model should validate this or any other object should to this labor.
Code:
var AddTask = Backbone.View.extend({
el: '#todos',
events:{
'click #add': 'addTask'
},
addTask: function(){
var taskTitle = $('#inputTask').val();
$('#inputTask').val(""); //clear the input
if($.trim(taskTitle) === ''){
this.displayMessage("Todo's can not be empty");
}else{
var task = new Task( {title: taskTitle} ); // create the task model
this.collection.add(task); //add the model to the collection
}
},
displayMessage: function(msg){
$('#inputTask').attr("placeholder", msg);
}
});

Related

How do I get Backbone to render the subView properly?

I am relatively new to Backbone.js and having difficulty rendering a subView. I have subViews in other parts of the app working properly, but I cant even render simple text in this one.
View:
Feeduni.Views.UnifeedShow = Backbone.View.extend({
template: JST['unifeeds/show'],
tagName: "section",
className: "unifeed-show",
render: function() {
var content = this.template({ unifeed: this.model });
this.$el.html(content);
var subView;
var that = this;
this.model.stories().each(function(stories) {
subView = new Feeduni.Views.StoriesShow({ model: stories });
that.subViews.push(subView);
that.$el.find(".show-content").append(subView.render().$el);
});
return this;
},
});
Subview:
Feeduni.Views.StoriesShow = Backbone.View.extend({
template: JST['stories/show'],
tagName: "div",
className: 'stories-show',
render: function() {
this.$el.text("Nothing shows up here");
return this;
},
});
Model:
Feeduni.Models.Unifeed = Backbone.Model.extend({
urlRoot: "/api/uninews",
stories: function() {
this._stories = this._stories || new Feeduni.Subsets.StoriesSub([], {
parentCollection: Feeduni.all_unifeeds
});
return this._stories;
},
});
The text "Nothing shows up here" should be displaying in the "show content" element, but all I get is this:
<section class="unifeed-show">
<article class="show-content">
</article>
</section>
Below is a slight modification of your code showing a working main view managing some sub-views.
var UnifeedShow = Backbone.View.extend({
// I've hard-coded the template here just for a sample
template: _.template("Feed: <%= feedName %><br/> <ul class='show-content'></ul>"),
className: "unifeed-show",
initialize: function () {
// Create an array to store our sub-views
this.subViews = [];
},
render: function () {
var content = this.template(this.model.toJSON());
this.$el.html(content);
var subView;
var that = this;
var subViewContent = this.$el.find(".show-content");
this.model.stories().each(function (story) {
var subView = new StoryShow({
model: story
});
this.subViews.push(subView);
subViewContent.append(subView.render().$el);
}, this);
return this;
}
});
var StoryShow = Backbone.View.extend({
tagName: 'li',
// This template will show the title
template: _.template('Title: <%= title %>'),
className: 'stories-show',
render: function () {
var content = this.template(this.model.toJSON());
this.$el.html(content);
return this;
},
});
var Unifeed = Backbone.Model.extend({
stories: function () {
// I'm just returning the value set on this model as a collection;
// You may need to do something different.
return new Backbone.Collection(this.get('stories'));
}
});
// ================================
// Code below is creating the model & view, then rendering
// ================================
// Create our model
var feed = new Unifeed();
// Put some data in the model so we have something to show
feed.set('feedName', 'A Sample Feed');
feed.set('stories', [{
title: "Story #1",
id: 1
}, {
title: "Story #2",
id: 5
}]);
// Create our main view
var mainView = new UnifeedShow({
model: feed,
el: $('#main')
});
// Render it, which should render the sub-views
mainView.render();
Here's a working JSFiddle:
https://jsfiddle.net/pwagener/7o9k5d6j/7/
Note that while this manual sort of sub-view management works OK, you'll be better off using something like a Marionette LayoutView to help manage parent and sub-views. It builds good best practices for this sort of thing without you needing to do it yourself.
Have fun!
The subview is named Feeduni.Views.StoriesShow but in your main view you are instantiating new Feeduni.Views.StoryShow. Name them consistently and see if you still have problems.

Getting a Paragraph that changes with dropdown using Backbone.js

I've gotten my dropdown to be populate with lessons, but then I am stuck trying to figure out how I can get the text beneath my dropdown to change based on what the user selects by using Backbone.js
I am populating a select by adding options containing my Lessons and having the title show. Now I am just stuck on where should I insert the text so that it will change based on the selection.
Here is my HTML:
<script type="text/template" id="lesson-template">
<span class="lesson-title"><%= title %></span>
//How should I insert the text?
</script>
<script type="text/template" id="library-template">
<h1> Lesson Library </h1>
<select class="lessons"></select>
</script>
Here is my JSON file where I pull the information, I want to show the title now:
[{
"title": "Intro",
"text":"Do this now"
},
{
"title": "Second",
"text":"And then this"
}]
This is what is included in my javascript file:
window.Lesson = Backbone.Model.extend({});
window.Lessons = Backbone.Collection.extend({
model: Lesson,
url: './lessons.json'
});
window.library = new Lessons();
window.LessonView = Backbone.View.extend({
tagName: 'option',
className: 'lesson',
initialize: function() {
_.bindAll(this,'render');
this.model.bind('change',this.render);
this.template = _.template($('#lesson-template').html());
},
render: function() {
var renderedContent = this.template(this.model.toJSON());
$(this.el).html(renderedContent);
return this;
}
});
window.LibraryLessonView = LessonView.extend({
});
window.LibraryView = Backbone.View.extend({
tagName: 'section',
className: 'library',
initialize: function() {
_.bindAll(this, 'render');
this.template = _.template($('#library-template').html());
this.collection.bind('reset', this.render);
},
render: function() {
var $lessons,
collection = this.collection;
$(this.el).html(this.template({}));
$lessons = this.$('.lessons');
this.collection.each(function(lesson) {
var view = new LibraryLessonView({
model: lesson,
collection: collection
});
$lessons.append(view.render().el);
});
return this;
}
});
First give an id to each lesson.
window.LessonView = Backbone.View.extend({
...
render: function() {
var renderedContent = this.template(this.model.toJSON());
$(this.el).html(renderedContent);
// Then put the id as the option's value
$(this.el).val(this.model.get('id'));
return this;
}
...
});
window.LibraryView = Backbone.View.extend({
...
// bind the select's onchange event to this.onSelect
events: {
'change select': 'onSelect'
},
...
...
onSelect: function(e) {
// Grab the id of the select lesson
var lessonId = $(e.target).val();
// And get the lesson data back
var lesson = _.indexBy(this.collection.toJSON(), 'id')[lessonId];
// Then you could render you lesson view with something like this :
$('#target-container').html(
_.template($('#lesson-template').html(), lesson)
);
},
...
});
You could also go for a more elaborate views / collections construct but I'd suggest you get this working first.

Backbone Sorting and Updating a listview after an action

i am trying to make my first backbone app, and have run into a problem that i just cant solve..
I have a list of links, each link has a counter next to it,
when i click on a link i want the counter to increment by 1. (i have made this, and it is working)
Next i want the link i clicked to move up in the list IF the counter value is higher than the link above.
like this.
first link (4)
second link (3)
third link (3) <-- if i click on this link i want it to move up above second link.
I have tried using comparator and sortBy, but each time i try something i just cant seem to re-render the view and also have the link move up one spot.
I did manage to sort the list initially, when the main view is initialized.
But updating the view and list placement after i click one of the links i cant figure out how to accomplish.
my code:
(function() {
window.App = {
Models: {},
Collections: {},
Views: {}
};
window.template = function(id) {
return _.template( $('#' + id).html() );
};
//Modellen
App.Models.Task = Backbone.Model.extend({
defaults: {
name: 'Foo Bar Baz',
uri: 'http://www.google.com',
counter: 0
},
validate: function(attr) {
if ( ! $.trim(attr.name) ) {
return 'En opgave kræver en title.';
};
}
});
//Collection
App.Collections.Tasks = Backbone.Collection.extend({
model: App.Models.Task,
comparator: function(task) {
return task.get('counter');
},
});
//Singel view
App.Views.TaskView = Backbone.View.extend({
tagName: 'li',
template: template('Tasks'),
initialize: function() {
this.model.on('change', this.render, this);
this.model.on('destroy', this.remove, this);
},
events: {
'click .edit' : 'retTask',
'click .delete' : 'destroy',
'click .uriLink' : 'addCounter'
},
retTask: function() {
var newTaskNavn = prompt('Hvad skal det nye navn være', this.model.get('name'));
if ( !newTaskNavn ) return;
this.model.set('name', newTaskNavn);
},
destroy: function() {
this.model.destroy();
},
addCounter: function(e) {
e.preventDefault();
var newCounter = this.model.get('counter');
this.model.set('counter', newCounter + 1);
},
remove: function() {
this.$el.remove();
},
render: function() {
this.$el.html(this.template(this.model.toJSON()) );
return this;
}
});
//Collection View
App.Views.TasksView = Backbone.View.extend({
tagName: 'ul',
initialize: function() {
this.collection.on('add', this.addOne, this);
this.render();
},
render: function() {
this.collection.each(this.addOne, this);
return this;
},
addOne: function(task) {
var taskView = new App.Views.TaskView({ model: task });
this.$el.append(taskView.render().el);
}
});
App.Views.AddTask = Backbone.View.extend({
el: '#addTask',
initialize: function() {
},
events: {
'submit' : 'submit'
},
submit: function(e) {
e.preventDefault();
var taskNavn = $(e.currentTarget).find('.navnClass').val(),
uriNum = $(e.currentTarget).find('.uriClass').val();
if ( ! $.trim(taskNavn)) {
var test = prompt('opgaven skal have et navn', '');
if ( ! $.trim(test)) return false;
taskNavn = test;
}
if( uriNum.indexOf( "http://" ) == -1 ) {
addedValue = 'http://',
uriNum = addedValue + uriNum;
}
$(e.currentTarget).find('input[type=text]').val('').focus();
//var task = new App.Models.Task({ name: taskNavn, uri: uriNum });
this.collection.add({ name: taskNavn, uri: uriNum });
}
});
// new tasks collection
var tasks = new App.Collections.Tasks([
{
name: 'Foo',
uri: 'www.google.com',
counter: 3
},
{
name: 'Bar',
uri: 'http://google.com',
counter: 2
},
{
name: 'Baz',
uri: 'http://www.google.com',
counter: 1
}
]);
// tasks.comparator = function(task) {
// return task.get("counter");
// };
tasks.sort();
// new collection view (add)
var addTaskView = new App.Views.AddTask({ collection: tasks});
// new collection view
var tasksView = new App.Views.TasksView({ collection: tasks });
$('.tasks').html(tasksView.el);
})();
My HTML: (if someone wanna try to replicate the scenario :)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>LinkList</title>
</head>
<body>
<h1>Mine opgaver</h1>
<form action="" id="addTask">
<input class="navnClass" type="text" placeholder="Link name"><input clas s="uriClass" type="text" placeholder="www.url-here.com">
<button class="nyOpgave">Ny opgave</button><br />
</form>
<div class="tasks">
<script type="text/template" id="Tasks">
<span class="linkNavn"><%= name %></span> - <%= uri %> : [<span class="counterClass"><%= counter %></span>] <button class="edit">Edit</button> <button class="delete">Delete</button>
</script>
</div>
<script src="js/underscore.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>
<script src="js/jquery.js"></script>
<script src="js/backbone.js"></script>
<script src="main.js"></script>
</body>
</html>
can anyone please help me figure this one out ?
/Cheers
Marcel
Okay , i have created the application for you , as you have intended it to run.I'm going to try and explain you the entire code , what i have written and why i have written.
First , take a look at the JSfiddle : here
Next , let me explain :
1.This is my model that stores the name of the link , href , the id(not used in my example but its just good practise to assign a unique id to each model) and finally the number of clicks to a link(model).
var myModel = Backbone.Model.extend({
defaults:{
'id' : 0,
'name' : null,
'link' : '#',
'clicks' : 0
}
});
2.This the collection , that stores all my models , i have added a comparator function so that when ever you add a model to a collection , it will sort the collection.
Note : i have added a - sign to sort the collection in descending order of clicks (link with maximum click to appear first)
var myCollection = Backbone.Collection.extend({
model: myModel,
comparator: function(item) {
return -item.get('clicks');
}
});
3.Now this is my main view , what do i mean main view ? This view does the main rendering of the list , that you want to show.Pretty self explanatory code here.One thing , the this.coll.bind('add change',this.render,this) , i have added a 'change' because whenever any of the models in this collection change , we want to re-render the entire list , this happens when i change the count of any link , on clicking it , i want to re-render the entire list.
var myView = Backbone.View.extend({
el: $("#someElement"),
tagName : 'ul',
initialize: function() {
this.coll = new myCollection();
this.coll.bind('add change',this.render,this);
},
events: {
"click #add": "add"
},
add: function(e){
e.preventDefault();
var mod = new myModel();
var name = $('#name').val();
var link = $('#link').val();
mod.set({'id':mod.cid, 'name':name,'link':link});
this.coll.add(mod);
},
render : function(){
$('#list').empty();
this.coll.sort();
this.coll.forEach(function(model){
var listItem = new printView({ model: model});
$('#list').append(listItem.render().el);
});
}
});
4.This is my sub-view , why do i ever make a second view , why isnt 1 view sufficient ?
Well this consider a scenario, with every link you have a delete button (for instance) when i click the delete button (and i have just 1 view) how do i identify which model to destroy(remove from collection ? ) , 1 possible way would be to associate a cid with each model and then on click i can do a this.coll.getByCid() , but this isnt such a good way to do it , IMHO , so i created a separate view for each model.This View renders each model and returns nothing more.
var printView = Backbone.View.extend({
tagName: 'li',
initialize : function(options) {
_.bindAll(this, "render");
},
events:{
"click a": "count"
},
render:function(){
var linkName = this.model.get("name");
var link= this.model.get("link");
var clicks = this.model.get("clicks");
this.$el.append("<a class='link' href='"+link+"'>"+linkName+"</a> ("+clicks+")");
return this;
},
count:function(e){
e.preventDefault();
e.stopPropagation();
var clicks = this.model.get("clicks");
clicks++;
this.model.set({'clicks':clicks});
}
});
5.Initializing my (main) myView
new myView();
Note: I do believe that this application/code can be written in much better way , with several improvements but with my calibre and with the fact that it works ( :p ) i think it can help you.
The collection comparator is only executed when new models are added to the collection: it doesn't update the collection order when properties change. In order to achieve this, you need to call collection.sort():
App.Collections.Tasks = Backbone.Collection.extend({
model: App.Models.Task,
initialize: function() {
this.on('change:counter', this.sort);
},
comparator: function(task) {
return task.get('counter');
}
});
In the list view you can listen to the collection's sort event, and re-render your view:
App.Views.TasksView = Backbone.View.extend({
tagName: 'ul',
initialize: function() {
this.collection.on('add', this.addOne, this);
this.collection.on('sort', this.render, this);
this.render();
},
render: function() {
//if there are existing child views, remove them
if(this.taskViews) {
_.each(this.taskViews, function(view) {
view.remove();
});
}
this.taskViews = [];
this.collection.each(this.addOne, this);
return this;
},
addOne: function(task) {
var taskView = new App.Views.TaskView({ model: task });
this.$el.append(taskView.render().el);
//keep track of child views
this.taskViews.push(taskView);
}
});

Loading initial data in Backbone.js

I'm new to backbone.js and MVC so apologise if this is a silly question...
I have been experimenting with some of the backbone.js tutorials out there and am trying to work out how to load an initial set of data onto the page.
If anyone could point me in the right direction or show me the what I'm missing below, it would be greatly appreciated!
Thanks!
The code is below or at: http://jsfiddle.net/kiwi/kgVgY/1/
The HTML:
Add list item
The JS:
(function($) {
Backbone.sync = function(method, model, success, error) {
success();
}
var Item = Backbone.Model.extend({
defaults: {
createdOn: 'Date',
createdBy: 'Name'
}
});
var List = Backbone.Collection.extend({
model: Item
});
// ------------
// ItemView
// ------------
var ItemView = Backbone.View.extend({
tagName: 'li',
// name of tag to be created
events: {
'click span.delete': 'remove'
},
// `initialize()` now binds model change/removal to the corresponding handlers below.
initialize: function() {
_.bindAll(this, 'render', 'unrender', 'remove'); // every function that uses 'this' as the current object should be in here
this.model.bind('change', this.render);
this.model.bind('remove', this.unrender);
},
// `render()` now includes two extra `span`s corresponding to the actions swap and delete.
render: function() {
$(this.el).html('<span">' + this.model.get('planStartDate') + ' ' + this.model.get('planActivity') + '</span> <span class="delete">[delete]</span>');
return this; // for chainable calls, like .render().el
},
// `unrender()`: Makes Model remove itself from the DOM.
unrender: function() {
$(this.el).remove();
},
// `remove()`: We use the method `destroy()` to remove a model from its collection.
remove: function() {
this.model.destroy();
}
});
// ------------
// ListView
// ------------
var ListView = Backbone.View.extend({
el: $('body'),
// el attaches to existing element
events: {
'click button#add': 'addItem'
},
initialize: function() {
_.bindAll(this, 'render', 'addItem', 'appendItem'); // every function that uses 'this' as the current object should be in here
this.collection = new List();
this.collection.bind('add', this.appendItem); // collection event binder
this.render();
},
render: function() {
_(this.collection.models).each(function(item) { // in case collection is not empty
appendItem(item);
}, this);
},
addItem: function() {
var item = new Item();
var planStartDate = $('#planStartDate').val();
var planActivity = $('#planActivity').val();
item.set({
planStartDate: planStartDate,
planActivity: planActivity
});
this.collection.add(item);
},
appendItem: function(item) {
var itemView = new ItemView({
model: item
});
$('ul', this.el).append(itemView.render().el);
}
});
var listView = new ListView();
})(jQuery);
Thanks.
Here's the modified example: http://jsfiddle.net/kgVgY/2/
You create the collection first with the data you want
var list = new List([{
createdOn: 'Jan',
createdBy: 'John',
planStartDate: "dfd",
planActivity: "dfdfd"
}]);
and then pass the collection to the view you want
var listView = new ListView({collection: list});
That's about all you had wrong in this code. Few minor unrelated notes:
You were using _(this.collection.models).each. Backbone collections use underscore to expose all those functions on themselves, so that is equivalent to this.collection.each
You don't really need the "unrender" method on the ItemView but since you aren't using that I'm guessing you're using it for debugging.

In a Backbone app what is work of model, views and collections by convention

I am right now developing a dead simple application using backbonejs mvc javascript library.Just to show how simple it is here is the html
Sample Html
<div class="container">
<div>
<label>
Name:
<input id="txtName" type="text" title="Type your name please" /></label>
<button id="btnSubmit" type="button" value="Submit">
Submit</button>
</div>
<div id="names">
</div>
</div>
Operation
Here's all i want the application to do.
User Types a name(or some alphanumeric string) and clicks submit.
The value(which is what they call model i suppose) after validation will be sent to restful service.
Service will return the same string along with status of database save operation.
I am now confused as to where the click event will handled(is it in the model?), after that where should the render method be called?(is it in views). Below you will find the script i had managed up until now
Model.js
//store a global reference to model
window["model"] = Backbone.Model.extend({
defaults: { name: "Type your name"
},
validate: function () {
}
});
View.js
//store a global reference to view
window["view"] = Backbone.View.extend({});
nothing in the view to say :(
Application.js
//when every thing is ready kick of the application
$(document).ready(function () {
var modelInstance = new model();
});
so do i add click event to the button in application.js or model.js.which is the convention/best practice?
Will it be possible for me to render the names collection returned from service into #names and status of the database insertion into another div somewhere up above the container? Can the view render both views?
So, here is a disclaimer: If this is all your app is doing, then Backbone is way too much ceremony. When the app gets more complex with more moving parts, Backbone becomes pretty amazing.
That being said, I have created a jsFiddle to highlight how you might use Backbone for what you want to do: http://jsfiddle.net/BrianGenisio/CZwnk/4/
Basically, you have a Person model and a People collection to hold the Person models. You then have three views: NewPersonView which is the form for entering your data, which is responsible for adding new items to the People collection. Then you have a PeopleView view which is responsible for showing People collection, which will watch the collection and show any additions. Finally, is the PersonView which is responsible for showing an individual Person model.
When it all comes together, it looks a little something like this:
HTML:
<script id="new_person_template" type="text/template">
<label>
Name:
<input id="txtName" type="text" title="Type your name please" />
</label>
<button id="btnSubmit" type="button" value="Submit">Submit</button>
</script>
<script id="person_template" type="text/template">
Hello, my name is <%= name %>
</script>
<div id="container">
</div>
JavaScript
window.app = {};
window.app.Person = Backbone.Model.extend({
defaults: { name: "Type your name" },
validate: function () { }
});
window.app.People = Backbone.Collection.extend({
model: window.app.Person
});
window.app.NewPersonView = Backbone.View.extend({
template: _.template($("#new_person_template").html()),
initialize: function () {
_.bindAll(this, 'submit');
},
events:
{
"click #btnSubmit": "submit"
},
render: function () {
$(this.el).html(this.template());
return this;
},
submit: function (x, y, z) {
// If you want this to go up to the server and sync, do this instead:
// this.model.create({name: $("#txtName").val()});
// but since we don't really have a server right now, we'll just create and add
var person = new window.app.Person({name: $("#txtName").val()});
this.model.add(person);
}
});
window.app.PersonView = Backbone.View.extend({
tagname: "li",
template: _.template($("#person_template").html()),
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
window.app.PeopleView = Backbone.View.extend({
tagName: "ul",
initialize: function() {
_.bindAll(this, "render", "renderPerson");
this.model.bind("add", this.renderPerson);
},
render: function() {
console.log("rendering");
this.model.each(this.renderPerson);
return this;
},
renderPerson: function(person) {
var personView = new window.app.PersonView({model: person});
$(this.el).append(personView.render().el);
}
});
$(document).ready(function () {
var people = new window.app.People();
var newPersonView = new window.app.NewPersonView({model: people});
var peopleView = new window.app.PeopleView({model: people});
$("#container").html(newPersonView.render().el);
$("#container").append(peopleView.render().el);
});
i would do the click in the view
you would need 2 views in my eyes, 1 is an appview that holds your entire app
the other is just a view for each usermodel you render out in your list .
here is your user model
var User = Backbone.Model.extend({
defaults: {
id: 0,
name: 'no-name'
}
});
here is your collection
var Users = Backbone.Collection.extend({
model: User,
create : function(model, options) {
var coll = this;
options || (options = {});
if (!(model instanceof Backbone.Model)) {
model = new this.model(model, {collection: coll});
} else {
model.collection = coll;
}
var success = function(savedModel, resp) {
coll.add(savedModel);
if (options.success) options.success(nextModel, resp);
// fire success event
coll.trigger("savesuccess", savedModel);
};
var error = function(resp) {
if(options.error) options.error(resp);
// fire error event
coll.trigger("saveerror");
}
return model.save(null, {success : success, error : error});
}
});
so here is a possible user view
var UserRow = Backbone.View.extend({
tagName: "li",
initialize: function(){
_.bindAll(this, 'render');
this.model.bind('change', this.render);
},
className: "user",
render: function() {
var user = this.model;
$(this.el).html(user.get('name'));
return this;
}
});
here is your appView
var AppView = Backbone.View.extend({
el: 'body',
events: {
'click button#btnSubmit': 'addUser'
},
initialize: function(){
this.collection = new Users();
this.collection.bind('add', this.appendUser);
this.collection.bind('savesuccess', this.saveSuccess);
this.collection.bind('saveerror', this.saveError);
this.counter = 0;
this.render();
},
addUser: function(){
this.counter++;
var user = new User();
user.set({
id: user.get('id') + this.counter,
name: $('#txtName', this.el).val()
});
this.collection.add(user);
},
appendUser: function(user){
var userRow = new UserRow({
model: user
});
$('ul#names', this.el).append(userRow.render().el);
},
saveSuccess: function(user){
alert('successfully saved, you can append something to the AppView DOM to show that saving was successful ...');
},
saveError: function(){
alert('failed to save, you can append something to the AppView Dom to show that saving failed, or remove the item again ...');
}
});
here you initialize it all
var App = new AppView();
as you can see, rendering the items itself does not happen after saving it in the database, it is added to the collection, the appView binds to the add event of the collection and appends the new user to your list.
the CRUD server connection works automatically, due to using the create function on the collection.
the idea here is that you don't need to user backbone events, trigger an event from your collections successful save or error save, and let any view bind to that event to make something happen on the screen when save was a success or not.

Categories

Resources