#WDI Week 9 Notes
##Sunday
###Code School: The Anatomy of Backbone.js
Source:
http://backbone.codeschool.com/levels/1
http://courseware.codeschool.com/The_Anatomy_of_BackboneJS.pdf
##Monday
###Warmup
Regular expressions calculator:
https://gist.github.com/anonymous-wolf/9dd62f862b65324a034d
http://rubylearning.com/satishtalim/ruby_regular_expressions.html
class Calculator
def initialize
end
def ask(question)
question.match(/What is (-?\d+) plus (-?\d+)?/) do |m|
m[1].to_i + m[2].to_i
end
end
end
###Retro
###TDD
####TDD Homework
- Code School RSpec track https://www.codeschool.com/courses/testing-with-rspec
- http://rspec.info/
- http://betterspecs.org/
- pre-interview test
###Coffeescript
console.log 'hello world'
# no semicolons
items = ['beer', 'wine', 'spirits']
# backwards conditionals
beerLover = true if items.length > 0
# while items.length > 0 then drinkBeer()
# sort of iterators
for item in items
console.log item
for i in [5..0]
console.log i
console.log 'blast off'
nums = [0..20]
console.log nums
# generates this in one way
nums = [0..21]
console.log nums
# generates this in another way, to save space
for drink in items
console.log drink if drink != 'spirits'
# trickier conditionals
for drink in items when drink isnt 'spirits'
console.log drink
# this is the syntax for a function
drinkBeer = ->
console.log 'Glug glug'
drinkBeer()
# this is the syntax for a function
drinkBeer = ->
console.log 'Glug glug'
items.pop()
while items.length > 0 then drinkBeer()
#a function with an argument
hello = (target)->
console.log 'hello ' + target
hello 'world'
#obejcts
#the js way
# fridge = {
# beer: [],
# chips: [],
# consume: function(){
# }
# }
fridge =
beer: ['cider', 'vb', 'tooheys', 'guiness']
chips: ['potato chips', 'corn chips']
consume: ->
console.log('nom nom nom')
@beer.pop()
# @beer means this.beer
fridge.consume()
fridge.consume()
console.log fridge
# jquery
# $ ->
#can also write document.ready as above
$(document).ready ->
$('#wonderland').on 'click', ->
console.log 'there was a click'
# you can do up to and includin as well as up to
nums = [0...5]
console.log nums
nums = [0..5]
console.log nums
# you can alos use interpolation, or something similar
####Homework
https://www.codeschool.com/courses/coffeescript
##Tuesday
###Warmup
https://gist.github.com/anonymous-wolf/688b3ffa222af9bd03e6
https://gist.github.com/anonymous-wolf/b74c2f2ac5afcee8b783
###Backbone Intro
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Backbone zoo</title>
<script src="js/jquery.js"></script>
<script src="js/underscore.js"></script>
<script src="js/backbone.js"></script>
<script src="js/intro.js"></script>
</head>
<body>
<div id="main"></div>
<script type="text/x-underscore-template" id="animal-template">
<p><%= type %> lives in the <%=ecosystem%> and has <%=stripes%> stipes.</p>
</script>
<!-- it doesnt matter what the type is as long as its not js
but at least calling it x-underscore-template somebody reading
will know that its not javascript -->
<!-- so this will be my template that knows how to print out an animal -->
</body>
</html>
// console.log(_, Backbone);
var Animal = Backbone.Model.extend({
defaults: {
type: 'animal',
ecosystem: '',
stripes: 0
},
initialize: function() {
console.log('I am a new animal');
this.on('change:type', function(model){
var type = model.get('type');
console.log('I am now ' + type)
});
}
});
var Zoo = Backbone.Collection.extend({
model: Animal
});
var animal1 = new Animal({type: 'frog', ecosystem: 'pond', stripes: 30})
var animal2 = new Animal({type: 'dog', ecosystem: 'house', stripes: 40})
var animal3 = new Animal({type: 'bat', ecosystem: 'cave'})
var gaZoo = new Zoo([animal1, animal2, animal3]);
var ZooView = Backbone.View.extend({
el: '#main',
initialize: function(){
console.log('ZooView init');
},
events:{
'click p': 'animalClick'
},
render: function(){
// this.$el.html('blah blah blah');
// gets me the jquery version of this
// console.log('Associated collection', this.collection);
var view = this;
var animalTemplate = $('#animal-template').html();
// this line gets me the html inside of my template
var animalHTML = _.template(animalTemplate);
this.collection.each(function(animal){
// var $p = $('<p/>');
// $p.text(animal.get('type'));
view.$el.append(animalHTML(animal.toJSON()));
})
},
animalClick: function(){
console.log('you clicked on an animal');
}
});
// need to set the value of this to view, and then refer to view,
// because the value of this gets clobbered when you go into an each loop
// so i hang onto the value of this when it is what i want it to be
var AppRouter = Backbone.Router.extend({
routes: {
'': 'index',
'animals/:id': 'viewAnimal'
},
index: function(){
console.log('you found the home page');
var zooView = new ZooView({collection: gaZoo});
// this is saying
// create me a new view for this collection andnow
// i will ahve access to the view within this collection
zooView.render();
},
viewAnimal: function(){
console.log('you are viewing an anaimal');
}
});
$(document).ready(function(){
var router = new AppRouter();
Backbone.history.start();
// the above two lines r rquired to start the router
});
// The first thing we do with backbone is our models
// when we load the page its going to fetch underscore and backbone
// and then my intro js code
// then its goign ot define this class of Animal
// what we are going to end up building is a zoo,
// and the thing that we have inside of a zoo is animals, so
// i've created a class of animals
// ive made a zoo Collection
// you can think of a collection as being similar to an array
// but it has some extra features you can play with
// in the console:
// var zebra = new Animal ({type: 'zebra', ecosystem: 'savanna', stripes: 100})
// I am a new animal
// undefined
// zebra.attributes
// Object {type: "zebra", ecosystem: "savanna", stripes: 100}
// zebra.set('type', 'butterfly')
// I am now butterfly
// child {cid: "c1", attributes: Object, _changing: false, _previousAttributes: Object, changed: Object…}
// zebra.attributes
// Object {type: "butterfly", ecosystem: "savanna", stripes: 100}
// gaZoo.each(function(a){
// console.log(a.get('ecosystem'));
// });
// gaZoo.toJSON();
// gaZoo.sortBy(function(model){
// return model.get('stripes');
// });
// returns a list of animals in ascending order by number of stripes
// we can find things with our underscore methods: http://underscorejs.org/#collections
// var ZooView = Backbone.View.extend({
// el: '#main',
// this is telling the view where to appear on the page
// });
// need to initiate a view
// and call the render function
// animal.type
// animal.attributes
// animal.toJSON()
// typeof animal.toJSON()
// the view is the thing that knows how to show the collection on the page
// if theyre to do with this view, all of the events will be grouped toegther in the view
###Blog
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Backbone Blog</title>
<script src="js/jquery.js"></script>
<script src="js/underscore.js"></script>
<script src="js/backbone.js"></script>
<script src="js/blog.js"></script>
</head>
<body>
<a href="#">Home</a>
<a href="#posts/post-about-chico">Featured post</a>
<div id="main"></div>
<script id="appView-template" type="text/x-template">
<h2>Recent posts</h2>
<ul id="posts"></ul>
</script>
<script id="postListView-template" type="text/x-underscore-template">
<h3><%= title %></h3>
</script>
<script id="postView-template" type="text/x-underscore-template">
<h2><%= title %></h2>
<p><%= content %></p>
</script>
</body>
</html>
// console.log(_, Backbone, jQuery);
// kind of like the rails router with embedded actions
// makes the application have meaningful urls
var AppRouter = Backbone.Router.extend({
routes: {
'':'index',
'posts/:id':'viewPost'
},
index: function(){
// if (today === "Sunday"){
// var appView = new AppView({collection: blogPosts});
// }else{
// var appView = new AppView({collection: funnyPosts});
// }
// console.log('routed to index');
var appView = new AppView({collection: blogPosts});
appView.render();
},
viewPost:function(slug){
// console.log('viewing post', id);
var post = blogPosts.get(slug);
console.log(post.toJSON());
var postView = new PostView({model: post});
postView.render();
}
});
// our models as per rails
// the defaults are similar to a schema
var Post = Backbone.Model.extend({
idAttribute: 'slug',
defaults: {
title: 'New Post',
content: 'New post content'
}
});
// this is really just part of our model, but is like a fancy array
// for storying a collection of models
// uses underscore.js to give us activerecordish methods like .get
var Posts = Backbone.Collection.extend({
model: Post
});
// seed data -- later we will retreive these from the server via ajax instead
var blogPosts = new Posts([
new Post({id: 1, slug: 'post-about-hotdogs', title: 'Post 1', content: 'Post 1 Content'}),
new Post({id: 2, slug: 'post-about-chico', title: 'Post 2', content: 'Post 2 Content'}),
new Post({id: 3, slug: 'post-about-harpo', title: 'Post 3', content: 'Post 3 Content'}),
new Post({id: 4, slug: 'post-about-groucho', title: 'Post 4', content: 'Post 4 Content'})
]);
// like rails views, but with the event handling stored here as well
// responsible for showing data on the page, but also allowing interaction
var AppView = Backbone.View.extend({
// defines the selector which this view is associated with
el: '#main',
render: function(){
console.log('rendering AppView', this.collection);
var appViewHTML = $('#appView-template').html();
this.$el.html(appViewHTML);
this.collection.each(function(post){
// console.log(post);
var postListView = new PostListView({model: post});
postListView.render();
});
}
});
var PostListView = Backbone.View.extend({
// new post list view will use this to create a new li
tagName: 'li',
events: {
'click': 'showPost'
// anytime there is a click anywehre in this view, call a method called showPost
},
render: function() {
// console.log('rendering post list view', this.model);
// fetch and compile the template
var postListViewTemplate = $('#postListView-template').html();
var postListViewHTML = _.template(postListViewTemplate);
// set the content of this views element to be the template for this model
this.$el.html(postListViewHTML(this.model.toJSON()));
// append this view's element to the posts ul
$('#posts').append(this.$el);
},
showPost: function(){
console.log('showing post', this.model.get('title'));
appRouter.navigate('posts/' + this.model.get('slug'), true);
}
});
var PostView = Backbone.View.extend({
el: '#main',
render: function(){
var postViewTemplate = $('#postView-template').html();
var postViewHTML = _.template(postViewTemplate);
this.$el.html(postViewHTML(this.model.toJSON()));
}
});
// this is global so we can access it inside certain views
var appRouter = new AppRouter();
$(document).ready(function(){
// this kicks off the router and makes the back button and fwd button work
Backbone.history.start();
});
// when we visit the route with no text in it
// that goes to the index function
// which jsut sasy go and get me a new view
// and rendew appview in it
// the lower case appView is the instance of the new AppView
// ====
// in the console:
// p1 = new Post()
// child {cid: "c2", attributes: Object, _changing: false, _previousAttributes: Object, changed: Object…}
// p1.attributes
// Object {title: "New Post", content: "New post content"}
// there are two ways of assicationg a view with a selector on the page
// el: '#main'
// taganme li means everytime we create a new instance of the post list view it will create a new li
###Addy's Todos
###RORO
###Homework
- https://backbonetutorials.com/
- http://addyosmani.github.io/backbone-fundamentals/#exercise-1-todos---your-first-backbone.js-app
##Wednesday
###Warmup
PlingPlangPlong:
https://gist.github.com/anonymous-wolf/89c0abb1dae801d7cab9
https://gist.github.com/anonymous-wolf/a91806ceea2980308751
###Backbone Folders
###Backbone Ajax fetch()
Animals example:
To make an animal have web stuff behind it all you need to do is specify a url root.
urlRoot: '/animals',
If I Create a new animal in the console
butterfly = new Animal({id: 75});
All I need to do is say butterfly.fetch()
spider = new Animal()
spider.set({type: 'hunchback', ecosystem: 'belltower'})
spider.save();
You can jsut call the fetch and save methods and you can just get back whatever data you need
###Backbone blog + Rails
rails new backboneblog-rails -T
the first thing to do is get rid of turbolinks from the Gemfile and the application.js
bundle
rails generate scaffold Posts title:string content:text
rake db:migrate
annotate
we can request http://localhost:3000/posts.json
the scaffolding magic makes this work
it goes to the views folder and looks for index.json.jBuilder file
but we deleted this file and included it ourselves in the posts controller, because joel prefers this way!
def index
@posts = Post.all
respond_to do |format|
format.html {}
format.json {render :json => @posts}
end
end
this enables me to request the html version or the json version and it gives it back to me
burning airlines pro-tip: backbone gem for rails
https://github.com/codebrew/backbone-rails
this will go and scaffold out the backbone code from the beginning
because this is going to be a single page application i want a single page
root :to => 'posts#landing' in my routes
in my controller
def landing
end
create landing.html.erb in my views
included this in our blog.js file:
_.templateSettings = { interpolate: /{{(.+?)}}/g };
and changed our landing.html.erb file from erb tags to handlebars curly brackets
app.Posts = Backbone.Collection.extend({
url: '/posts',
model: app.Post
});
addded the url to the posts.js file so that it knows where to make the request on the server
removed the seed data
in blog.js, we moved the router into a .done function to happen after the posts are loaded
when we are d0ing burning airlines we will need to make fetch requests to flights and seats etc
--
changed the slugs to ids
in post.js and postListView
How to remove the # from the url - best not to do this:
In the blog.js file add pushState: true
Backbone.history.start({pushState: true});
this makes the url prettier, but when i send this link to someone and they open it in a new tab
what happens is that they see it in a completely different page
if you use pushState navigation, it makes it look like a regular url, but when sent to a friend, the request that comes into the server it just posts/3, so somebody loading the page is no longer inside the backbone app, and they have the ability to edit, delete, etc
have to look up tutorials to make sure this doesnt happen, but it's a lot of effort to get rid of a hash!
also, pushState is also only supported by some brosers
to get browser support for pushState:
pushState: Modernizr.history
include this in application.html.erb
<script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.8.3/modernizr.min.js"></script>
we will look at polling requests for buringin airlines, where we make the fetch request happen every second, as opposed to every time the page loads
###Homework
exercise 2:
http://addyosmani.github.io/backbone-fundamentals/#Exercise 2: Book Library - Your First RESTful Backbone.js App
##Thursday
###Warmup
Prime Factors:
https://gist.github.com/anonymous-wolf/53bf5af6f4df75bf882c
https://gist.github.com/anonymous-wolf/056364f6f783123ab43d
https://github.com/wofockham/wdi-8/blob/master/0x-bonus/prime_factors/prime_factors.rb
###Backbone Ajax Post
Backbone forms:
https://github.com/powmedia/backbone-forms
In our intro / animals app:
<script src="js/backbone-forms.js"></script>curl https://raw.githubusercontent.com/powmedia/backbone-forms/master/distribution/backbone-forms.js > js/backbone-forms.js
add into intro.js
schema: {
type: 'Text'
ecosystem: 'Text',
stripes: 'Number'
// in the animal model there will be this schema key, its only the form that
// cares about it
},
Creating a new form and appending to the page:
In the console:
var animal = new Animal()
intro.js:14 I am a new animal
undefined
var form = new Backbone.Form({model: animal})
undefined
form.render()
child {cid: "view6", model: child, $el: jQuery.fn.init[1], el: form, options: Object…}$el: jQuery.fn.init[1]Field: function (){ return parent.apply(this, arguments); }Fieldset: function (){ return parent.apply(this, arguments); }NestedField: function (){ return parent.apply(this, arguments); }cid: "view6"el: formfields: Objectfieldsets: Array[1]model: childoptions: Objectschema: ObjectselectedFields: Array[0]template: function (data) {__proto__: Surrogate
form.el
$('body').append(form.el)
[]
```
```
var form = new Backbone.Form({model: animal})
undefined
form.render()
child {cid: "view8", model: child, $el: jQuery.fn.init[1], el: form, options: Object…}
$('body').append(form.el)
[]
```
**Event listen to ... comments can be appended to the page three times - watch out for this with burning arlines.**
####Generating a Comment model in backbone blog
rails generate model Comment post_id:integer author:string :content:text
c1 = Comment.create :author => 'Bilbo', :content => 'Cool post dude'
then set association
or to set up the association from the start:
p1 = Post.find(1)
p1.comments.create :author => 'Bilbo', :content => 'Cool post dude'
then create the comment js collection and comment js model
now we need to render the views
then we need a comment view and a template
###JS Ecosystem
http://coldhead.ninth.su/jsecosystem/#/
###Georgina from Lookahead Search
###Backbone flow
the js files jquery, backbone and underscore are loaded before any of our js files are loaded
those all need to appear first in my head
underscore needs to appear first because the others depend on it
the next thing we do is load in our models, collections and views
because of the way we start it with var app = app || {}, we can load them in any order
but good way is to group all models, collections, then views together
then by the time we get down to our blog js, which is where our code actually starts, we have defined our entire app object
so app now has some things hanging off it:
- it has Post and
- Posts (collection - which knows where to fetch the instance)
- comment
- appview
- postview
so we now have all the things that we actually need loaded in memory
then the way we actually make things start happening is by starting the router, the backbone history line
so in our blog.js file
we know we actually need a tangible collection, not a class of posts, but an instance of the posts collection
the browser will take note of this function, finish loading other js files, run the html, leave the templates alone, get to the bottom of the body, and at that point the document ready fires
the document ready function says if the number of elements on the page with the id of main is 0, abandon everything
but if we do have the right div, it will keep running
(note - use handlebars template for burning airlines)
then we say this app is going to depend on some blog posts
in our app.posts, we have a url where it knows it can go and get its posts from, on line 6 of posts.js it says this ur '/posts', which matches up with the routes.. if you make a get request to /posts, that gives you all of the posts
so we say go and fetch me the blog posts, and only when it is done, should the next thing happen
so once the document is ready, it fires off the fetch request. once the request comes back and the fetch is done, we create the app router, because we now have something which is useful to the user, we have the blog posts
so the app router is ready to go, we have an instance of it, we can do routing as soon as backbone starts listening to changes in the hash, which is what backbone.history.start does
when the document ready is finished, the main container will be empty
the first thing that the approuter does is it looks for the route, which is '', and it will run the code in the index action (this is specified in the approuter)
so at this point, we have the route, and we have fetched the blog posts from the server
so we are in the index action and it says my job is to show the view on the page. first it creates an instance of the view, and then renders that view
when we created an instance of this view, it ran everything in our initiilize method (in the appview.js file), then everything in the render method for this view will happen.
this is the point at which it is getting the collection, going through each of the posts in the collection, and running the render chunk of code.
so each post is going to have its own view on the page, so the render for the postListView then gets called, which shoves it onto the page
the reason we had a postListView on its own was so that we could listen to something
so we now have a global appview, with our postlistviews
it has the ability to listen for an event, which is a click on a particular view
so as soon as you click on a view, the postlistview.js file tells it to showPost
and the showPost function uses the approuter to take us to the post/id url
the js says if someone clicks on this post item, go to /posts/4
so if i click on this the only thing that happens is it updates the url
so now we have said navigate to /posts/id
and the showpost function says , when this is true, it will take us back to the router
the router sees that we have changed the url, so the viewpost function is then called from the approuter
so the viewpost action runs, and any dynamic part gets passed in to the function (the id is dynamic because it is written ':id' with colon in front of it)
this function viewPost gets the post, creates an instance of the post view, and renders that post on the page
so then we go to the postview
the postView tells it where on the page it is going to go.
it replaces everything in the div with a main id
because we created a model, inside of here we have access to the model
so we go and find one of our templates, compile it using underscore, and pass in the json object for that model
we say for this view, go and get its element and make its html whatever the object gets back
so at this point we can see the single post on its own
now we are at the point where we want to see the comments underneath
this is where we make a second ajax request
we want to create a new instance of the comments collection for a particualr post
we say go and make this ajax request for the comments, and then the rest is dealt with in the comments collection itself
the comments collection says when someone initializes or instantiates a new comments collection, save the id so we can remember where it is getting its posts from
then we sya every time a new add event fires, we should run a function, which creates a view for that single comment, and renders that view
then we look at the comment view, which goes and compiles the template, uses that template for this model, and shoves the comment on the page
the final piece of ajax is when someone clicks on the submit comment button, we call the submit comment function,
we save the values of the author and content, we create a new comment objct that lives in backbones memory that knows the comment author and which post it belongs to
this comment now exists in backbone memory but the server has no idea about my comment and what it says
so now i need to persist this to the server
once we have created it in memory, we say save this comment for me
because save has a url associated with it, the comment is forever goign to be associated with this particular post
so that the user can see it on the page, we say once we are done saving this function, go and load all of the comments again, fetch them
so the fetch is called when you first render the comments on the post, but we call fetch a second time when a comment is saved
so my comment will appear at the bottom, and anybody elses comments that may have happened in the last couple of moments will also appear in there
the differnce between get and fetch:
- get is getting someting from the collection in the browser
- fetch goes and updates the array with whatever is on the server
if we then go to a specific blog post we want ot know whcih comments they have
AJAX - blog posts().fetch
AJAx - posts/3/comments
AJAX - .save
so the rails app really only has to do one thing
it serves up the very first page
and after that this has everyting we need to show us
after the first page is loaded, all of the interaction with rails is just through json
it's a single page app because this is a single page, and it goes and gets dynamic data by making requests to json
so the only html page that we have used is the root, thats the only html page that we serve up
##Friday
###Burning Airlines Lab
the admin thing
models associations seed data
one person do admin
one person do search
one person do the flight booking
https://gist.github.com/wofockham/7d9ef83a3362c8d4d8af