This is a simple app that we'll build together at CharlotteJS meetup on June 20, 2013. The library app uses localStorage for our store.
Bit.ly URL to this repo: http://bit.ly/cjs-backbone
If you want to work off of the build-out
branch to try and build the app yourself, here are steps you'll need to follow for each piece. Consult the Backbone Docs if you need refreshers on some of the methods.
- Make sure new model instances of default values:
title
Stringauthor
Stringimg
String Can useplaceholder.gif
completed
Boolean
- Create a
toggleStatus
method- This method should toggle the value of the
completed
property and usesave
to save your method. - Your
save
call should also include asuccess
callback handler that triggers thevisible
event on the model.
- This method should toggle the value of the
- Create your collection class using
Book
for your modelBook
is the variable passed in to our module definition using RequireJS. This is a reference to the new model class we created in the first step.
- Set up a new store, since this is just specific to our storage here is the code you will need to use:
localStorage: new Store( "anidentifier" )
- This just sets up our collection to use Backbone.localStorage
- Set up your
comparator
property to sort the collection bycompleted
.
- Use an
li
as the view element and make sure its class attribute containsbook span4
- Create your template property and use Underscore's
template
method usingtpl
as the content.tpl
is a variable defined in our module definition. This is a reference to our template in/templates/book.html
if you want to reference the code.
- Set up your events object to manage the view's DOM events. The list below is in an "event selector : callbackName" format.
"click .togglestatus" : "toggleStatus"
"click .destroy": "removeBook"
- Create your
initialize
method and inside set up listeners for model events. The list below is in "modelevent : methodName" format.change : render
destroy : remove
visible : toggleVisible
- Create your
render
method. In this method you'll need to use your template method to hand off your model's attributes and append them to the view element. - Create a
toggleStatus
method ( it should accept one argument for the event object )- First off,
preventDefault
on the event (if there was a fallback URL we want to make sure we prevent it from loading). - Now, call our model's
toggleStatus
method.
- First off,
- Create a
removeBook
method. In this method, we simply need to destroy the model.
- Use the existing
.bookshelf
element for your view element - Create your
initialize
method and set up your collection event listeners and fetch the collection ( remember to pass{ reset: true }
tofetch
so that thereset
event fires )- Set up your collection event listeners (list is in "collectionevent : methodName" format):
add : render
reset : addBooks
- Call
fetch
on your collection
- Set up your collection event listeners (list is in "collectionevent : methodName" format):
- Create your
render
method (hint: you'll need to passmodel
in as a parameter)- Inside of
render
create a newBookView
instance and render it.BookView
is a variable defined in our module definition that references our model view that we created.
- Append your new view instance element to the collection view element.
- Inside of
- Create an
addBooks
method- In this method, you'll need to empty the current view element's HTML
- You'll also need to utilize one of Underscore's proxied methods to iterate over our collection list. On each model in the collection, call our
render
method. (hint: if you useforEach
don't forget to pass yourthis
context)
- Use the existing
.app
element for your view element - Set up your events object to manage the view's DOM events. The list below is in an "event selector : callbackName" format.
"click a" : "filterBooks"
"click li.add a" : "showForm"
submit: "createBook"
- Create your
initialize
method and do the following:- Go ahead and set up a couple cached jQuery objects that you can use throughout your view:
this.$filterBar
- Should cache a jQuery collection for.nav-pills
.this.$addForm
- Should cache a jQuery collection for.addForm
.- Remember, you can use
$el
to execute scoped jQuery object methods.
- Set up a collection event listener that will listen for the
filter
event and call ourfilterBooks
method.
- Go ahead and set up a couple cached jQuery objects that you can use throughout your view:
render
is already created and filled out. This will keep track of our filter bar state as we step through the app to ensure that it matches current state.- Create the
filterBooks
method and do the following:- Use one of Underscore's proxied methods to iterate over the collection. On each model, trigger the
visible
event (hint: don't forget to pass yourthis
context if you useforEach
) - At the end of your method, call
render
(to make sure our filter bar is in active state)
- Use one of Underscore's proxied methods to iterate over the collection. On each model, trigger the
- Create the
createBook
method that will be used when the "Add Book" form is submitted. Do the following in your function body:- First off prevent the default form submission.
- Then use
create
convenience function to create a new model instance. To get the object of attributes to use for your new instance, you can callthis.newAttrs()
which is set up already for you. - On the very last line of your method, call
this.$addForm.modal( "hide" )
. This is simply to hide our add a book form.
- There are a couple more methods that are already in the app view file, here's a brief explanation of both:
showForm
- This is just going to show our modal to add a new book when the "Add a book" link is clicked.newAttrs
- This is a convenience function to get all the form data being submitted in our new book form.
Now we want to set up our Application constructor. The constructor function is already defined in the file, we just need to create a few properties that each instance of our application will share.
Below where the steps mention to create a new instance, the variable reference is what we've defined in our RequireJS definition function. These names relate to the collection and views that we've required as a dependency in our app.
- Create a
this.collections.books
property and create a newBooks
collection instance. - Create a
this.views.books
property and create a newBooksView
view instance (don't forget to pass our new collection above to our new view instance). - Create a
this.views.app
property and create a newAppView
view instance (don't forget to pass our new collection above to our new view instance). - Create a
this.common.bookFilter
property and assign it the value of an empty string for now. This is what we'll use to keep track of filter state across our app. - The last step is inside of the piece that is already filled out for you about localStorage. Inside that snippet you'll see
"youridentifier"
. Just replace that string with whatever identifier name you chose when creating your new store on your collection.
- Create your Router class.
- Add a routes object that has 3 routes:
""
that will be used as our main route that should callmain
":type(/)"
that will be used for our filter and should callsetFilter
"*path"
to see how we can use a catch all route.
- Create our methods that we'll use when the routes above are matched:
main
- Set
window.library.common.bookFilter
to an empty string.- You may be wondering where
window.library
comes from. We'll define that in our next step, but it simply refers to the newApp
instance that we create. We'll store it as a global so that we can reference it throughout our application.
- You may be wondering where
- Trigger
filter
on the collection (you'll access the collection atwindow.library.collections.books
)
- Set
setFilter
- This method should accept a parameter that matches what our route matched.
- Set
window.library.common.bookFilter
to equal the argument passed in to the method. - Trigger
filter
on the collection (you'll access the collection atwindow.library.collections.books
)
catchAll
- This method is just to show what the catch-all route path looks like when using a *splat.
console.log
the path argument that gets passed in to the method
- This method is just to show what the catch-all route path looks like when using a *splat.
Down to the last step and just a few things to do here. At the top of this file is the RequireJS configuration for our app. You'll want to scroll down to where you see the comment // Kick off our app
and that's where you'll do your work. The snippet below shows where your code will go.
// Kick off our app
require( [
"app",
"router"
],
function( App, Router ) {
// YOUR CODE WILL GO HERE
});
There are 3 final steps that we need to do:
- Create a new
App
instance onwindow.library
- Create a new
Router
instance onwindow.library.router
- Tell Backbone.History to
start
watching for changes.
You should now be able to load your app in a browser and have a fully functional app that can add new books, delete books, and toggle the read status.
That doesn't mean you have to be done though! Extend the app more. Maybe enable the books to be edited? Perhaps drag-and-drop ordering? You're in control now. This could be the greatest library (you know one that catalogs books) that anyone has ever seen.