diff --git a/CNAME b/CNAME new file mode 100644 index 00000000..0c9de916 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +backbonetutorials.com diff --git a/README.md b/README.md new file mode 100644 index 00000000..2229aeb4 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ + +# Backbone Tutorials + +This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome. +About Backbone Tutorials + +As single page apps and large scale javascript applications become more prominent on the web, useful resources for those developers who are jumping the ship are crucial + +I started this site to not only consolidate my understanding of backbone.js but to also document what I have learned thus far for myself and others. + +Thomas Davis - [@neutralthoughts](http://twitter.com/neutralthoughts) - Twitter + +## Contributions + +* Thanks to Cactus([https://github.com/cactus](https://github.com/cactus)) for creating the blog feed + +## About the author + +Looking for feedback on my latest startup Protosal(http://protosal.com) any and all would be great! + +**Contact:** + +* \#cdnjs on freenode +* @neutralthoughts on twitter +* thomasalwyndavis@gmail.com + +**Projects:** + +* Javascript Library CDN - http://cdnjs.com +* Proposal Generation Start up - http://protosal.com +* Backbone.js Tutorials - http://backbonetutorials.com +* Technical Blog - http://thomasdavis.github.com +* Github Account - https://github.com/thomasdavis +* Freelance Blog - http://thomasalwyndavis.com +* Quora - http://www.quora.com/Thomas-Davis +* StackOverflow - http://stackoverflow.com/users/580675/thomas-davis + +Love you mum! diff --git a/_config.yml b/_config.yml new file mode 100644 index 00000000..021f688d --- /dev/null +++ b/_config.yml @@ -0,0 +1,3 @@ +markdown: rdiscount +pygments: true +permalink: /:title diff --git a/_layouts/default.html b/_layouts/default.html new file mode 100644 index 00000000..7fea996c --- /dev/null +++ b/_layouts/default.html @@ -0,0 +1,104 @@ + + +
+ + +This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+As single page apps and large scale javascript applications become more prominent on the web, useful resources for those developers who are jumping the ship are crucial.
+I started this site to not only consolidate my understanding of backbone.js but to also document what I have learned thus far for myself and others.
+You can contact the original owner of this site through github or via twitter(@neutralthoughts). You can also find me at #cdnjs on freenode.net.
+ + + +This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
++
#cdnjs on irc.freenode.net, don’t forget to follow me on twitter.
This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+You can contact the original owner of this site through github or via twitter(@neutralthoughts).
+ + + +This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+The main goal behind BackboneTutorials.com is to build a comprehensive guide for developers of all levels of experience. Discussion and debate are encouraged and will hopefully breed innovation for javascript applications.
+All contributions will be well acknowledged on the site.
+ + + +This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+Generate proposals for your clients with ease using predefined templates. Track how often your proposals get accepted or declined and who your best client are.
+CouchDb and Node.js Backend
+Flow is a task management app that makes working with your team a breeze. You can see the internals under window.Flow.
+ + + +This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+As single page apps and large scale javascript applications become more prominent on the web, useful resources for those developers who are jumping the ship are crucial.
+I started this site to not only consolidate my understanding of backbone.js but to also document what I have learned thus far for myself and others.
+ + + +This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+You can contact the original owner of this site through github or via twitter(@neutralthoughts).
+ + + +This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+The main goal behind BackboneTutorials.com is to build a comprehensive guide for developers of all levels of experience. Discussion and debate are encouraged and will hopefully breed innovation for javascript applications.
+All contributions will be well acknowledge on the site.
+ + + +This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+Across the internet the definition of MVC is so diluted that it’s hard to tell what exactly your model should be doing. The authors of backbone.js have quite a clear definition of what they believe the model represents in backbone.js.
+++Models are the heart of any JavaScript application, containing the interactive data as well as a large part of the logic surrounding it: conversions, validations, computed properties, and access control.
+
So for the purpose of the tutorial let’s create a model.
+ Person = Backbone.Model.extend({
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person;
+
+So initialize() is triggered whenever you create a new instance of a model( models, collections and views work the same way ). You don’t have to include it in your model declaration but you will find yourself using it more often than not.
+Now we want to pass some parameters when we create an instance of our model.
+ Person = Backbone.Model.extend({
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67});
+ delete person;
+ // or we can set afterwards, these operations are equivelent
+ var person = new Person();
+ person.set({ name: "Thomas", age: 67});
+
+
+So passing a javascript object to our constructor is the same as calling model.set(). Now that these models have attributes set we need to be able to retrieve them.
+Using the model.get() method we can access model properties at anytime.
+ Person = Backbone.Model.extend({
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+
+ var age = person.get("age"); // 67
+ var name = person.get("name"); // "Thomas"
+ var children = person.get("children"); // ['Ryan']
+
+
+Sometimes you will want your model to contain default values. This can easily be accomplished by setting a property name ‘defaults’ in your model declaration.
+ Person = Backbone.Model.extend({
+ defaults: {
+ name: 'Fetus',
+ age: 0,
+ children: []
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+
+ var age = person.get("age"); // 67
+ var name = person.get("name"); // "Thomas"
+ var children = person.get("children"); // ['Ryan']
+
+
+Models can contain as many custom methods as you like to manipulate attributes. By default all methods are public.
+ Person = Backbone.Model.extend({
+ defaults: {
+ name: 'Fetus',
+ age: 0,
+ children: []
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ },
+ adopt: function( newChildsName ){
+ var children_array = this.get("children");
+ children_array.push( newChildsName );
+ this.set({ children: children_array });
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+ person.adopt('John Resig');
+ var children = person.get("children"); // ['Ryan', 'John Resig']
+
+
+So we can implement methods to get/set and perform other calculations using attributes from our model at any time.
+Now onto one of the more useful parts of using a library such as backbone. All attributes of a model can have listeners bound to them to detect changes to their values. In our initialize function we are going to bind a function call everytime we change the value of our attribute. In this case every time we adopt a new kid we are going to throw a party.
+ Person = Backbone.Model.extend({
+ defaults: {
+ name: 'Fetus',
+ age: 0,
+ children: []
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ this.bind("change:children", function(){
+ alert("More children, single parenting is great!");
+ var children = this.get("children"); // ['Ryan', 'Stewie Griffin']
+ });
+ },
+ adopt: function( newChildsName ){
+ var children_array = this.get("children");
+ children_array.push( newChildsName );
+ this.set({ children: children_array });
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+ person.adopt('Stewie Griffin'); // This triggers a change and will alert()
+
+
+So we can bind the a change listener to individual attributes or if we like simply ‘this.bind(“change”, function(){});’ to listen for changes to all attributes of the model.
+Models actually have to be a part of a collection for requests to the server to work by default. This tutorial is more of a focus on individual models. Check back soon for a tutorial on collection implementation.
+Get all the current attributes
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+ var attributes = person.toJSON(); // { name: "Thomas", age: 67, children: ['Ryan']}
+ /* This simply returns a copy of the current attributes. */
+ delete attributes;
+ var attributes = person.attributes;
+ /* The line above gives a direct reference to the attributes and you should be careful when playing with it. Best practise would suggest that you use .set() to edit attributes of a model to take advantage of backbone listeners. */
+
+Validate data before you set or save it
+ Person = Backbone.Model.extend({
+ // If you return a string from the validate function,
+ // Backbone will throw an error
+ validate: function( attributes ){
+ if( attributes.age < 0 && attributes.name != "Dr Manhatten" ){
+ return "You can't be negative years old";
+ }
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ this.bind("error", function(model, error){
+ // We have received an error, log it, alert it or forget it :)
+ alert( error );
+ });
+ }
+ });
+
+ var person = new Person;
+ person.set({ name: "Mary Poppins", age: -1 });
+ // Will trigger an alert outputting the error
+ delete person;
+
+ var person = new Person;
+ person.set({ name: "Dr Manhatten", age: -1 });
+ // God have mercy on our souls
+
+
+This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+Backbone views are used reflect what your applications data model looks like. They are also used to listen to events and react accordingly. This tutorial will not be addressing binding models/collections with views. This tutorial will focus on view functionality and how to use it with a javascript templating library.
+We will be using jQuery 1.5.1 as our DOM manipulator.
+ Person = Backbone.Model.extend({
+ initialize: function(){
+ alert("Welcome to this world");
+ } /ch
+ });
+
+ var person = new Person;
+
+So initialize() is triggered whenever you create a new instance of a model( models, collections and views work the same way ). You don’t have to include it in your model declaration but you will find yourself using it more often than not.
+Now we want to pass some parameters when we create an instance of our model.
+ Person = Backbone.Model.extend({
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67});
+ delete person;
+ // or we can set afterwards, these operations are equivelent
+ var person = new Person();
+ person.set({ name: "Thomas", age: 67});
+
+
+So passing a javascript object to our constructor is the same as calling model.set(). Now that these models have attributes set we need to be able to retrieve them.
+Using the model.get() method we can access model properties at anytime.
+ Person = Backbone.Model.extend({
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+
+ var age = person.get("age"); // 67
+ var name = person.get("name"); // "Thomas"
+ var children = person.get("children"); // ['Ryan']
+
+
+Sometimes you will want your model to contain default values. This can easily be accomplished by setting a property name ‘defaults’ in your model declaration.
+ Person = Backbone.Model.extend({
+ defaults: {
+ name: 'Fetus',
+ age: 0,
+ children: []
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+
+ var age = person.get("age"); // 67
+ var name = person.get("name"); // "Thomas"
+ var children = person.get("children"); // ['Ryan']
+
+
+Models can contain as many custom methods as you like to manipulate attributes. By default all methods are public.
+ Person = Backbone.Model.extend({
+ defaults: {
+ name: 'Fetus',
+ age: 0,
+ children: []
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ },
+ adopt: function( newChildsName ){
+ var children_array = this.get("children");
+ children_array.push( newChildsName );
+ this.set({ children: children_array });
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+ person.adopt('John Resig');
+ var children = person.get("children"); // ['Ryan', 'John Resig']
+
+
+So we can implement methods to get/set and perform other calculations using attributes from our model at any time.
+Now onto one of the more useful parts of using a library such as backbone. All attributes of a model can have listeners bound to them to detect changes to their values. In our initialize function we are going to bind a function call everytime we change the value of our attribute. In this case every time we adopt a new kid we are going to throw a party.
+ Person = Backbone.Model.extend({
+ defaults: {
+ name: 'Fetus',
+ age: 0,
+ children: []
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ this.bind("change:children", function(){
+ alert("More children, single parenting is great!");
+ var children = this.get("children"); // ['Ryan', 'Stewie Griffin']
+ });
+ },
+ adopt: function( newChildsName ){
+ var children_array = this.get("children");
+ children_array.push( newChildsName );
+ this.set({ children: children_array });
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+ person.adopt('Stewie Griffin'); // This triggers a change and will alert()
+
+
+So we can bind the a change listener to individual attributes or if we like simply ‘this.bind(“change”, function(){});’ to listen for changes to all attributes of the model.
+Models actually have to be a part of a collection for requests to the server to work by default. This tutorial is more of a focus on individual models. Check back soon for a tutorial on collection implementation.
+Get all the current attributes
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+ var attributes = person.toJSON; // { name: "Thomas", age: 67, children: ['Ryan']}
+ /* This simply returns a copy of the current attributes. */
+ delete attributes;
+ var attributes = person.attributes;
+ /* The line above gives a direct reference to the attributes and you should be careful when playing with it. Best practise would suggest that you use .set() to edit attributes of a model to take advantage of backbone listeners. */
+
+Validate data before you set or save it
+ Person = Backbone.Model.extend({
+ // If you return a string from the validate function,
+ // Backbone will throw an error
+ validate: function( attributes ){
+ if( attributes.age < 0 && attributes.name != "Dr Manhatten" ){
+ return "You can't be negative years old";
+ }
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ this.bind("error", function(model, error){
+ // We have received an error, log it, alert it or forget it :)
+ alert( error );
+ });
+ }
+ });
+
+ var person = new Person;
+ person.set({ name: "Mary Poppins", age: -1 });
+ // Will trigger an alert outputting the error
+ delete person;
+
+ var person = new Person;
+ person.set({ name: "Dr Manhatten", age: -1 });
+ // God have mercy on our souls
+
+
+This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+Building single page web apps or complicated user interfaces will get extremely difficult by simpling using jQuery or mooTools. The problem is standard javascript libraries are great at what they do and without realizing it you can build an entire application without any formal structure. You will with ease turn your application into a nested pile of jQuery callbacks, all tied to concrete DOM elements.
+I shouldn’t need to explain why building something without any structure is a bad idea. Of course you can always invent your own way of implement your own way of structuring your application but you miss out on the benefits of the open source community.
+Backbone is an incredibly small library for the amount of functionality and structure it gives you. One can not easily summarize the benefits you will reap from using it. If you read through some of the beginner tutorials the benefits will soon become self evident and due to backbone.js light nature you can incrementally include it in any current or future projects.
+This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+Backbone collections are simply an ordered set of models. Such that it can be used in situations such as;
+Typically your collection will only use one type of model but models themselves are not limited to a type of collection;
+Here is a generic Model/Collection example.
+ var Song = Backbone.Model.extend({
+ initialize: function(){
+ console.log("Music is the answer");
+ }
+ });
+
+ var Album = Backbone.Collection.extend({
+ model: Song
+ });
+
+Now we are going to populate a creation with some useful data.
+ var Song = Backbone.Model.extend({
+ defaults: {
+ name: "Not specified",
+ artist: "Not specified"
+ },
+ initialize: function(){
+ console.log("Music is the answer");
+ }
+ });
+
+ var Album = Backbone.Collection.extend({
+ model: Song
+ });
+
+ var song1 = new Song({ name: "How Bizarre", artist: "OMC" });
+ var song2 = new Song({ name: "Sexual Healing", artist: "Marvin Gaye" });
+ var song3 = new Song({ name: "Talk It Over In Bed", artist: "OMC" });
+
+ var myAlbum = new Album([ song1, song2, song3]);
+ console.log( myAlbum.models ); // [song1, song2, song3]
+
+
+Backbone is an incredibly small library for the amount of functionality and structure it gives you. One can not easily summarize the benefits you will reap from using it. If you read through some of the beginner tutorials the benefits will soon become self evident and due to Backbone.js light nature you can incrementally include it in any current or future projects.
+This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+Across the internet the definition of MVC is so diluted that it’s hard to tell what exactly your model should be doing. The authors of backbone.js have quite a clear definition of what they believe the model represents in backbone.js.
+++Models are the heart of any JavaScript application, containing the interactive data as well as a large part of the logic surrounding it: conversions, validations, computed properties, and access control.
+
So for the purpose of the tutorial let’s create a model.
+ Person = Backbone.Model.extend({
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person;
+
+So initialize() is triggered whenever you create a new instance of a model( models, collections and views work the same way ). You don’t have to include it in your model declaration but you will find yourself using it more often than not.
+Now we want to pass some parameters when we create an instance of our model.
+ Person = Backbone.Model.extend({
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67});
+ delete person;
+ // or we can set afterwards, these operations are equivelent
+ var person = new Person();
+ person.set({ name: "Thomas", age: 67});
+
+
+So passing a javascript object to our constructor is the same as calling model.set(). Now that these models have attributes set we need to be able to retrieve them.
+Using the model.get() method we can access model properties at anytime.
+ Person = Backbone.Model.extend({
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+
+ var age = person.get("age"); // 67
+ var name = person.get("name"); // "Thomas"
+ var children = person.get("children"); // ['Ryan']
+
+
+Sometimes you will want your model to contain default values. This can easily be accomplished by setting a property name ‘defaults’ in your model declaration.
+ Person = Backbone.Model.extend({
+ defaults: {
+ name: 'Fetus',
+ age: 0,
+ children: []
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+
+ var age = person.get("age"); // 67
+ var name = person.get("name"); // "Thomas"
+ var children = person.get("children"); // ['Ryan']
+
+
+Models can contain as many custom methods as you like to manipulate attributes. By default all methods are public.
+ Person = Backbone.Model.extend({
+ defaults: {
+ name: 'Fetus',
+ age: 0,
+ children: []
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ },
+ adopt: function( newChildsName ){
+ var children_array = this.get("children");
+ children_array.push( newChildsName );
+ this.set({ children: children_array });
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+ person.adopt('John Resig');
+ var children = person.get("children"); // ['Ryan', 'John Resig']
+
+
+So we can implement methods to get/set and perform other calculations using attributes from our model at any time.
+Now onto one of the more useful parts of using a library such as backbone. All attributes of a model can have listeners bound to them to detect changes to their values. In our initialize function we are going to bind a function call everytime we change the value of our attribute. In this case if the name of our “person” changes we will alert their new name.
+ Person = Backbone.Model.extend({
+ defaults: {
+ name: 'Fetus',
+ age: 0,
+ children: []
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ this.bind("change:name", function(){
+ var name = this.get("name"); // 'Stewie Griffin'
+ alert("Changed my name to " + name );
+ });
+ },
+ changeName: function( name ){
+ this.set({ name: name });
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+ person.changeName('Stewie Griffin'); // This triggers a change and will alert()
+
+So we can bind the a change listener to individual attributes or if we like simply ‘this.bind(“change”, function(){});’ to listen for changes to all attributes of the model.
+Models actually have to be a part of a collection for requests to the server to work by default. This tutorial is more of a focus on individual models. Check back soon for a tutorial on collection implementation.
+Get all the current attributes
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+ var attributes = person.toJSON(); // { name: "Thomas", age: 67, children: ['Ryan']}
+ /* This simply returns a copy of the current attributes. */
+ delete attributes;
+ var attributes = person.attributes;
+ /* The line above gives a direct reference to the attributes and you should be careful when playing with it. Best practise would suggest that you use .set() to edit attributes of a model to take advantage of backbone listeners. */
+
+Validate data before you set or save it
+ Person = Backbone.Model.extend({
+ // If you return a string from the validate function,
+ // Backbone will throw an error
+ validate: function( attributes ){
+ if( attributes.age < 0 && attributes.name != "Dr Manhatten" ){
+ return "You can't be negative years old";
+ }
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ this.bind("error", function(model, error){
+ // We have received an error, log it, alert it or forget it :)
+ alert( error );
+ });
+ }
+ });
+
+ var person = new Person;
+ person.set({ name: "Mary Poppins", age: -1 });
+ // Will trigger an alert outputting the error
+ delete person;
+
+ var person = new Person;
+ person.set({ name: "Dr Manhatten", age: -1 });
+ // God have mercy on our souls
+
+
+This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+Backbone routers are used for routing your applications URL’s when using hash tags(#). In the traditional MVC sense they don’t neccesarily fit the semantics and if you have read “What is a view?” it will elaborate on this point. Though a Backbone “router” is still very useful for any application/feature that needs URL routing/history capabilities.
+Defined routers should always contain at least one route and a function to map the particular route to. In the example below we are going to define a route that is always called.
+Also note that routes intepret anything after “#” tag in the url. All links in your application should target “#/action” or “#action”. (Appending a forward slash after the hashtag looks a bit nicer e.g. http://example.com/#/user/help)
+<script>
+ var AppRouter = Backbone.Router.extend({
+ routes: {
+ "*actions": "defaultRoute" // matches http://example.com/#anything-here
+ },
+ defaultRoute: function( actions ){
+ // The variable passed in matches the variable in the route definition "actions"
+ alert( actions );
+ }
+ });
+ // Initiate the router
+ var app_router = new AppRouter;
+ // Start Backbone history a neccesary step for bookmarkable URL's
+ Backbone.history.start();
+
+</script>
+
+<a href="#action">Activate route</a>
+<a href="#/route/action">Activate another route</a>
+<!-- Notice the change in the url -->
+
+Please note: * Prior to Backbone 0.5 (released 1. July 2011) Routes was originally called Controllers. Due to clearity developers on the Backbone team renamed it to Routes. Hence, if you find yourself using an older version of Backbone you should write Backbone.Controller.extend({ * });
+Most conventional frameworks allow you to define routes that contain a mix of static and dynamic route parameters. For example you might want to retrieve a post with a variable id with a friendly URL string. Such that your URL would look like “http://example.com/#/posts/12”. Once this route was activated you would want to access the id given in the URL string. This example is implemented below.
+<script>
+ var AppRouter = Backbone.Router.extend({
+ routes: {
+ "/posts/:id": "getPost",
+ "*actions": "defaultRoute" // Backbone will try match the route above first
+ },
+ getPost: function( id ) {
+ // Note the variable in the route definition being passed in here
+ alert( "Get post number " + id );
+ },
+ defaultRoute: function( actions ){
+ alert( actions );
+ }
+ });
+ // Instantiate the router
+ var app_router = new AppRouter;
+ // Start Backbone history a neccesary step for bookmarkable URL's
+ Backbone.history.start();
+
+</script>
+
+<a href="#/posts/120">Post 120</a>
+<a href="#/posts/130">Post 130</a>
+<!-- Notice the change in the url -->
+
+Backbone uses two styles of variables when implementing routes. First there are “:params” which match any URL components between slashes. Then there are “*splats” which match any number of URL components. Note that due to the nature of a “*splat” it will always be the last variable in your URL as it will match any and all components.
+Any “*splats” or “:params” in route definitions are passed as variables respective order to the associated function. A route defined as “/:route/:action” will pass 2 variables(route, action) to the call back function. Which can be accessed with “function( route, action )”. (If this is confusing please post a comment and I will try articulate it better)
+Here are some examples of using “:params” and “*splats”
+ routes: {
+
+ "/posts/:id": "getPost",
+ // <a href="http://example.com/#/posts/121">Example</a>
+
+ "/download/*path": "downloadFile",
+ // <a href="http://example.com/#/download/user/images/hey.gif">Download</a>
+
+ "/:route/:action": "loadView",
+ // <a href="http://example.com/#/dashboard/graph">Load Route/Action View</a>
+
+ },
+
+ getPost: function( id ){ alert(id); /* 121 */ },
+ downloadFile: function( path ){ alert(path); /* user/images/hey.gif */ },
+ loadView: function( route, action ){
+ alert(route + "_" + action);
+ /* dashboard_graph */
+ }
+
+Routes are quite powerful and in an ideal world your application should never contain too many. If you need to implement hash tags with SEO in mind, do a google search for “google seo hashbangs”.
+Remember to do a pull request for any errors you come across.
+No Tips and Tricks
+This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+Backbone views are used to reflect what your applications’ data models look like. They are also used to listen to events and react accordingly. This tutorial will not be addressing how to bind models and collections to views but will focus on view functionality and how to use views with a JavaScript templating library, specifically Underscore.js’s _.template.
+We will be using jQuery 1.5 as our DOM manipulator. It’s possible to use other libraries such as MooTools or Sizzle, but official Backbone.js documentation endorses jQuery. Backbone.View events may not work with other libraries other than jQuery.
+For the purposes of this demonstration, we will be implementing a search box. A live example can be found on jsFiddle.
+ SearchView = Backbone.View.extend({
+ initialize: function(){
+ alert("Alerts suck.");
+ }
+ });
+
+ // The initialize function is always called when instantiating a Backbone View.
+ // Consider it the constructor of the class.
+ var search_view = new SearchView;
+
+The “el” property references the DOM object created in the browser. Every Backbone.js view has an “el” property, and if it not defined, Backbone.js will construct its own, which is an empty div element.
+Let us set our view’s “el” property to div#search_container, effectively making Backbone.View the owner of the DOM element.
+<div id="search_container"></div>
+
+<script type="text/javascript">
+ SearchView = Backbone.View.extend({
+ initialize: function(){
+ alert("Alerts suck.");
+ }
+ });
+
+ var search_view = new SearchView({ el: $("#search_container") });
+</script>
+
+Note: Keep in mind that this binds the container element. Any events we trigger must be in this element.
+Backbone.js is dependent on Underscore.js, which includes its own micro-templating solution. Refer to Underscore.js’s documentation for more information.
+Let us implement a “render()” function and call it when the view is initialized. The “render()” function will load our template into the view’s “el” property using jQuery.
+<div id="search_container"></div>
+
+<script type="text/javascript">
+ SearchView = Backbone.View.extend({
+ initialize: function(){
+ this.render();
+ },
+ render: function(){
+ // Compile the template using underscore
+ var template = _.template( $("#search_template").html(), {} );
+ // Load the compiled HTML into the Backbone "el"
+ this.el.html( template );
+ }
+ });
+
+ var search_view = new SearchView({ el: $("#search_container") });
+</script>
+
+<script type="text/template" id="search_template">
+ <label>Search</label>
+ <input type="text" id="search_input" />
+ <input type="button" id="search_button" value="Search" />
+</script>
+
+Tip: Place all your templates in a file and serve them from a CDN. This ensures your users will always have your application cached.
+To attach a listener to our view, we use the “events” attribute of Backbone.View. Remember that event listeners can only be attached to child elements of the “el” property. Let us attach a “click” listener to our button.
+<div id="search_container"></div>
+
+<script type="text/javascript">
+ SearchView = Backbone.View.extend({
+ initialize: function(){
+ this.render();
+ },
+ render: function(){
+ var template = _.template( $("#search_template").html(), {} );
+ this.el.html( template );
+ },
+ events: {
+ "click input[type=button]": "doSearch"
+ },
+ doSearch: function( event ){
+ // Button clicked, you can access the element that was clicked with event.currentTarget
+ alert( "Search for " + $("#search_input").val() );
+ }
+ });
+
+ var search_view = new SearchView({ el: $("#search_container") });
+</script>
+
+<script type="text/template" id="search_template">
+ <label>Search</label>
+ <input type="text" id="search_input" />
+ <input type="button" id="search_button" value="Search" />
+</script>
+
+Using template variables
+<div id="search_container"></div>
+
+<script type="text/javascript">
+ SearchView = Backbone.View.extend({
+ initialize: function(){
+ this.render();
+ },
+ render: function(){
+ //Pass variables in using Underscore.js Template
+ var variables = { search_label: "My Search" };
+ // Compile the template using underscore
+ var template = _.template( $("#search_template").html(), variables );
+ // Load the compiled HTML into the Backbone "el"
+ this.el.html( template );
+ },
+ events: {
+ "click input[type=button]": "doSearch"
+ },
+ doSearch: function( event ){
+ // Button clicked, you can access the element that was clicked with event.currentTarget
+ alert( "Search for " + $("#search_input").val() );
+ }
+ });
+
+ var search_view = new SearchView({ el: $("#search_container") });
+</script>
+
+<script type="text/template" id="search_template">
+ <!-- Access template variables with <%= %> -->
+ <label><%= search_label %></label>
+ <input type="text" id="search_input" />
+ <input type="button" id="search_button" value="Search" />
+</script>
+
+If you have any questions, leave a comment below.
+This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+Building single-page web apps or complicated user interfaces will get extremely difficult by simply using jQuery or MooTools. The problem is standard JavaScript libraries are great at what they do – and without realizing it you can build an entire application without any formal structure. You will with ease turn your application into a nested pile of jQuery callbacks, all tied to concrete DOM elements.
+I shouldn’t need to explain why building something without any structure is a bad idea. Of course you can always invent your own way of implement your own way of structuring your application but you miss out on the benefits of the open source community.
+Backbone is an incredibly small library for the amount of functionality and structure it gives you. One can not easily summarize the benefits you will reap from using it. If you read through some of the beginner tutorials the benefits will soon become self evident and due to Backbone.js light nature you can incrementally include it in any current or future projects.
++#cdnjs on irc.freenode.net, don't forget to follow me on twitter.
+ diff --git a/contact.textile b/contact.textile new file mode 100644 index 00000000..c80ab394 --- /dev/null +++ b/contact.textile @@ -0,0 +1,9 @@ +--- +layout: default +title: Contact +--- + +h2. Contact + +p. You can contact the original owner of this site through "github":https://github.com/thomasdavis or via twitter("@neutralthoughts":http://twitter.com/neutralthoughts). + diff --git a/contribute.textile b/contribute.textile new file mode 100644 index 00000000..2a61a829 --- /dev/null +++ b/contribute.textile @@ -0,0 +1,10 @@ +--- +layout: default +title: Contribute +--- + +h2. Contribute + +p. The main goal behind BackboneTutorials.com is to build a comprehensive guide for developers of all levels of experience. Discussion and debate are encouraged and will hopefully breed innovation for javascript applications. + +All contributions will be well acknowledged on the site. diff --git a/css/reset.css b/css/reset.css new file mode 100644 index 00000000..5b41bb7b --- /dev/null +++ b/css/reset.css @@ -0,0 +1,260 @@ +/** + * HTML5 ✰ Boilerplate + * + * style.css contains a reset, font normalization and some base styles. + * + * Credit is left where credit is due. + * Much inspiration was taken from these projects: + * - yui.yahooapis.com/2.8.1/build/base/base.css + * - camendesign.com/design/ + * - praegnanz.de/weblog/htmlcssjs-kickstart + */ + + +/** + * html5doctor.com Reset Stylesheet (Eric Meyer's Reset Reloaded + HTML5 baseline) + * v1.6.1 2010-09-17 | Authors: Eric Meyer & Richard Clark + * html5doctor.com/html-5-reset-stylesheet/ + */ + +html, body, div, span, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, +small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, figcaption, figure, +footer, header, hgroup, menu, nav, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} + +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} + +blockquote, q { quotes: none; } + +blockquote:before, blockquote:after, +q:before, q:after { content: ""; content: none; } + +ins { background-color: #ff9; color: #000; text-decoration: none; } + +mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; } + +del { text-decoration: line-through; } + +abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; } + +table { border-collapse: collapse; border-spacing: 0; } + +hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; } + +input, select { vertical-align: middle; } + + +/** + * Font normalization inspired by YUI Library's fonts.css: developer.yahoo.com/yui/ + */ + +body { font:13px/1.231 sans-serif; *font-size:small; } /* Hack retained to preserve specificity */ +select, input, textarea, button { font:99% sans-serif; } + +/* Normalize monospace sizing: + en.wikipedia.org/wiki/MediaWiki_talk:Common.css/Archive_11#Teletype_style_fix_for_Chrome */ +pre, code, kbd, samp { font-family: monospace, sans-serif; } + + +/** + * Minimal base styles. + */ + +/* Always force a scrollbar in non-IE */ +html { overflow-y: scroll; } + +/* Accessible focus treatment: people.opera.com/patrickl/experiments/keyboard/test */ +a:hover, a:active { outline: none; } + +ul, ol { margin-left: 2em; } +ol { list-style-type: decimal; } + +/* Remove margins for navigation lists */ +nav ul, nav li { margin: 0; list-style:none; list-style-image: none; } + +small { font-size: 85%; } +strong, th { font-weight: bold; } + +td { vertical-align: top; } + +/* Set sub, sup without affecting line-height: gist.github.com/413930 */ +sub, sup { font-size: 75%; line-height: 0; position: relative; } +sup { top: -0.5em; } +sub { bottom: -0.25em; } + +pre { + /* www.pathf.com/blogs/2008/05/formatting-quoted-code-in-blog-posts-css21-white-space-pre-wrap/ */ + white-space: pre; white-space: pre-wrap; word-wrap: break-word; + padding: 15px; +} + +textarea { overflow: auto; } /* www.sitepoint.com/blogs/2010/08/20/ie-remove-textarea-scrollbars/ */ + +.ie6 legend, .ie7 legend { margin-left: -7px; } + +/* Align checkboxes, radios, text inputs with their label by: Thierry Koblentz tjkdesign.com/ez-css/css/base.css */ +input[type="radio"] { vertical-align: text-bottom; } +input[type="checkbox"] { vertical-align: bottom; } +.ie7 input[type="checkbox"] { vertical-align: baseline; } +.ie6 input { vertical-align: text-bottom; } + +/* Hand cursor on clickable input elements */ +label, input[type="button"], input[type="submit"], input[type="image"], button { cursor: pointer; } + +/* Webkit browsers add a 2px margin outside the chrome of form elements */ +button, input, select, textarea { margin: 0; } + +/* Colors for form validity */ +input:valid, textarea:valid { } +input:invalid, textarea:invalid { + border-radius: 1px; -moz-box-shadow: 0px 0px 5px red; -webkit-box-shadow: 0px 0px 5px red; box-shadow: 0px 0px 5px red; +} +.no-boxshadow input:invalid, .no-boxshadow textarea:invalid { background-color: #f0dddd; } + + +/* These selection declarations have to be separate + No text-shadow: twitter.com/miketaylr/status/12228805301 + Also: hot pink! */ +::-moz-selection{ background: #FF5E99; color:#fff; text-shadow: none; } +::selection { background:#FF5E99; color:#fff; text-shadow: none; } + +/* j.mp/webkit-tap-highlight-color */ +a:link { -webkit-tap-highlight-color: #FF5E99; } + +/* Make buttons play nice in IE: + www.viget.com/inspire/styling-the-button-element-in-internet-explorer/ */ +button { width: auto; overflow: visible; } + +/* Bicubic resizing for non-native sized IMG: + code.flickr.com/blog/2008/11/12/on-ui-quality-the-little-things-client-side-image-resizing/ */ +.ie7 img { -ms-interpolation-mode: bicubic; } + +/** + * You might tweak these.. + */ + +body, select, input, textarea { + /* #444 looks better than black: twitter.com/H_FJ/statuses/11800719859 */ + color: #444; + /* Set your base font here, to apply evenly */ + /* font-family: Georgia, serif; */ +} + +/* Headers (h1, h2, etc) have no default font-size or margin; define those yourself */ +h1, h2, h3, h4, h5, h6 { font-weight: bold; } + +a, a:active, a:visited { color: #607890; } +a:hover { color: #036; } + + +/** + * Primary styles + * + * Author: + */ + + + + + + + + + + + + + + + + +/** + * Non-semantic helper classes: please define your styles before this section. + */ + +/* For image replacement */ +.ir { display: block; text-indent: -999em; overflow: hidden; background-repeat: no-repeat; text-align: left; direction: ltr; } + +/* Hide for both screenreaders and browsers: + css-discuss.incutio.com/wiki/Screenreader_Visibility */ +.hidden { display: none; visibility: hidden; } + +/* Hide only visually, but have it available for screenreaders: by Jon Neal. + www.webaim.org/techniques/css/invisiblecontent/ & j.mp/visuallyhidden */ +.visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } +/* Extends the .visuallyhidden class to allow the element to be focusable when navigated to via the keyboard: drupal.org/node/897638 */ +.visuallyhidden.focusable:active, +.visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; } + +/* Hide visually and from screenreaders, but maintain layout */ +.invisible { visibility: hidden; } + +/* The Magnificent Clearfix: Updated to prevent margin-collapsing on child elements. + j.mp/bestclearfix */ +.clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; overflow: hidden; } +.clearfix:after { clear: both; } +/* Fix clearfix: blueprintcss.lighthouseapp.com/projects/15318/tickets/5-extra-margin-padding-bottom-of-page */ +.clearfix { zoom: 1; } + + + +/** + * Media queries for responsive design. + * + * These follow after primary styles so they will successfully override. + */ + +@media all and (orientation:portrait) { + /* Style adjustments for portrait mode goes here */ + +} + +@media all and (orientation:landscape) { + /* Style adjustments for landscape mode goes here */ + +} + +/* Grade-A Mobile Browsers (Opera Mobile, Mobile Safari, Android Chrome) + consider this: www.cloudfour.com/css-media-query-for-mobile-is-fools-gold/ */ +@media screen and (max-device-width: 480px) { + + + /* Uncomment if you don't want iOS and WinMobile to mobile-optimize the text for you: j.mp/textsizeadjust */ + /* html { -webkit-text-size-adjust:none; -ms-text-size-adjust:none; } */ +} + + +/** + * Print styles. + * + * Inlined to avoid required HTTP connection: www.phpied.com/delay-loading-your-print-css/ + */ +@media print { + * { background: transparent !important; color: black !important; text-shadow: none !important; filter:none !important; + -ms-filter: none !important; } /* Black prints faster: sanbeiji.com/archives/953 */ + a, a:visited { color: #444 !important; text-decoration: underline; } + a[href]:after { content: " (" attr(href) ")"; } + abbr[title]:after { content: " (" attr(title) ")"; } + .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } /* Don't show links for images, or javascript/internal links */ + pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } + thead { display: table-header-group; } /* css-discuss.incutio.com/wiki/Printing_Tables */ + tr, img { page-break-inside: avoid; } + @page { margin: 0.5cm; } + p, h2, h3 { orphans: 3; widows: 3; } + h2, h3{ page-break-after: avoid; } +} diff --git a/css/ribbon.css b/css/ribbon.css new file mode 100644 index 00000000..580b9d3c --- /dev/null +++ b/css/ribbon.css @@ -0,0 +1,115 @@ +@font-face { + font-family: Collegiate; + src: url("Collegiate.ttf"); +} + +.ribbon-holder { + position: absolute; + top: 0; + overflow: hidden; + height: 10em; +} + +.right.ribbon-holder { + right: 0; +} + +.left.ribbon-holder { + left: 0; +} + +.ribbon, +.ribbon:hover { + text-decoration: none; +} + +.ribbon { + font-family: Collegiate, sans-serif; + letter-spacing: -.1px; + opacity: 0.95; + + padding: 0.25em 0; + position: relative; + top: 2.5em; + + /* Defaults friendly for white pages. */ + -moz-box-shadow: 0 0 13px #888; + -webkit-box-shadow: 0 0 13px #888; + color: #FFF; + display: block; + line-height: 1.35em; +} + +.ribbon .text { + padding: 0.1em 3em; +} + +.right .ribbon { + -moz-transform: rotate(45deg); + -webkit-transform: rotate(45deg); + right: -2.6em; +} + +.left .ribbon { + -moz-transform: rotate(-45deg); + -webkit-transform: rotate(-45deg); + left: -2.6em; +} + +.white.ribbon { + color: #111; + background-color: #F5F5F5; + background: -webkit-gradient(linear, left bottom, left top, from(#f3f3f3), to(#fff)); + -moz-box-shadow: 0 0 13px #999; + -webkit-box-shadow: 0 0 13px #999; + text-shadow: 0 0 .05em; +} + +.white.ribbon .text { + border: 1px solid #cecece; +} + +.red.ribbon { + background-color: #9a0000; + background: -webkit-gradient(linear, left bottom, left top, from(#9a0000), to(#a90000)); +} + +.red.ribbon .text { + border: 1px solid #bf6060; +} + +.green.ribbon { + background-color: #006e00; + background: -webkit-gradient(linear, left bottom, left top, from(#006e00), to(#007200)); +} + +.green.ribbon .text { + border: 1px solid #6bac6b; +} + +.darkblue.ribbon { + background-color: #121621; + color: #ecedee; +} + +.darkblue.ribbon .text { + border: 1px solid #53565e; +} + +.orange.ribbon { + background-color: #E57504; + background: -webkit-gradient(linear, left bottom, left top, from(#dc7202), to(#ee7906)); +} + +.orange.ribbon .text { + border: 1px solid #ebaa65; +} + +.gray.ribbon { + background-color: #6d6d6d; + background: -webkit-gradient(linear, left bottom, left top, from(#6a6a6a) to(#6d6d6d)); +} + +.gray.ribbon .text { + border: 1px solid #a4a4a4; +} diff --git a/css/stacklayout.css b/css/stacklayout.css new file mode 100644 index 00000000..149a432f --- /dev/null +++ b/css/stacklayout.css @@ -0,0 +1,96 @@ +/* +* StackLayout by Campbell McGuiness +* +* http://stacklayout.com/ +* http://twitter.com/stacklayout +* http://www.decalcms.com/ +* http://workingsoftware.com.au/ +* +* This work is licensed under Creative Commons * Attribution-ShareAlike 3.0 Australia License * http://creativecommons.org/licenses/by-sa/3.0/au/ +* +* Please retain this credit and let us know if you use StackLayout for inclusion on http://stacklayout.com/who.html +* +* cam@workingsoftware.com.au +*/ +.stack, +.stack1of2, +.stack1of3, +.stack2of3, +.stack1of4, +.stack3of4, +.stack1of5, +.stack2of5, +.stack3of5, +.stack4of5, +.stackAuto +{ + display:inline-block; + font-family:'Courier New',monospace; + letter-spacing:-0.65em; + word-spacing:-0.65em; + text-align:center; + vertical-align:top; +} +.stackContent +{ + display:block; + letter-spacing:normal; + word-spacing:normal; + text-align:left; +} +.stackContent:after +{ + content:"."; + display:block; + height:0; + clear:both; + visibility:hidden; +} +.stackAuto .stackContent +{ + text-align:center; +} +.stackAuto +{ + width:auto; +} +.stack +{ + width:100%; +} +.stack1of2 +{ + width:50%; +} +.stack1of3 +{ + width:33.334%; +} +.stack2of3 +{ + width:66.667%; +} +.stack1of4 +{ + width:25%; +} +.stack3of4 +{ + width:75%; +} +.stack1of5 +{ + width:20%; +} +.stack2of5 +{ + width:40%; +} +.stack3of5 +{ + width:60%; +} +.stack4of5 +{ + width:80%; +} diff --git a/css/style.css b/css/style.css new file mode 100644 index 00000000..d9a8e4e2 --- /dev/null +++ b/css/style.css @@ -0,0 +1,81 @@ + +body { font:1em/1.625em "lucida grande","lucida sans unicode", sans-serif; background-color:#FFFEF0; +font-size-adjust:none; +font-style:normal; +font-variant:normal; +font-weight:normal; +} + +p { padding:0 0 0.8125em 0; color:#111; font-weight:300;} + +p + p { text-indent:1.625em;} + +p.first:first-letter{ float:left;font-family: baskerville,"palatino linotype",serif;font-size:3em;font-weight:700;line-height:1em;margin-bottom:-0.2em;padding:0.2em 0.1em 0 0; } +p img { float: left; margin: 0.5em 0.8125em 0.8125em 0; padding: 0; } +p img.right { float: right; margin: 0.5em 0 0.8125em 0.8125em } + + +h1,h2{ font-weight:normal; color: #333; font-family:Georgia, serif; } +h3,h4,h5,h6 { font-weight: normal; color: #333; font-family:Georgia, serif; } + + +h1 { font-size: 2.125em; margin-bottom: 0.765em; } +h2 { font-size: 1.9em; margin-bottom: 0.855em; } +h3 { font-size: 1.7em; margin-bottom: 0.956em; } +h4 { font-size: 1.4em; margin-bottom: 1.161em; } +h5,h6 { font-size: 1.313em; margin-bottom: 1.238em; } + + + +ul{list-style-position:outside;} +li ul, +li ol { margin:0 1.625em; } +ul, ol { margin: 0 0 1.625em 0; } + + +dl { margin: 0 0 1.625em 0; } +dl dt { font-weight: bold; } +dl dd { margin-left: 1.625em; } + +a { color:#005AF2; text-decoration:none; } +a:hover { text-decoration: underline; } + + +table { margin-bottom:1.625em; border-collapse: collapse; } +th { font-weight:bold; } +tr,th,td { margin:0; padding:0 1.625em 0 1em; height:26px; } +tfoot { font-style: italic; } +caption { text-align:center; font-family:Georgia, serif; } + + +abbr, acronym { border-bottom:1px dotted #000; } +address { margin-top:1.625em; font-style: italic; } +del {color:#000;} + + +blockquote { padding:1em 1em 1.625em 1em; font-family:georgia,serif;font-style: italic; margin-bottom: 20px;} +blockquote:before { content:"\201C";font-size:3em;margin-left:-.625em; font-family:georgia,serif;color:#aaa;line-height:0;}/* From Tripoli */ +blockquote:after { content:"\201D";font-size:3em;margin-right:0px; font-family:georgia,serif;color:#aaa;line-height:0;margin-top: 25px;float: right;}/* From Tripoli */ +blockquote > p {padding:0; margin:0; } + +strong { font-weight: bold; } +em, dfn { font-style: italic; } +dfn { font-weight: bold; } +pre, code { margin: 1.625em 0; white-space: pre; } +pre, code, tt { font: 1em monospace; line-height: 1.5; } +tt { display: block; margin: 1.625em 0; } +hr { margin-bottom:1.625em; } + + + +.oldbook { font-family:"Warnock Pro","Goudy Old Style","Book Antiqua","Palatino",Georgia,serif; } +.note { font-family:Georgia, "Times New Roman", Times, serif; font-style:italic; font-size:0.9em; margin:0.1em; color:#333; } +.mono { font-family:"Courier New", Courier, monospace; } + +.tutorials { + list-style: none; + margin-left: 20px; +} +ul { + margin-left: 20px; +} diff --git a/css/syntax.css b/css/syntax.css new file mode 100644 index 00000000..2774b764 --- /dev/null +++ b/css/syntax.css @@ -0,0 +1,60 @@ +.highlight { background: #ffffff; } +.highlight .c { color: #999988; font-style: italic } /* Comment */ +.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +.highlight .k { font-weight: bold } /* Keyword */ +.highlight .o { font-weight: bold } /* Operator */ +.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ +.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ +.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #aa0000 } /* Generic.Error */ +.highlight .gh { color: #999999 } /* Generic.Heading */ +.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ +.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #555555 } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #aaaaaa } /* Generic.Subheading */ +.highlight .gt { color: #aa0000 } /* Generic.Traceback */ +.highlight .kc { font-weight: bold } /* Keyword.Constant */ +.highlight .kd { font-weight: bold } /* Keyword.Declaration */ +.highlight .kp { font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #009999 } /* Literal.Number */ +.highlight .s { color: #d14 } /* Literal.String */ +.highlight .na { color: #008080 } /* Name.Attribute */ +.highlight .nb { color: #0086B3 } /* Name.Builtin */ +.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ +.highlight .no { color: #008080 } /* Name.Constant */ +.highlight .ni { color: #800080 } /* Name.Entity */ +.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ +.highlight .nn { color: #555555 } /* Name.Namespace */ +.highlight .nt { color: #000080 } /* Name.Tag */ +.highlight .nv { color: #008080 } /* Name.Variable */ +.highlight .ow { font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #009999 } /* Literal.Number.Float */ +.highlight .mh { color: #009999 } /* Literal.Number.Hex */ +.highlight .mi { color: #009999 } /* Literal.Number.Integer */ +.highlight .mo { color: #009999 } /* Literal.Number.Oct */ +.highlight .sb { color: #d14 } /* Literal.String.Backtick */ +.highlight .sc { color: #d14 } /* Literal.String.Char */ +.highlight .sd { color: #d14 } /* Literal.String.Doc */ +.highlight .s2 { color: #d14 } /* Literal.String.Double */ +.highlight .se { color: #d14 } /* Literal.String.Escape */ +.highlight .sh { color: #d14 } /* Literal.String.Heredoc */ +.highlight .si { color: #d14 } /* Literal.String.Interpol */ +.highlight .sx { color: #d14 } /* Literal.String.Other */ +.highlight .sr { color: #009926 } /* Literal.String.Regex */ +.highlight .s1 { color: #d14 } /* Literal.String.Single */ +.highlight .ss { color: #990073 } /* Literal.String.Symbol */ +.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #008080 } /* Name.Variable.Class */ +.highlight .vg { color: #008080 } /* Name.Variable.Global */ +.highlight .vi { color: #008080 } /* Name.Variable.Instance */ +.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ diff --git a/examples.textile b/examples.textile new file mode 100644 index 00000000..f872d2c5 --- /dev/null +++ b/examples.textile @@ -0,0 +1,18 @@ +--- +layout: default +title: Contact +--- + +h2. Examples of sites using Backbone.js + +h3. "Protosal.com":http://app.protosal.com + +p. Generate proposals for your clients with ease using predefined templates. Track how often your proposals get accepted or declined and who your best client are. + +_CouchDb and Node.js Backend_ + +h3. "GetFlow.com":http://getflow.com + +p. Flow is a task management app that makes working with your team a breeze. You can see the internals under window.Flow. + + diff --git a/index.html b/index.html new file mode 100644 index 00000000..cd15889c --- /dev/null +++ b/index.html @@ -0,0 +1,28 @@ +--- +layout: default +title: +--- +This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+As single page apps and large scale javascript applications become more prominent on the web, useful resources for those developers who are jumping the ship are crucial.
+I started this site to not only consolidate my understanding of backbone.js but to also document what I have learned thus far for myself and others.
+ + + +This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+You can contact the original owner of this site through github or via twitter(@neutralthoughts).
+ + + +This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+The main goal behind BackboneTutorials.com is to build a comprehensive guide for developers of all levels of experience. Discussion and debate are encouraged and will hopefully breed innovation for javascript applications.
+All contributions will be well acknowledge on the site.
+ + + +This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+Across the internet the definition of MVC is so diluted that it’s hard to tell what exactly your model should be doing. The authors of backbone.js have quite a clear definition of what they believe the model represents in backbone.js.
+++Models are the heart of any JavaScript application, containing the interactive data as well as a large part of the logic surrounding it: conversions, validations, computed properties, and access control.
+
So for the purpose of the tutorial let’s create a model.
+ Person = Backbone.Model.extend({
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person;
+
+So initialize() is triggered whenever you create a new instance of a model( models, collections and views work the same way ). You don’t have to include it in your model declaration but you will find yourself using it more often than not.
+Now we want to pass some parameters when we create an instance of our model.
+ Person = Backbone.Model.extend({
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67});
+ delete person;
+ // or we can set afterwards, these operations are equivelent
+ var person = new Person();
+ person.set({ name: "Thomas", age: 67});
+
+
+So passing a javascript object to our constructor is the same as calling model.set(). Now that these models have attributes set we need to be able to retrieve them.
+Using the model.get() method we can access model properties at anytime.
+ Person = Backbone.Model.extend({
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+
+ var age = person.get("age"); // 67
+ var name = person.get("name"); // "Thomas"
+ var children = person.get("children"); // ['Ryan']
+
+
+Sometimes you will want your model to contain default values. This can easily be accomplished by setting a property name ‘defaults’ in your model declaration.
+ Person = Backbone.Model.extend({
+ defaults: {
+ name: 'Fetus',
+ age: 0,
+ children: []
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+
+ var age = person.get("age"); // 67
+ var name = person.get("name"); // "Thomas"
+ var children = person.get("children"); // ['Ryan']
+
+
+Models can contain as many custom methods as you like to manipulate attributes. By default all methods are public.
+ Person = Backbone.Model.extend({
+ defaults: {
+ name: 'Fetus',
+ age: 0,
+ children: []
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ },
+ adopt: function( newChildsName ){
+ var children_array = this.get("children");
+ children_array.push( newChildsName );
+ this.set({ children: children_array });
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+ person.adopt('John Resig');
+ var children = person.get("children"); // ['Ryan', 'John Resig']
+
+
+So we can implement methods to get/set and perform other calculations using attributes from our model at any time.
+Now onto one of the more useful parts of using a library such as backbone. All attributes of a model can have listeners bound to them to detect changes to their values. In our initialize function we are going to bind a function call everytime we change the value of our attribute. In this case every time we adopt a new kid we are going to throw a party.
+ Person = Backbone.Model.extend({
+ defaults: {
+ name: 'Fetus',
+ age: 0,
+ children: []
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ this.bind("change:children", function(){
+ alert("More children, single parenting is great!");
+ var children = this.get("children"); // ['Ryan', 'Stewie Griffin']
+ });
+ },
+ adopt: function( newChildsName ){
+ var children_array = this.get("children");
+ children_array.push( newChildsName );
+ this.set({ children: children_array });
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+ person.adopt('Stewie Griffin'); // This triggers a change and will alert()
+
+
+So we can bind the a change listener to individual attributes or if we like simply ‘this.bind(“change”, function(){});’ to listen for changes to all attributes of the model.
+Models actually have to be a part of a collection for requests to the server to work by default. This tutorial is more of a focus on individual models. Check back soon for a tutorial on collection implementation.
+Get all the current attributes
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+ var attributes = person.toJSON(); // { name: "Thomas", age: 67, children: ['Ryan']}
+ /* This simply returns a copy of the current attributes. */
+ delete attributes;
+ var attributes = person.attributes;
+ /* The line above gives a direct reference to the attributes and you should be careful when playing with it. Best practise would suggest that you use .set() to edit attributes of a model to take advantage of backbone listeners. */
+
+Validate data before you set or save it
+ Person = Backbone.Model.extend({
+ // If you return a string from the validate function,
+ // Backbone will throw an error
+ validate: function( attributes ){
+ if( attributes.age < 0 && attributes.name != "Dr Manhatten" ){
+ return "You can't be negative years old";
+ }
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ this.bind("error", function(model, error){
+ // We have received an error, log it, alert it or forget it :)
+ alert( error );
+ });
+ }
+ });
+
+ var person = new Person;
+ person.set({ name: "Mary Poppins", age: -1 });
+ // Will trigger an alert outputting the error
+ delete person;
+
+ var person = new Person;
+ person.set({ name: "Dr Manhatten", age: -1 });
+ // God have mercy on our souls
+
+
+This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+Backbone views are used reflect what your applications data model looks like. They are also used to listen to events and react accordingly. This tutorial will not be addressing binding models/collections with views. This tutorial will focus on view functionality and how to use it with a javascript templating library.
+We will be using jQuery 1.5.1 as our DOM manipulator.
+ Person = Backbone.Model.extend({
+ initialize: function(){
+ alert("Welcome to this world");
+ } /ch
+ });
+
+ var person = new Person;
+
+So initialize() is triggered whenever you create a new instance of a model( models, collections and views work the same way ). You don’t have to include it in your model declaration but you will find yourself using it more often than not.
+Now we want to pass some parameters when we create an instance of our model.
+ Person = Backbone.Model.extend({
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67});
+ delete person;
+ // or we can set afterwards, these operations are equivelent
+ var person = new Person();
+ person.set({ name: "Thomas", age: 67});
+
+
+So passing a javascript object to our constructor is the same as calling model.set(). Now that these models have attributes set we need to be able to retrieve them.
+Using the model.get() method we can access model properties at anytime.
+ Person = Backbone.Model.extend({
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+
+ var age = person.get("age"); // 67
+ var name = person.get("name"); // "Thomas"
+ var children = person.get("children"); // ['Ryan']
+
+
+Sometimes you will want your model to contain default values. This can easily be accomplished by setting a property name ‘defaults’ in your model declaration.
+ Person = Backbone.Model.extend({
+ defaults: {
+ name: 'Fetus',
+ age: 0,
+ children: []
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+
+ var age = person.get("age"); // 67
+ var name = person.get("name"); // "Thomas"
+ var children = person.get("children"); // ['Ryan']
+
+
+Models can contain as many custom methods as you like to manipulate attributes. By default all methods are public.
+ Person = Backbone.Model.extend({
+ defaults: {
+ name: 'Fetus',
+ age: 0,
+ children: []
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ },
+ adopt: function( newChildsName ){
+ var children_array = this.get("children");
+ children_array.push( newChildsName );
+ this.set({ children: children_array });
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+ person.adopt('John Resig');
+ var children = person.get("children"); // ['Ryan', 'John Resig']
+
+
+So we can implement methods to get/set and perform other calculations using attributes from our model at any time.
+Now onto one of the more useful parts of using a library such as backbone. All attributes of a model can have listeners bound to them to detect changes to their values. In our initialize function we are going to bind a function call everytime we change the value of our attribute. In this case every time we adopt a new kid we are going to throw a party.
+ Person = Backbone.Model.extend({
+ defaults: {
+ name: 'Fetus',
+ age: 0,
+ children: []
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ this.bind("change:children", function(){
+ alert("More children, single parenting is great!");
+ var children = this.get("children"); // ['Ryan', 'Stewie Griffin']
+ });
+ },
+ adopt: function( newChildsName ){
+ var children_array = this.get("children");
+ children_array.push( newChildsName );
+ this.set({ children: children_array });
+ }
+ });
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+ person.adopt('Stewie Griffin'); // This triggers a change and will alert()
+
+
+So we can bind the a change listener to individual attributes or if we like simply ‘this.bind(“change”, function(){});’ to listen for changes to all attributes of the model.
+Models actually have to be a part of a collection for requests to the server to work by default. This tutorial is more of a focus on individual models. Check back soon for a tutorial on collection implementation.
+Get all the current attributes
+
+ var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
+ var attributes = person.toJSON; // { name: "Thomas", age: 67, children: ['Ryan']}
+ /* This simply returns a copy of the current attributes. */
+ delete attributes;
+ var attributes = person.attributes;
+ /* The line above gives a direct reference to the attributes and you should be careful when playing with it. Best practise would suggest that you use .set() to edit attributes of a model to take advantage of backbone listeners. */
+
+Validate data before you set or save it
+ Person = Backbone.Model.extend({
+ // If you return a string from the validate function,
+ // Backbone will throw an error
+ validate: function( attributes ){
+ if( attributes.age < 0 && attributes.name != "Dr Manhatten" ){
+ return "You can't be negative years old";
+ }
+ },
+ initialize: function(){
+ alert("Welcome to this world");
+ this.bind("error", function(model, error){
+ // We have received an error, log it, alert it or forget it :)
+ alert( error );
+ });
+ }
+ });
+
+ var person = new Person;
+ person.set({ name: "Mary Poppins", age: -1 });
+ // Will trigger an alert outputting the error
+ delete person;
+
+ var person = new Person;
+ person.set({ name: "Dr Manhatten", age: -1 });
+ // God have mercy on our souls
+
+
+This site is by no means the definite guide to backbone.js and all corrections and contributions are welcome.
+Building single page web apps or complicated user interfaces will get extremely difficult by simpling using jQuery or mooTools. The problem is standard javascript libraries are great at what they do and without realizing it you can build an entire application without any formal structure. You will with ease turn your application into a nested pile of jQuery callbacks, all tied to concrete DOM elements.
+I shouldn’t need to explain why building something without any structure is a bad idea. Of course you can always invent your own way of implement your own way of structuring your application but you miss out on the benefits of the open source community.
+Backbone is an incredibly small library for the amount of functionality and structure it gives you. One can not easily summarize the benefits you will reap from using it. If you read through some of the beginner tutorials the benefits will soon become self evident and due to backbone.js light nature you can incrementally include it in any current or future projects.
+