-
-
Save dmitriz/3853693 to your computer and use it in GitHub Desktop.
| <!-- | |
| Web Application with Backbone: BEGINNER TUTORIAL | |
| Backbone.js is a very popular and light(weight) JavaScript library | |
| for building Web Applications. | |
| However, learning it I found frustratingly few simple examples of | |
| true workable applications with all lines of code explained. | |
| Here is my attempt to hack the popular Jerome Gravel-Niquet's Backbone Todo App | |
| down to a MVA (Minimum Workable App), and turn it into | |
| a Beginner Tutorial by adding long | |
| and tedious explanations for every single line of Code. | |
| When not broken, this App should offer Entry Field for your Item, | |
| where you can enter any Text (but please be careful with other characters). | |
| Pressing ENTER will add the Item (if not empty) to the List. | |
| Then hovering your mouse over a List Entry, | |
| you see a little gray cross image at the very right, | |
| which you can click to instantly delete the Item for good. | |
| Just before deleting as your mouse hovers over that image ready to click, | |
| it turns black (i.e. the image, not your mouse, hopefully) | |
| to give you the last chance to save the Item from its coming death. | |
| This is it, as far as the unsofisticated functionality goes. | |
| Yet remarkably, this involves quite a few useful things Backbone can offer | |
| to sweeten your life when attempting to write something more useful. | |
| So the primary goal of this Tutorial is to explain all those little features | |
| and hopefully help the reader to get a quick start, | |
| even if she is only vaguely familiar with all this JavaScript stuff. | |
| Since I've stripped this App to its absolute bare nacked minimum, | |
| including whatever styles are used for decoration purposes, | |
| this is probably the ugliest looking App you have ever seen. | |
| And just to relieve the reader's pain, she is welcome to add any of her | |
| favorite styles to make the App 'not so hard on the eyes'. | |
| Unfortunately, every beautiful page seems to have very 'unbeautiful' | |
| CSS code underneath, which would leave no hope to keep this tutorial | |
| (reasonably) short keeping all lines ferociously explained. | |
| You can copy the file either with 'Download Gist' or the '<>' Icon "View Raw", | |
| and view it with your Browser, where you see links to download 4 missing files. | |
| Once those are in the same directory, the App should become functional. | |
| I could provide many links to the terms discussed. | |
| However, the links are known to become outdated or die/break at moments of viewing. | |
| Thus it will better serve the reader simply to copy-paste them | |
| into her favorite Search Engine. | |
| Practice. Yes, you can only learn through practice! | |
| Which is why I included little paragraphs with homework. | |
| To do it, you need to access JavaScript Console provided | |
| either by Firebug Extension for Firefox, | |
| or natively in the View/Developer menu in Chrome, also available for other Browsers. | |
| Have Fun! | |
| --> | |
| <!DOCTYPE html> <!-- The (reasonably) new short HTML5 tag --> | |
| <html> | |
| <head> | |
| <title>Item Management App with Backbone</title> | |
| <!-- It is recommended to declare the charset even for English Web sites --> | |
| <meta charset='UTF-8'/> | |
| <style> | |
| /* To reduce the code to bare minimum we style only elements showing the Items, | |
| the only styling needed for the App to work correctly. | |
| These elements all appear inside the ordered list 'ol' with id='todo-list'. | |
| Every Item will be placed in its own 'li' element | |
| as child of <ol id='todo-list'>. | |
| Every 'li' element will contain a 'span' with class='todo-destroy' | |
| used to display a part of the image 'destroy.png'. | |
| The whole HTML structure here fits into the simple scheme: | |
| ol.#todo-list > li > span.todo-destroy in CSS style | |
| or as HTML: | |
| <ol id="todo-list"> | |
| <li> | |
| <div class='todo-content'><%= content %></div> | |
| <span class='todo-destroy'></span> | |
| </li> | |
| <li> | |
| ... | |
| </li> | |
| ... | |
| </ol> | |
| */ | |
| /* Newbie note. Even though 'style' tag can appear anywhere, | |
| it is recommended to place it inside the 'head', | |
| so it will be active before the 'body' gets processed. | |
| Otherwise the browser would show the unstyled elements first | |
| and reapply the new style to them after */ | |
| /* styling Elements <li> that are children of <ol id="todo-list"> */ | |
| #todo-list li { | |
| /* needed to allow positioning its children element relative to itself*/ | |
| position: relative; | |
| border-bottom: 1px solid gray; /* puts a gray line */ | |
| /* adds space 0.2 the size of letter 'm' at the top and bottom of the text */ | |
| padding: .2em 0; | |
| } | |
| /* styling Elements <span class='todo-destroy'> | |
| that are children of <ol id="todo-list">. | |
| These Elements will contain the 'destroy.png' image, | |
| appearing at the end of each line upon mouse hovering over it */ | |
| #todo-list .todo-destroy { | |
| /* we don't display the image initially, | |
| only upon mouse hovering -- to be declared below */ | |
| display: none; | |
| /* absolute positioning inside relative positioning | |
| allows to use the 2 positioning declarations below */ | |
| position: absolute; | |
| /* space between the right edges of this element and its parent */ | |
| right: 3px; | |
| /* space between the top edges of this element and its parent */ | |
| top: 3px; | |
| /* prescribing the width of the element corresponding | |
| to the horizontal size of the image, | |
| otherwise the image would repeat since declared as background */ | |
| width: 20px; | |
| /* prescribing the hight of the element, showing the upper half (gray) of the image */ | |
| height: 20px; | |
| /* declaring the image as background, should be in the same directory */ | |
| background: url(destroy.png); | |
| } | |
| /* Practice. Change the numbers above and see the effect in the browser */ | |
| /* declarations for the container with 'destroy.png' | |
| as shown upon mouse hovering over the parent 'li' element */ | |
| #todo-list li:hover .todo-destroy { | |
| /* now the element is displaying showing the upper half (gray) of the image */ | |
| display: block; | |
| } | |
| /* declarations for the same container when mouse hovers over it */ | |
| #todo-list .todo-destroy:hover { | |
| /* shifts the image 20 pixels up to display its lower half (black) */ | |
| background-position: 0 -20px; | |
| } | |
| </style> | |
| <!-- Loading external JavaScript libraries/frameworks in the right order, | |
| they have to be in the same directory | |
| and have to be loaded before our main JavaScript code. | |
| Minified versions can be used for faster loading and less bandwidth --> | |
| <!-- if you are reading that far, you've certainly heard about jQuery --> | |
| <script src='jquery.js'></script> | |
| <!-- lightweight utility library used by and to be loaded before backbone.js --> | |
| <script src='underscore.js'></script> | |
| <script src='backbone.js'></script> <!-- Backbone framework --> | |
| <script> // The main JavaScript Code | |
| /* We need to decide where to place our JavaScript Code. | |
| Since it refers to HTML elements of our document, | |
| we need to make sure the latter are loaded before. | |
| One way is to place the Code at the end of the 'body'. | |
| However, if there are large images or videos taking longer to load, | |
| our Code will have to wait leaving our page | |
| without functionality during the waiting time. | |
| A better solution is to place it in the 'head' but inside | |
| '$(function(){ ... })' or '$().ready(function() { ... })' | |
| or '$(document).ready(function() { ... }'. | |
| This uses the jQuery utility '.ready()' which executes the function enclosed | |
| only after the HTML of the 'body' (the DOM) is fully loaded. | |
| That means our JavaScript will wait for all needed HTML elements to be present | |
| but will not wait for images or videos to load. */ | |
| $(function() { | |
| /* We shall need to reference the RETURN key. | |
| It is a good practice to store it in a variable at the beginning of the Code, | |
| rather than 'hard-coding' it down inside the Code, | |
| so we can easily change it later if needed. */ | |
| /* Per convention, constant variables whose values | |
| are not changed are written using capitals. | |
| We save the key code of the RETURN key, | |
| which is pressed when the Item needs to be stored */ | |
| var RETURN_KEYCODE = 13; | |
| /* Newbie note. Allways use 'var' to declare your variables. */ | |
| /* We store Items as Backbone Models in a Backbone Collection. | |
| A Model is the simplest piece of data | |
| with a bunch of useful methods coming from Backbone. | |
| The data are stored as 'attributes'. | |
| Since our Item is a string, we only need one attribute for each Model. | |
| We need a list of Items, | |
| so it is convenient to organized them in a Backbone Collection, | |
| which is basically an array of Models again, | |
| with a bunch of useful methods coming from Backbone. | |
| As we add our Items dynamically, we start with empty Collection. | |
| (Note the convention that instances of Models, Collections and Views | |
| begin with small letters.) */ | |
| /* We intentionally remove 'var', | |
| so the Variable here will be available in the Console for practicing */ | |
| /* var */ itemCollection = new Backbone.Collection(); | |
| /* Practice. Type 'itemCollection' in your Console. Your should get an object. | |
| Then inspect its properties. These are set by Backbone. */ | |
| /* Next we need Views to display our Items, one View per Item. | |
| The Views will be similar, | |
| so there is no need to define Methods (Properties pointing to Functions) | |
| for each View separately. | |
| Instead Methods will be defined on the Prototype. | |
| In a nutshell, Prototype is a property of any Function 'F()' | |
| that can be used to store properties | |
| to be inherited by any intance defined via 'new F()'. | |
| However you don't need to worry about Prototypes | |
| as Backbone does it for your behind the scene. | |
| All you need to do is to Extend the plain Backbone.View constructor Function | |
| with your custom Properties and Methods. | |
| Then every time we need an instance say of a View, | |
| we get it using the 'new' operator with our constructor: | |
| view = new ItemView(options); | |
| */ | |
| /* var */ ItemView = Backbone.View.extend({ | |
| /* Every View creates an Element where it will be attached, | |
| referenced 'view.el' where 'view' is any Backbone view. | |
| Note that as long as this Element is not yet attached to our DOM, | |
| it is not visible in the Browser. */ | |
| // The 'tagName' property specifies the HTML tag name of that Element. | |
| tagName: 'li', | |
| /* Next we prepare and cache Template to display our Items. | |
| Our Template will be stored in the HTML 'script' Element | |
| <script id='item-template'> at the end of the file, | |
| see the long description below preceeding it. | |
| Using jQuery we extract Template's HTML content as $('#item-template').html() | |
| and then pass it to Underscore's _.template() utility which creates a Function. | |
| We reference this Function as 'itemTemplate'. | |
| Whenever we need HTML code displaying our Item, | |
| we simply evaluate this Function | |
| with data object {content: 'our content goes here'} passed to it as argument. | |
| For instance, | |
| itemTemplate({content: 'My Text'}) | |
| produces the code: | |
| <div class='todo-content'> My Text </div> | |
| <span class='todo-destroy'></span> */ | |
| /* Practice. In your Console type: | |
| myHTML = _.template($('#item-template').html()). | |
| You will get a Function. You can inspect its source by typing 'myHTML.source', | |
| this property is provided by Underscore for convenience. | |
| Now pass any value of 'content' to this function. For example, type: | |
| myHTML({content: 'HAHAHA!'}) | |
| You will see the resulting HTML with | |
| 'HAHAHA!' inserted in place of '<%= content %>'. | |
| Now use our cached Function 'itemTemplate' to achieve the same result. | |
| You need to create an instance of ItemView by typing | |
| 'myView = new ItemView()' or 'myView = new ItemView', | |
| then our Function will be accessed as 'myView.itemTemplate()' */ | |
| //compiling template function with Underscore's _.template() | |
| itemTemplate: _.template($('#item-template').html()), | |
| /* We next want to destroy our Item whenever User clicks on the 'destroy.png' image | |
| appearing at the end of the line displaying each Item, | |
| i.e. on the element <span class='todo-destroy'> | |
| that we included in Template. | |
| Backbone provides us with a simple way of doing it. | |
| Capture the 'click' Event and assign to it Event Handler Function | |
| that will do the needed work for us. | |
| This assignment can be done via Backbone's 'events' Property. | |
| The latter is an object, whose keys are our DOM (Document Object Model) Events | |
| (i.e. Events arising from User's interaction with the Document) | |
| and whose values are their Handling Functions. | |
| We only have one Event 'click .todo-destroy', | |
| to which we assign Function 'clear' to be defined later. | |
| You should not forget to define that Function or an error will be thrown. | |
| So every time User clicks on a DOM Element captured by CSS selector '.todo-destroy' | |
| (within the scope of the View), | |
| the Handling Function 'clear' is executed. | |
| 'Withing the scope of the View' means that our View | |
| only reacts to Events of its Element 'el' and its 'children'. | |
| Even though there are many Elements of class='todo-destroy' in our Document, | |
| one for each Item, only one will be used for each View. | |
| Note that these are DOM Events | |
| that should not be confused with Backbone's Built-in Events. | |
| See 'delegateEvents' in Backbone's Documentation for more details. */ | |
| events: { | |
| /* clicking any element with class="todo-destroy" | |
| leads to executing this.clear() function */ | |
| 'click .todo-destroy': 'clear' | |
| }, | |
| /* Practice. In your Console use ItemView constructor to instantiate new view: | |
| myView = new ItemView() | |
| Check events property: | |
| myView.events | |
| */ | |
| /* In our 'events' property above we introduced the Function 'clear' to be executed | |
| when Event 'click .todo-destroy' is triggered, | |
| i.e. when the User clicks on the Element with class='todo-destroy'. | |
| We need to define this Function */ | |
| clear: function() { | |
| // Backbone's Method: destroys Item's Model and removes it from Collection | |
| this.model.destroy(); | |
| // Backbone's Method: removes this View and its Element 'el' from the DOM | |
| this.remove(); | |
| }, | |
| /* In Backbone every View has 'render()' Property (or Method) | |
| that is originally void and is meant to be overridden. | |
| It is up to the programmer what this method does but typically it creates HTML | |
| shown by the View and refreshes it when needed. | |
| Note that if the View Element 'el' is not placed into our Document yet, | |
| it is not yet visible in Browser. */ | |
| render: function() { | |
| this.$el.html(this.itemTemplate(this.model.toJSON())); | |
| /* In our render() Method we use the standard Backbone's construction | |
| to update our View's Element. | |
| First note the usage of JavaScript's 'this' keyword, | |
| which is called Context and refers to the object, whose Method we are executing. | |
| Since 'render()' is our View's method, similar to the above Method 'itemTemplate()', | |
| its Context 'this' is the View itself. | |
| Note that here we are still defining here Constructor | |
| (from which our Views will inherit), not a View itself. | |
| That means, 'this' will be set at the execution time | |
| to be the current View at that time. | |
| For each Item (and its Backbone Model) we shall create its own View responsible | |
| to display that Model and to process its Events. | |
| For each View 'myView', we shall store the corresponding Model as 'myView.model'. | |
| This way every View "knows" where its Model is. | |
| However, it is a good practice to keep the Model "in the dark" about its View, | |
| i.e. not to put any referce on the Model to its View. | |
| The reason is that Model's are considered bare data | |
| that may be accessed by several Views. | |
| The data should not "know" anything about their presentation, | |
| which is what View's business is. | |
| Thus 'this.model' will refer to the Model corresponding to the current View 'this'. | |
| Next we use Backbone's Method 'toJSON()' available for every Model, | |
| which is somewhat confusing (see below) and, | |
| unfortunately, not very well explained in Backbone's Documentation. | |
| For better understanding, let us play with Backbone Models: | |
| Practice. In your Console create a Model: | |
| person = new Backbone.Model() | |
| Then set some attributes: | |
| person.set('name', 'Bob') | |
| person.set('age', 101) | |
| Now inspect properties of 'person'. | |
| You see the reference 'person.attributes' that shows the attributes we just set. | |
| Note that the attributes are not set on 'person' directly. | |
| That is, 'person.name' and 'person.age' are undefined. | |
| This is to keep them separate from Backbone's own properties | |
| that are available for every model. | |
| For instance, we are going to use 'toJSON()' as method of our Model, | |
| which would be lost if we set it to something else. | |
| That is why one should avoid setting properties directly | |
| on Models (or Collections, Views etc) | |
| but use the 'set()' Method above. | |
| Note that one could use 'person.attributes.age = 101' instead, | |
| which is not recommended however, | |
| as one e.g. can accidentally overwrite the whole 'person.attributes' hash that way. | |
| To get the value of an attribute, we use another Method, | |
| unsurprisingly called 'get()'. | |
| Practice. In your Console: | |
| person.get('name') | |
| person.get('age') | |
| Now let us use toJSON(): | |
| person.toJSON() | |
| You should get an Object with keys 'name' and 'age' | |
| and corresponding values 'Bob' and 101. | |
| According to Backbone's Documentation, 'model.toJSON()' | |
| returns a copy of model's attributes. | |
| It is however not a JSON (JavaScript Object Notation) string, | |
| which is a way to represent Objects in a string (i.e. to serialize them). | |
| (Note there is also native JavaScript global object 'JSON' | |
| containing Methods 'strigify()' and 'parse()'.) | |
| To get a JSON string representing Model's attributes, you can use | |
| JSON.stringify(person.attributes) or JSON.stringify(person.toJSON()), | |
| however, the best and simplest way is | |
| JSON.stringify(person). | |
| What happens here, is the native JavaScript function 'JSON.stringify()' checks | |
| whether its argument (in our case 'person') | |
| has Method 'toJSON()' defined for it and executes it if yes. | |
| Since 'person' is a Backbone Model, it is provided with 'toJSON()' Method. | |
| */ | |
| /* We don't need however to serialize our Model's attributes. | |
| Instead we pass the attribute Object as parameter to | |
| our previously defined Function 'this.itemTemplate()', discussed above, | |
| which returns the compiled HTML Code obtained from our Template. | |
| Finally, set the HTML obtained as the content of the View Element 'this.el'. | |
| This is done with jQuery. | |
| For our convenience, Backbone caches the needed jQuery Object as 'this.$el'. | |
| It only remains to set the HTML content via the Method 'html()', | |
| which is available for any jQuery Object. | |
| Practice. Assuming you set 'myView = new ItemView()' in your Console, | |
| compile its Template with your favorite HTML string, e.g. | |
| myHtml = myView.itemTemplate({content: "<h3>HA!</h3>"}). | |
| Then use jQuery to replace the content of any HTML Element with 'myHtml', e.g. | |
| $('h1').html(myHtml). | |
| Alternatively one could use the verbose JavaScript native functions | |
| 'getElementsByTagName' and 'innerHTML': | |
| document.getElementsByTagName('h1')[0].innerHTML = myHtml. | |
| */ | |
| /* It is recommended to end the definition of 'render()' | |
| by returning the Context, i.e. the View. | |
| This way we can use chain constructions like 'myView.render().el' etc. */ | |
| return this; // returns the current view for chaining | |
| } | |
| /* Practice. Let us test the defined 'render()' Method. | |
| We need to pass to 'myView' a model with 'content' attribute: | |
| myView.model = new Backbone.Model({content: "HA!"}). | |
| Execute 'myView.render()'. It should output our View with its 'el' property reset. | |
| Inspect 'myView.el.innerHTML' */ | |
| }); // end of ItemView | |
| /* Everything we did so far was related to a single Item. | |
| However, we need to generate the list of all Items. | |
| For this we define another View extension: */ | |
| // Again remove 'var' to access it for practice | |
| /* var */ CollectionView = Backbone.View.extend({ | |
| /* This time we shall only create one instance of this View. | |
| Therefore it will be convenient to set its Element directly | |
| by passing its CSS Selector to 'el': */ | |
| el: '#todoapp', | |
| /* Practice. In your Console instantiate another copy of CollectionView: | |
| cv1 = new CollectionView | |
| Its Element is automatically attached to DOM. Inspect this: | |
| cv1.el.innerHTML | |
| */ | |
| /* We next cache (save) our Input and Output Elements (wrapped as jQuery Objects). | |
| This way we don't need to run jQuery's search for this Element again, | |
| which has considerable performance advantage. | |
| */ | |
| // caching jQuery Object for the Input form | |
| input: $('#new-todo'), | |
| // caching jQuery Object for the Item list's 'ol' Element | |
| output: $('#todo-list'), | |
| /* Practice. In your Console check these properties for the View 'cv1' defined above | |
| and for 'cv' set by the Code: | |
| cv.input | |
| cv.output | |
| */ | |
| /* We need to watch for another Event, namely for User to press RETURN. | |
| This is taken care by the 'events' Property, as discussed above. */ | |
| events: { | |
| // binding 'press key' event with 'createOnEnter' Method | |
| 'keypress #new-todo': 'createOnEnter', | |
| }, | |
| // when executing here, last pressed character is passed as 'character' | |
| createOnEnter: function(key) { | |
| /* Note that we put 'key' as our Function's argument. | |
| This is because the 'keypress' Event passess the key pressed | |
| as argument to its Event handling Function */ | |
| // breaking execution unless RETURN was pressed | |
| if (key.keyCode != RETURN_KEYCODE) return; | |
| /* We use jQuery's 'val()' Function to retrieve the current value of our 'input' field | |
| and check it for being empty */ | |
| // breaking execution if the <input> field is empty | |
| if (!this.input.val()) return; | |
| /* Now we know that RETURN was pressed and 'val()' is not an empty string. | |
| This means we want to register a new Item, i.e. to create a new Model. | |
| It is conveniently done by using Backbone's 'add()' method | |
| provided for every Backbone Collection. | |
| Here we use it to add a new Model with attribute 'content' | |
| set to the value of our input field. | |
| */ | |
| // creating new Item in itemCollection | |
| itemCollection.add({content: this.input.val()}); | |
| /* Practice. Try to add a new Model manually in your Console: | |
| itemCollection.add({content: 'HAHAHA!!!'}) | |
| The Document should be automatically updated showing the new Model. | |
| This is because of the Event Listener defined below. | |
| */ | |
| /* Note that here, for simplicity, | |
| we insert whatever the User entered in the 'input' field | |
| directly into our Model without any validation. | |
| For instance, try to enter '<script>' as new Item to see the Error in your Console. | |
| Needless to say, one should never accept any data | |
| without validation in real life Applications. | |
| E.g. if Item name should only contain letters and numbers, | |
| one can remove all other characters using Regular Expressions: | |
| this.input.val().replace(/\W+/g,'') | |
| */ | |
| /* Once the new Item is Entered, we want to clear the 'input' field for new Items: */ | |
| this.input.val(''); | |
| }, // end of 'createOnEnter' | |
| /* For every Backbone Model, Collection, and View, we can define | |
| 'initialize()' Method, which is automatically executed each time the Model, | |
| Collection or View is instantiated (created). | |
| In case of CollectionView that we are defining now, | |
| this Function is only executed once at the beginning.*/ | |
| initialize: function() { // runs upon instantiating the view | |
| /* For User's convenience, we set focus to our 'input' Element using jQuery: */ | |
| this.input.focus(); // jquery focus method to set focus | |
| /* Now comes one of the most important Backbone's features -- | |
| refreshing the View upon Collection changes. | |
| This is done using Backbone's new Event Listener Method 'listenTo()', | |
| which is available for every Backbone Model, Collection, or View. | |
| Here our View 'this' listens to our main Collection 'itemCollection', | |
| more specifically to its 'add' Event. | |
| The latter is triggered whenever a Model is added to it. | |
| When this happens, 'this.addOne' Function is executed. | |
| What is the last 'this' for? It is the Context of 'addOne()'. | |
| That means, if 'addOne()' executes as result of this Event Listener, | |
| its 'this' Object will be the same as 'this' for the current View, | |
| i.e. the View itself. | |
| (In old Backbone tutorials one can see | |
| Underscore's utility '_.bindAll' used for this purpose. | |
| It is not needed here anymore, since 'this' can be simply passed as 4th argument.) | |
| Note that 'listenTo()' is only defined in Backbone 0.9.9 and above. | |
| */ | |
| this.listenTo(itemCollection, 'add', this.addOne, this); | |
| }, // end of 'initialize' | |
| /* Finally we need to define 'addOne()'. | |
| Since it is triggered by the 'add' Backbone Built-in Event, | |
| it is passed the arguments 'model', 'collection', 'options' | |
| (see Backbone's Documentation) | |
| Here we only use the first argument and call it 'item'. | |
| Thus, inside our Function, 'item' points to the Model added to 'itemCollection'. */ | |
| /* adding single Item by creating itemView for it, | |
| and appending its element to the `<ul>` */ | |
| addOne: function(item) { | |
| /* We have just added new Model 'item' representing our Item. | |
| However, our Document has not been updated as of yet. | |
| In order to show the new Item, | |
| we create a new View using the constructor 'ItemView' defined before, | |
| to which we pass our new Model as 'model' property. | |
| This 'model' will then be used inside 'itemTemplate' */ | |
| // create View for the new Model | |
| var itemView = new ItemView({model: item}); | |
| /* The View is created but we still need to run its 'render()' Method | |
| and to add its 'el' to the Document. | |
| Remember that our 'render()' returns the View itself? | |
| That feature is used now, where we "chain" 'el' after applying 'render()' | |
| and finally place 'el' at the top of our cached 'output' Element. | |
| Instead we could run 'itemView.render()' and then | |
| use 'itemView.el' in another Code line but chaining saved us one line. */ | |
| // placing the View Element with jQuery at the top of the 'output' Element | |
| this.output.prepend(itemView.render().el); | |
| /* Instead of 'prepend()' we could use jQuery's 'append()' | |
| to place the new Element at the bottom. | |
| Practice. In your Console use the view 'cv' defined | |
| by the Code to prepend/append your favorite HTML strings: | |
| cv.output.prepend('<h1>Ha</h1>') | |
| cv.output.append('<h1>Ha</h1>') | |
| */ | |
| } // end of 'addOne' | |
| }); // end of 'CollectionView' | |
| /* It remains to start the App by instantiating a CollectionView, | |
| which is saved as 'cv' to make it accessible at your Console for practicing: */ | |
| cv = new CollectionView(); // | |
| }); // end of '$(function(){})' | |
| </script> | |
| </head> | |
| <body> | |
| <h1>Item Management with <a href="http://backbonejs.org/">Backbone</a> | |
| </h1> | |
| <h2>Hacked from the <a href="http://jgn.me/">Jerome Gravel-Niquet's</a> | |
| <a href="http://documentcloud.github.com/backbone/docs/todos.html"> | |
| Backbone todo application</a> | |
| </h2> | |
| <p> | |
| It this App is 'dead'/unresponsive, likely you did not put the files | |
| <a href="http://code.jquery.com/jquery.js">jquery.js</a>, | |
| <a href="http://underscorejs.org/underscore.js">underscore.js</a>, | |
| <a href="http://backbonejs.org/backbone.js">backbone.js</a> and | |
| <a href="http://www.maths.tcd.ie/~zaitsev/app/destroy.png">destroy.png</a> | |
| in the same directory. | |
| </p> | |
| <!-- The main App container --> | |
| <div id='todoapp'> | |
| <!-- Input field for entering Item name --> | |
| <input id='new-todo' placeholder='New Entry?' type='text' /> | |
| <!-- Ordered List Container, where Items will show up --> | |
| <ol id='todo-list'></ol> | |
| </div> | |
| <!-- To display each Item, we need a piece of HTML code. | |
| This code is the same except for the name of the Item, | |
| which we store in the variable 'content'. | |
| A convenient way of handling this is to use a Template. | |
| We use the Underscore.js template engine. | |
| According to its syntax, we wrap our variable 'content' as <%= content %>, | |
| so the template engine will evaluate it | |
| and replace that piece with the value of 'content'. | |
| Alternatively we could use <%- content %> (or <%-content%>) | |
| to escape the value of 'content'. | |
| One can also write any piece of JavaScript bewteen <% and %> that will be executed. | |
| See the Documentation of Underscore.js for more details --> | |
| <!-- It is convenient to place our template | |
| inside a 'script' tag with attribute type='text/template', | |
| to make sure it won't be executed by JavaScript. | |
| Placing templates at the end of the 'body' | |
| will not block loading any preceeding Element, | |
| but it will still be available for our JavaScript Code enclosed | |
| with the '$(document).ready()' utility, see above --> | |
| <!-- Item View Template, will be compiled with '<%= content %>' | |
| replaced by the value of variable 'content' --> | |
| <script type='text/template' id='item-template'> | |
| <div class='todo-content'><%= content %></div> | |
| <span class='todo-destroy'></span> | |
| </script> | |
| </body> | |
| </html> |
Hi there! Could you talk me through the differences between the original implementation (or say our TodoMVC implementation) and your version? From what I can tell, you're opting for more readable variable names with a greater level of commenting.
Hi Addy!
I am very sorry I've noticed only now your request. Somehow I could not see how to activate email notifications for these comments.
The main difference of this version is that it has very short code in all parts: html, css and js.
Surely, at the price of sacrificing the look and functionality, but it is still a working app!
I've chosen only two basic functions: enter todo and delete it,
and left only the code responsible for them.
The html/css is also reduced to very minimum with explanation.
I've thought this might be helpful for a beginner to get started,
and then move on to look at the more complete but longer code in other versions.
The goal is to build a working app with minimum possible code starting from scratch.
The App can be enjoyed in http://www.maths.tcd.ie/~zaitsev/app/html/todo-backbone-minimized.html and the source found in http://www.maths.tcd.ie/~zaitsev/app/
Learning Backbone.js I found frustratingly few simple examples of true workable applications with all lines of code easily understood. Here is my attempt to hack the popular Jerome Gravel-Niquet's Backbone Todo App down to a MVA (Minimum Workable App).