I'm quite a noob to Backbone.js, but I love jQuery. However I love how Backbone organizes the code into models, views and collections but I still can't get my head around how I would use it when writing JS code.
For example take this simple code I wrote in jQuery that appends a suggestions box when a user types in an input box:
// this is the model
var questions = [
{question: "what is your name"},
{question: "How old are you"},
{question: "what is your mothers name"},
{question: "where do work/or study"}
];
// search data function
function searchData(datas,term){
return _.filter(datas, function(obj) {
return ~obj.question.toLowerCase().indexOf(term);
});
}
// Events
$("#suggestinput").on({
keyup: function(){
var results = searchData(questions, this.value);
$("#suggestions")
.addClass("active")
.html(_.reduce(results, function(last, q){
return last + "<p>" + q.question + "</p>";
}, ""));
},
blur: function () {
$("#suggestions").removeClass('active');
$("#suggestions").html(" ");
},
focus: function () {
if ( $(this).val() === "" ) {
$("#suggestions").addClass('active');
$("#suggestions").html("<p> start typing a question </p>");
}
}
});
Any advice on constructing a mini feature like this using the Backbone.js structure? I'm not saying to write the whole code, just a rough guideline or an explanation would be much appreciated.
There's also a working example of this code on JSFiddle: http://jsfiddle.net/X8geT/1/
Disclaimer: as any solution in Backbone, there are multiple ways to deal with a given problem, the following answer is probably completely overkill and should be taken with all due circumspection.
Define your models
They will represent your data and how it should be handled. Here I will define a Question model (empty, but you never know), a Questions collection (where filtering for a term is defined), and a search state controller responsible for storing the current term and the matching questions:
var Question = Backbone.Model.extend();
var Questions = Backbone.Collection.extend({
model: Question,
matches: function (term) {
if (term==="")
return [];
term = term.toLowerCase();
return this.filter(function(model) {
return model.get('question').toLowerCase().indexOf(term)!==-1
});
}
});
var QuestionController = Backbone.Model.extend({
defaults: {
term: ""
},
initialize: function(opts) {
this.questions = opts.questions; //known questions
this.matches = new Questions(); //filtered questions
//when the term changes, update the matches
this.on('change:term', function (model, term) {
this.matches.reset(this.questions.matches(term));
}, this);
}
});
Define your views
They will display the state of the models in the DOM and will handle interactions with the user. Let's have a SearchView that will handle the input and a SuggestionsView that will display the current suggestions:
var SearchView = Backbone.View.extend({
events: {
'keyup ': function (e) {
this.model.set('term', this.$el.val());
},
'focus ': function (e) {
this.trigger('start');
},
'blur ': function (e) {
this.trigger('stop');
}
}
});
var SuggestionsView = Backbone.View.extend({
initialize: function () {
_.bindAll(this, 'activate', 'deactivate', 'render');
this.listenTo(this.model.matches, 'reset', this.render);
},
activate: function () {
this.$el.addClass('active');
this.render();
},
deactivate: function () {
this.$el.removeClass('active');
this.$el.html(" ");
},
render: function () {
var html = '';
if (this.model.get("term") === "") {
html = "<p> start typing a question </p>";
} else {
if (this.model.matches.length === 0) {
html = "<p> No match </p>";
} else {
this.model.matches.each(function(model) {
html += '<p>'+model.get('question')+'</p>';
});
}
}
this.$el.html(html);
}
});
Bring models and views together
Instantiate, bind events and run things
var questions = new Questions([
{question: "what is your name"},
{question: "How old are you"},
{question: "what is your mothers name"},
{question: "where do work/or study"}
]);
var controller = new QuestionController({
questions: questions
});
var vSearch = new SearchView({
el: '#suggestinput',
model: controller
});
var vSuggestions=new SuggestionsView({
el: '#suggestions',
model: controller
});
vSuggestions.listenTo(vSearch, 'start', vSuggestions.activate);
vSuggestions.listenTo(vSearch, 'stop', vSuggestions.deactivate);
And a Fiddle to play with http://jsfiddle.net/nikoshr/QSE95/
Of course, you could greatly simplify this by having a single view handling all interactions, but where would be the fun?