├── .gitignore ├── README.md ├── angular-base ├── index.html └── script.js ├── angular.md ├── angular ├── index.html └── script.js ├── backbone ├── index.html └── script.js ├── css └── style.css ├── ember ├── index.html └── js │ ├── app.js │ └── libs │ ├── ember-1.10.0.debug.js │ ├── ember-template-compiler-1.10.0.js │ └── jquery-1.10.2.js ├── frameworks.md ├── imgs ├── AngularJS-large.png ├── backbone.png ├── ember.png ├── express.png ├── mongodb.png ├── mvcbase.png ├── nodejs.png ├── npm-logo.png ├── npm-logo.svg └── react.png └── react ├── README.md ├── index.html ├── script.js └── script.jsx /.gitignore: -------------------------------------------------------------------------------- 1 | react/.module-cache -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #JS Frameworks 2 | 3 | A small lesson on the three popular frameworks for [HackerYou](http://hackeryou.com/). 4 | 5 | [Angular](https://angular.io/) 6 | [Backbone](http://backbonejs.org/) 7 | [Ember](http://emberjs.com/) 8 | [React](https://facebook.github.io/react/) 9 | [Node](https://nodejs.org/) 10 | [Express](http://expressjs.com/) 11 | [MongoDB](https://www.mongodb.org/) 12 | 13 | There is an example of the same app written in Angular, Backbone, and Ember. Each example can be found in their own folders in the repo. 14 | 15 | 16 | -------------------------------------------------------------------------------- /angular-base/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular Example 6 | 7 | 8 | 9 |
10 |
11 |

Rijks Museum

12 | 13 |
14 |
15 | 16 |
17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /angular-base/script.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rchristiani/js-frameworks/1997c9e51aae37ac0b4b7f3b0dfce318b9785005/angular-base/script.js -------------------------------------------------------------------------------- /angular.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ##Working with AngularJS 4 | We have talked about the main three frameworks that are popular today, lets actually dig into one of them and take a look at an example from a project that will look familiar. We will use the Rijks Museum API to create an simple application were we can display all the initial art, and also search for a term 5 | 6 | ##The Setup 7 | The first thing we need to do is set up our markup for the application. 8 | Lets keep it really simple, it will be a basic page, and we will just include angular at the bottom as well as out own script. A starting point can be found in the `angular-base` directory. 9 | 10 | 11 | 12 | 13 | 14 | Angular Example 15 | 16 | 17 |
18 |
Rijks Museum
19 |
20 |
21 |
22 | 23 | 24 | 25 | 26 | 27 | There are a few things to point out inside of this markup. Firs being the `ng-app="art-app"`. This is how we tell angular what markup to look at, what is considered to be our app. You do not have to have it on the `html` element if do not want but since the whole page will ne an app lets just do it anyways. The beauty of angular is that it can be used in a few places here and there, sort of like little widgets. 28 | 29 | The next is the `ng-controller="MainCtrl"` attribute. This we will look at in a second. As you will see as we go ahead with Angular is that it is very declarative. Meaning that we put a lot of our logic into the DOM. We will see more examples of this later. 30 | 31 | ##Initialize Application 32 | 33 | Inside of our `script.js` file we need to initialize our Angular application. We do this in a familiar way, although at first it might not look like that. 34 | 35 | var app = angular.module('art-app',[]); 36 | 37 | What we are doing here is creating an `app` variable, `art-app` refers to the name we put in the `ng-app` attribute tag in our markup. The variable `app` will be used as our namespace, similar to how we have been writing: 38 | 39 | var app = {}; 40 | app.init = function() { 41 | //Some stuff 42 | }; 43 | app.getArt = function() { 44 | //Some other stuff 45 | }; 46 | 47 | Remember when we added the `ng-controller="MainCtrl"` to our `main` tag, we can now define that controller in our JS file. 48 | 49 | app.controller('MainCtrl', function() { 50 | ... 51 | }); 52 | 53 | This is where we will be spending most of our time. If you remember in the MVC pattern the **Controller** is used to adjust our model, we will be doing that in here. 54 | 55 | In Angular the concept of the **Model** is a special variable called `$scope`. One more concept to look at is the **View** in Angular. Angular uses the HTML markup as its **View**, remember when I said that Angular is 'declarative' this is where that happens. 56 | 57 | Inside our markup, go to the `.art-work` section. Inside here will we will be making some changes. 58 | 59 |
60 | {{item}} 61 |
62 | 63 | There are a few things happening here. There is this new attribute called `ng-repeat`. This is a build-in function, also called a Directive, of sorts from Angular, in this case `ng-repeat` is like a for loop. The value of `ng-repeat` is `item in art`, we will define `art` in a second, but this is just like a for-in loop. 64 | 65 | for(var key in obj) { 66 | console.log(key); 67 | } 68 | 69 | You might have noticed that we wrap the `item` var in `{{}}`. What we are doing here is using some template tags in our HTML. Angular will go through our document when it loads and change these `{{}}` tags into the content that is inside them based on what we set. 70 | 71 | Back in our `script.js` files lets see this in action. Inside our `controller` lets add some data that we can show on the page. This is where `art` comings into action. 72 | 73 | app.controller('MainCtrl', function($scope) { 74 | $scope.art = ['Hey','There']; 75 | }); 76 | 77 | If you refresh the page you will now see two lines. Hey and There. The `ng-repeat` is looping over the array we have assigned to `$scope.art` and for each iteration spitting them out as `{{item}}`. If each item was an object we could go `{{item.name}}` and access properties on it. Notice how our data was automatically added to the page? This is something called Data Binding. Angular is constantly watching an waiting for out data to change, when it does it takes the appropriate actions. 78 | 79 | You will have noticed that I just added the `$scope` variable. Angular has something called Dependency Injection. This is a concept where Angular will resolve any parameters passed into our function and find appropriate values. In this case we need to have access to the `$scope`. We will see this in action later on as we will create something ourselves and inject it into this controller. 80 | 81 | ###Ajax in angular 82 | 83 | Built into Angular there is a service called `$http`. Think of this as the `$.ajax()` of angular. We will be using this to get our information from the api. 84 | 85 | If you remember we use the MVC pattern so we can separate our concerns, we could use the `$http` service inside our `controller` but that would make things to dependent, or tightly coupled. In Angular we have something called a factory. After our `app.controller...` on a new line add the following: 86 | 87 | 88 | app.factory('Art', function(){ 89 | ... 90 | }); 91 | 92 | Inside here we will write some reusable code for getting data from the api, as well as searching the api. Notice that the `.factory()` method takes two arguments, first a string, this will be used as the name of our factory. The second is a function we want to run when we use this factory. 93 | 94 | First lets add some variables for our api key and the url. 95 | 96 | app.factory('Art',function() { 97 | var apiKey = 'eO58IERD'; 98 | var apiUrl = 'https://www.rijksmuseum.nl/api/en/collection/?key=' + apiKey; 99 | }); 100 | 101 | This is great but now we need to actually make an ajax call. In order to do that we need the `$http` service that is built into Angular. To make it available to use we need to inject it into our factory function. Lets also pass in something called `$q`, we will discuss this in a bit. 102 | 103 | app.factory('Art', function($http,$q) { 104 | ... 105 | }); 106 | 107 | Great, now we have access to the `$http` service inside of our factory, which is very similar to `$.ajax`. From here we need to make the functions that we will use to get the art, and also search the api for a keyword. To do this we will return an object that has two methods on it, one called `getArt` and one called `search`. 108 | 109 | app.factory('Art',function() { 110 | var apiKey = 'eO58IERD'; 111 | var apiUrl = 'https://www.rijksmuseum.nl/api/en/collection/?key=' + apiKey; 112 | 113 | return { 114 | getArt: function() { 115 | //Gets all the art 116 | }, 117 | search: function(term) { 118 | //used to search for a specific term 119 | } 120 | } 121 | }); 122 | 123 | Lets dig into actually making the call to the api. Lets just see it in action, inside our `getArt` methods lets add this. 124 | 125 | getArt: function() { 126 | $http.get(apiUrl).success(function(results) { 127 | console.log(results); 128 | }); 129 | }, 130 | 131 | This call should look pretty familiar this is the pattern we use when we use `$.ajax` our results come back in the `.success` function. Great, we are getting data and we are almost there. I am just going to add some code below and we will go through it, it will be a little complex, but lets walk though it. 132 | 133 | getArt: function() { 134 | //We use a defered object to see if our request is ready 135 | var def = $q.defer(); 136 | //Make the request 137 | $http.get(apiUrl) 138 | //If it is success resolve the def 139 | .success(def.resolve) 140 | //If not reject it 141 | .error(def.reject); 142 | //Return the promise so we can 143 | return def.promise; 144 | }, 145 | 146 | Remember that `$q` service that we added? This is a little service for something called a Promise or a Deferred object. A Promise is the promise of an eventual action to happen. If you think about it like ordering something online. When you order something before it is shipped it would be considered to be *pending*. It your credit card gets rejected it would *reject* the order. If you receive the order at your home the company would *resolved* the order. In JS we can use this to wait until our ajax call comes back. 147 | 148 | Lets go back to our `MainCtrl` and actually use this function. Back in our controller we had initially injected `$scope` but with the magic of Angular we can now inject our `Art` service. That will now look something like this. 149 | 150 | app.controller('MainCtrl', function($scope,Art){ 151 | Art.getArt().then(function(result) { 152 | //When the promise is resolved the result is sent 153 | //to the callback in the .then() method 154 | $scope.art = result.artObjects; 155 | }); 156 | } 157 | 158 | I went ahead and added a bunch of stuff, but lets walk through what is happening. We call the `.getArt()` method from our `Art` service. Because we returned a `Promise` from the deferred object we can use this `.then()` method to be called when the promise is resolved, meaning we have data. When that happens the callback function gets passed the results of from our ajax call and we can go ahead and use them like we used our array at the beginning. 159 | 160 | If you remember from api project all the data was on a property called `.artObjects` if we set `$scope.art` to be equal to `result.artObjects` Angulars data binding will work its magic and inform the template in the HTML that is not has some information. It is instantly updated with that data. 161 | 162 | Lets change our template to show the image and the long title. 163 | 164 |
165 | 166 |

{{item.longTitle}}

167 |
168 | 169 | Now we are assign the title and image to HTML elements. If you refresh your page you should now see images. What about the situation when there is no image? Angular has another directive call `ng-if` that we can use to conditionally only show art with an image. 170 | 171 |
172 | 173 |

{{item.longTitle}}

174 |
175 | 176 | We add `ng-if="item.webImage.url != null"` to the `.art-item` div, now only items that have an image will be displayed! 177 | 178 | ###What about searching? 179 | 180 | Lets add a search form to our page in the header that we can use to get a search value. 181 | 182 |
183 |

Rijks Museum

184 | 188 |
189 | 190 | There are two things that are not normal here. On the text `input` there is this `ng-model` attribute. This is another Angular directive we can use the value of this inside of our controller later. We also have another directive called `ng-click`. As you can imagine this is binding a click event and calling some function that we define when it is clicked. In this case the `search()` function. 191 | 192 | Lets jump back into our controller and define this `search` function. 193 | 194 | $scope.search = function() { 195 | //Get the query from the ng-model 196 | var query = $scope.searchQuery; 197 | //Send that query to your .seach() method in the factory 198 | Art.search(query).then(function(result) { 199 | //When it resolves we get our art 200 | $scope.art = result.artObjects; 201 | }); 202 | }; 203 | 204 | I added a bunch more things here. First thing I do is add this `search` function to our `$scope`. Next I use that `searchQuery` model I set up early to to get the value of our form, we can use that and pass it to our `Art.search()` method. 205 | 206 | Inside our `Art` factory we should now add a `search` method to the returned object. This will look almost identical to the `getArt` one. The only difference being the argument we pass in and append to our url. 207 | 208 | search: function(serchTerm) { 209 | //Use a defered object to see if our request is ready 210 | var def = $q.defer(); 211 | //Make our request 212 | $http.get(apiUrl+'&q='+serchTerm) 213 | //If it is successful resolve the def 214 | .success(def.resolve) 215 | //else reject it 216 | .error(def.reject); 217 | //Return the promise 218 | return def.promise; 219 | } 220 | 221 | ##Conclusion 222 | 223 | That is about it for this example. This is not super in depth example, more a quick look at how you would use a framework like this to build something you already know now to build. 224 | 225 | -------------------------------------------------------------------------------- /angular/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular Example 6 | 7 | 8 | 9 |
10 |
11 |

Rijks Museum

12 | 16 |
17 |
18 |
19 | 20 |

{{item.longTitle}}

21 |
22 |
23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /angular/script.js: -------------------------------------------------------------------------------- 1 | //Initialize our application 2 | var app = angular.module('art-app',[]); 3 | //Add main controller 4 | app.controller('MainCtrl', function($scope,Art){ 5 | //When the page is loaded get info to fill the page 6 | Art.getArt().then(function(result) { 7 | //When the promise is resolved the result is sent 8 | //to the callback in the .then() method 9 | $scope.art = result.artObjects; 10 | }); 11 | //Search function on our scope 12 | $scope.search = function() { 13 | //Get the query from the ng-model 14 | var query = $scope.searchQuery; 15 | //Send that query to your .seach() method in the factory 16 | Art.search(query).then(function(result) { 17 | //When it resolves we get our art 18 | $scope.art = result.artObjects; 19 | }); 20 | }; 21 | }); 22 | //Factory for getting the Art 23 | app.factory('Art', function($http,$q) { 24 | var apiKey = 'pUaGTYo5'; 25 | var apiUrl = 'https://www.rijksmuseum.nl/api/en/collection/?key=' + apiKey; 26 | 27 | //Return an object with our methods 28 | return { 29 | getArt: function() { 30 | //We use a defered object to see if our request is ready 31 | var def = $q.defer(); 32 | //Make the request 33 | $http.get(apiUrl) 34 | //If it is success resolve the def 35 | .success(def.resolve) 36 | //If not reject it 37 | .error(def.reject); 38 | 39 | //Return the promise so we can 40 | return def.promise; 41 | }, 42 | search: function(serchTerm) { 43 | //Use a defered object to see if our request is ready 44 | var def = $q.defer(); 45 | //Make our request 46 | $http.get(apiUrl+'&q='+serchTerm) 47 | //If it is successful resolve the def 48 | .success(def.resolve) 49 | //else reject it 50 | .error(def.reject); 51 | //Return the promise 52 | return def.promise; 53 | } 54 | }; 55 | }); -------------------------------------------------------------------------------- /backbone/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Backbone Example 6 | 7 | 8 | 20 | 21 | 22 | 34 | 35 | 36 |
37 | 38 |
39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /backbone/script.js: -------------------------------------------------------------------------------- 1 | var app = {} 2 | app.apiKey = 'pUaGTYo5'; 3 | app.apiUrl = 'https://www.rijksmuseum.nl/api/en/collection/?key=' + app.apiKey; 4 | //Our main view 5 | app.MainView = Backbone.View.extend({ 6 | //The element we want to append everything too 7 | el: '.main-section', 8 | //Our template, this is in the head of our html 9 | //We are use the undercores templating engine 10 | template: _.template($('#main-template').html()), 11 | //We set up a simple object for our events 12 | events: { 13 | 'click input[type="submit"]' : 'searchTerm' 14 | }, 15 | //In backbone, everytime you call new on an object an 16 | //Initialize function it run 17 | initialize: function() { 18 | //Where we render our template to the DOM 19 | this.render(); 20 | //We make a new collection 21 | this.collection = new app.ArtCollection(); 22 | //We listen for a reset event on the collection 23 | //And when that is triggered we want to render the artwork 24 | this.listenTo(this.collection,'reset', this.renderArtwork); 25 | //We then fetch the collection using .fetch and passing reset:true to make sure 26 | //The reset event is triggered 27 | this.collection.fetch({reset:true}); 28 | }, 29 | render: function() { 30 | //A simple render method, we are just appending our template to the html 31 | this.$el.append(this.template()); 32 | }, 33 | renderArtwork: function() { 34 | //We use underscores .each method to loop through all the collected models 35 | _.each(this.collection.models, function(model) { 36 | //For each item we create a new subview 37 | var artView = new app.ArtWorkView({ 38 | //We pass in the model data in to the view 39 | model: model 40 | }); 41 | }); 42 | }, 43 | searchTerm: function() { 44 | //this function is called when we click submit on the form 45 | //To make sure we have a reference to our main view we store it in a var 46 | //called self 47 | var self = this; 48 | //We then grab the query from the input 49 | var query = this.$el.find('input[type="text"]').val(); 50 | //We reset it 51 | this.$el.find('input[type="text"]').val(''); 52 | //We create a new collection just for searching 53 | var searchCollection = new app.SearchArtWork(); 54 | //We add a property called searchterm on it to be used in the collection 55 | //We give this the value of our query 56 | searchCollection.searchTerm = query; 57 | //And we call fetch on it. 58 | searchCollection.fetch({ 59 | //Fetch can take an object, much like $.ajax that has a 60 | //Success method on it. 61 | success: function(res) { 62 | //In here we empty our initial view 63 | self.$el.find('.art-work').empty(); 64 | //Set the collection to be our results 65 | self.collection = res; 66 | //And then call renderArtwork to put new results on the page. 67 | self.renderArtwork(); 68 | } 69 | }) 70 | } 71 | }); 72 | 73 | //A simple model is needed to make sure backbone knows 74 | //What to do with our data 75 | app.ArtModel = Backbone.Model.extend({}); 76 | 77 | //Are initial collection 78 | app.ArtCollection = Backbone.Collection.extend({ 79 | //We assign our model 80 | //So that when we get a result back, the vales get mapped to our model 81 | model: app.ArtModel, 82 | //We set up the url for backbone to call when we call .fetch 83 | url: app.apiUrl, 84 | //We have this handy parse method so that we can just return our 85 | //Array of data nicely. 86 | parse: function(res) { 87 | //Returning just the art objects. 88 | return res.artObjects; 89 | } 90 | }); 91 | 92 | //Our simple Art wOrk View 93 | app.ArtWorkView = Backbone.View.extend({ 94 | //Grabbing our template 95 | template: _.template($('#art-work').html()), 96 | initialize: function() { 97 | //Rendering this view 98 | this.render(); 99 | }, 100 | render: function() { 101 | //Find the container we want to put our view in 102 | //And append it 103 | $('.art-work').append(this.template(this.model.toJSON())); 104 | } 105 | }); 106 | 107 | //Our search Collection 108 | app.SearchArtWork = Backbone.Collection.extend({ 109 | //Again setting the model we want 110 | model: app.ArtModel, 111 | url: function() { 112 | //For the url this time we use a function 113 | //So we can return the proper url 114 | //Based on the searchTerm 115 | return app.apiUrl + '&q=' + this.searchTerm 116 | }, 117 | //Again parsing the results so we can get what we want 118 | parse: function(res) { 119 | return res.artObjects; 120 | } 121 | }) 122 | 123 | $(function() { 124 | //Start it all off with the initial main view 125 | var view = new app.MainView(); 126 | }); -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0} 2 | .main-section { 3 | width: 80%; 4 | margin: 0 auto; 5 | } 6 | header { 7 | background: black; 8 | color: #fff; 9 | padding: 50px; 10 | margin-bottom: 50px; 11 | } 12 | header h1 { 13 | display: inline-block; 14 | } 15 | .search-box { 16 | float: right; 17 | margin-top: 17px; 18 | } 19 | input[type="text"] { 20 | height: 35px; 21 | width: 300px; 22 | border: none; 23 | color: #000; 24 | padding-left: 15px; 25 | } 26 | input[type="submit"] { 27 | border: 1px solid #fff; 28 | background: none; 29 | padding: 10px 15px; 30 | } 31 | input[type="text"], 32 | input[type="submit"] { 33 | border-radius: 5px; 34 | } 35 | .art-work { 36 | -webkit-columns: 3; 37 | -moz-columns: 3; 38 | columns: 3; 39 | -webkit-column-break-inside: avoid; 40 | page-break-inside: avoid; 41 | break-inside: avoid; 42 | } 43 | .art-item { 44 | border-radius: 5px; 45 | border: 1px solid #eee; 46 | margin-bottom: 25px; 47 | padding: 10px; 48 | } 49 | .art-item img { 50 | width: 100%; 51 | } 52 | -------------------------------------------------------------------------------- /ember/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Ember Example 6 | 7 | 8 | 37 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /ember/js/app.js: -------------------------------------------------------------------------------- 1 | //Create the Ember Application 2 | App = Ember.Application.create(); 3 | 4 | //Ember has a strict naming convention 5 | //If we are at the home page, this is considered the Index 6 | //Everything must we prefixed as such 7 | App.IndexRoute = Ember.Route.extend({ 8 | model: function() { 9 | //Return our initial results as the model property 10 | //for the index route 11 | return $.ajax({ 12 | method: "GET", 13 | url: 'https://www.rijksmuseum.nl/api/en/collection/', 14 | format: 'jsonp', 15 | data : { 16 | key: 'pUaGTYo5' 17 | }, 18 | }).then(function(res) { 19 | return res.artObjects; 20 | }); 21 | } 22 | }); 23 | 24 | //A controller is used here to handle any actions or changing of data 25 | App.IndexController = Ember.Controller.extend({ 26 | actions: { 27 | //This is an action to be called from the template 28 | searchArt: function() { 29 | var self = this; 30 | $.ajax({ 31 | method: "GET", 32 | url: 'https://www.rijksmuseum.nl/api/en/collection/', 33 | format: 'jsonp', 34 | data : { 35 | key: 'pUaGTYo5', 36 | //searchQuery comes from our template as we 37 | //Define it as a value on an input in there 38 | //This automatically gets bound to the controller and route 39 | q: self.searchQuery 40 | }, 41 | }).then(function(res) { 42 | //Reset our model with the results 43 | //Data binding will handle the notification to the the template 44 | self.set('model', res.artObjects); 45 | }); 46 | } 47 | } 48 | }); 49 | -------------------------------------------------------------------------------- /frameworks.md: -------------------------------------------------------------------------------- 1 | #JS Frameworks 2 | If you have been looking at job listings lately, as I am sure a lot of you have, you will have seen a few terms popping up over and over. 3 | 4 | > Experience with AngularJs 5 | 6 | or maybe 7 | 8 | > Experience with modern client-side application frameworks (Backbone.js, Ember.js, Angular.js) 9 | 10 | ##What are these? 11 | After seeing these terms you might have done some searching and ending up at their websites, or someones blog post about them. The are JavaScript libraries that have bean created to help make our lives easier when it comes to writing large complex JS applications. 12 | 13 | ##Why do we have these 14 | 15 | These frameworks/libraries came out of a need for better organization and separation of concerns when it came to writing larger JavaScript applications in the browser. As you have been getting into using jQuery to build more interactive applications you might notice that you end up doing a lot in some functions, when really we should keep things as simple as possible. These libraries try to emulate a design pattern called MVC. 16 | 17 | ##What is MVC but first What is a Design Pattern? 18 | MVC, or Model View Controller, is a design pattern that was developed back in the 70's for the smalltalk-80 language. A design pattern is a reusable solution that can be used to solve common problems. 19 | 20 | For example, we have been using namespaceing to organize our JS: 21 | 22 | var app = {}; 23 | app.myFunction() {}; 24 | 25 | The problem this little pattern solves is spaghetti code, before this we would just create functions and call them from anywhere, there was no organization. Using the namespacing pattern, we can easily group things together. If you have a bunch of events we need to fire, we could have a `app.events()` method. 26 | 27 | MVC solves the problem of keeping our Model(data) separate from our View and keeping our logic inside a Controller. 28 | 29 | A diagram might look something like this: 30 | 31 | ![MVC Diagram](imgs/mvcbase.png) 32 | 33 | The basic idea is that the **View** listens for events on it, when an action is triggered it talks to the **Controller** which then makes changes to the **Model** which then triggers a change in the **View** to reflect the models new information. 34 | 35 | That is a pretty brief example of how they might work. Lets talk a little about the three main JS frameworks that you will see in a posting. And then we will dive into the most popular, AngularJS. 36 | 37 | ###Backbone 38 | *** 39 | ![Backbone Logo](imgs/backbone.png) 40 | 41 | Backbone is by far the most mature and oldest of the three main frameworks. And because of that reason it is not super talked about these days. In my opinion though, it is a solid library and my favourite. 42 | 43 | It is very small, compared to Angular or Ember. Because of this you have to be in control of a lot of things. We will see that in Angular and Ember they sort of do some *magic* that happens, when we update something they automatically make some change somewhere else. However in Backbone you have to me more concerned with what is happening. It is more of a manual process. If you make a change to some data, you have to set up the next set, does a view get updated? Does it get saved to a database somewhere? 44 | 45 | Backbone is a very small library, you can actually read the whole thing is you wanted [HERE](http://backbonejs.org/docs/backbone.html). Backbone and Underscore(a js library backbone depends on) are both really well documented. Because of this Backbone is often forked and extended into new, fuller featured, versions. 46 | 47 | There are many version of it, the two most popular are [MarionetteJs](http://marionettejs.com/) and [AmpersandJS](https://ampersandjs.com/). 48 | 49 | Some sample Backbone code would look like this: 50 | 51 | app.ArtWorkView = Backbone.View.extend({ 52 | //Grabbing our template 53 | template: _.template($('#art-work').html()), 54 | initialize: function() { 55 | //Rendering this view 56 | this.render(); 57 | }, 58 | render: function() { 59 | //Find the container we want to put our view in 60 | //And append it 61 | $('.art-work').append(this.template(this.model.toJSON())); 62 | } 63 | }); 64 | 65 | This is taken from the [Backbone Example application](backbone/index.html) 66 | 67 | Some sites in the wild that use Backbone as sites like [Rdio](http://rdio.com),[Trello](http://trello.com) and [Airbnb](https://www.airbnb.ca/) 68 | 69 | ###Ember 70 | *** 71 | 72 | ![Ember Logo](imgs/ember.png) 73 | 74 | Ember is by far the largest and most difficult to get into. The learning curve is pretty steep. The tag line for Ember is *A framework for creating ambitious web applications*. The goal for Ember however is that once you get over that hump, things become very easy you don't have to do much thinking about how something works. Ember is a very opinionated framework, meaning that there is really very few ways to do something in Ember. Angular and Backbone have a bit more freedom as to how you perform a task or name something. In Ember everything is tightly controlled. 75 | 76 | For example, if I want to create a route for a homepage, it would be called index. I would have to make an ember controller call `IndexController`: 77 | 78 | So it might look something like this: 79 | 80 | App.IndexController = Ember.Controller.extend({ 81 | //Controller code in here 82 | }); 83 | 84 | If I wanted a route for that same page I would follow the same patter: 85 | 86 | App.IndexRoute = Ember.Route.extend({ 87 | //Route stuff goes here 88 | }); 89 | 90 | Ember has been picking up a lot of steam lately. Unlike Backbone, Ember is in constant development and because of that it is constantly getting better and changing. But because of that it can be hard to follow and keep up with. 91 | 92 | I have built an example application [HERE](ember/index.html) using Ember. 93 | 94 | Some example applications that have been build using Ember are [Fresh Books](http://www.freshbooks.com/) and [Discourse](http://www.discourse.org/). These are large complex applications that need to be organized well. 95 | 96 | ###React 97 | *** 98 | ![React Logo](imgs/react.png) 99 | 100 | React is a newer library from Facebook. The idea of React is to focus on creating fast interactive UI. It focuses on render speed and uses something called JSX as their template. 101 | 102 | One thing that was introduced is this concept of the "virtual DOM". Because of this it makes reacts rendering of a page very very fast. 103 | 104 | var PageTitle = React.createClass({ 105 | render: function() { 106 | return

Hello {this.props.title}

; 107 | } 108 | }); 109 | 110 | React.render(, document.body); 111 | 112 | React Art App example coming soon. 113 | 114 | Examples of applications build with React are [Instagram](https://instagram.com/) and more and more UI element from [Facebook](https://facebook.com) 115 | 116 | 117 | 118 | ###Angular 119 | *** 120 | ![Angular Logo](imgs/AngularJS-large.png) 121 | 122 | Angular is by far the most popular library at the moment. I think a lot of that has to do with the fact that it is being developed at Google, that is a name people see to like. Much like the other two, Angular follows similar conventions. Unlike Ember however, you have a bit more freedom to work with. 123 | 124 | Angular also has a lot of built in features for you. It has services for making ajax calls, getting animations started quickly, simple stuff like scrolling to an anchor link on your page even. And as discussed early, there is a lot of magic in AngularJS. In Backbone you do a lot of manual work, in Angular things just work for you. This can be a good and a bad thing. 125 | 126 | Some example Angular code might look like this: 127 | 128 | app.controller('MainCtrl', function($scope,Art){ 129 | //When the page is loaded get info to fill the page 130 | Art.getArt().then(function(result) { 131 | //When the promise is resolved the result is sent 132 | //to the callback in the .then() method 133 | $scope.art = result.artObjects; 134 | }); 135 | //Search function on our scope 136 | $scope.search = function() { 137 | //Get the query from the ng-model 138 | var query = $scope.searchQuery; 139 | //Send that query to your .seach() method in the factory 140 | Art.search(query).then(function(result) { 141 | //When it resolves we get our art 142 | $scope.art = result.artObjects; 143 | }); 144 | }; 145 | }); 146 | 147 | Unlike Backbone or Ember where you notice we did stuff like `var view = Backbone.View.extend()` or `var IndexControler = Ember.Controller.extend()`. Angular works in this fashion of using methods on the Angular object to create controllers or whatever it is you are creating. 148 | 149 | I have built an example application [HERE](angular/index.html) using Angular. 150 | 151 | Example applications using AngularJS: [PS3 Youtube app](http://www.playstation.com/en-us/explore/playstationnetwork/entertainment/youtube/) and [Wanderlust](http://mywanderlust.co/) 152 | 153 | ##Are they for me? Should I learn them. 154 | 155 | Ultimately before jumping into one of these you need to have a firm understanding of JS. Like jQuery these are not new languages, they are just libraries built to provide us with certain functionality. 156 | 157 | [HERE](https://www.youtube.com/watch?v=kwSVWlzEefE) is a short video by Alex McPerson that might help you understand the evolution of the type of JS you are writing. Starting from simple little programs, to plugins, to using objects to organize your code and then getting into some sort of MVC framework like Backbone, Angular or Ember. I highly recommend checking out the video. 158 | 159 | 160 | ####Resources 161 | Backbone: 162 | [Backbone site](http://backbonejs.org/) 163 | 164 | Ember: 165 | [Ember getting started](http://emberjs.com/guides/) 166 | 167 | AngularJS: 168 | [Shaping up with AngularJS](https://www.codeschool.com/courses/shaping-up-with-angular-js) 169 | 170 | 171 | ##Node 172 | ![Node JS Logo](imgs/nodejs.png) 173 | 174 | Node is a JavaScript "runtime", or platform for writing server side JavaScript applications. It has become extremely popular as of late. Companies like Walmart have large portions of there systems running on Node servers. When working with Node it is common to work with MongoDB and a Node framework called Express. When you use this with AngularJS this is called the MEAN stack. 175 | 176 | ###npm 177 | ![NPM logo](imgs/npm-logo.png) 178 | 179 | Lets discuss npm for a moment. NPM stands for nifty procrastination machine, or normal people, mmm! or nobody publish monsters. Or what people often refer to it as Node Package Manger. 180 | 181 | NPM lets us download little packages, like gulp, that we can use inside of our node applications or tools. 182 | 183 | ##Node Applications 184 | What is an node application? Why use node? Well one good reason is that you know JavaScript already. Also it is pretty light weight and easy to install. If we take a look at the MEAN stack in terms of an application. We would use MongoDB as our database. Express, a Node framework, as our server application framework. Angular as our client side framework and Node as our server. 185 | 186 | ##MongoDB 187 | ![MongoDB Logo](imgs/mongodb.png) 188 | 189 | [MongoDB](https://www.mongodb.org/) is a database system commonly refereed to as a NoSql database. The way you store data in it is with documents, these documents look exactly like JSON. 190 | ``` 191 | { 192 | "_id" : ObjectId("555811826c10cdc54e4a61f7"), 193 | "first" : "Ryan", 194 | "last" : "Christiani", 195 | "email" : "ryan@ryanchristiani.com", 196 | "phone" : 9053307201, 197 | "address" : "303-291 Avenue Rd", 198 | "city" : "Toronto", 199 | "country" : "Canada", 200 | "postal" : "M4V 2G9", 201 | "plan" : "nightaccess", 202 | "stripeToken" : "", 203 | "status" : "Active", 204 | "notes" : null, 205 | "isActive" : true, 206 | "customerToken" : "*****", 207 | "subToken" : "****", 208 | "payments" : [ ], 209 | "__v" : 0 210 | } 211 | ``` 212 | ##Express 213 | ![Express JS](imgs/express.png) 214 | 215 | [Express](http://expressjs.com/) is a application framework for creating Node application. Express lets us create routes based on URL's. 216 | 217 | An example of this might look like this: 218 | 219 | ``` 220 | app.get('/api/alumnis/:id', alumni.getAlumniById); 221 | ``` 222 | 223 | In this example using Express and the `.get()` method we well our application to listen for a get request like `http://localhost/api/alumnis/555811826c10cdc54e4a61f7` and this will retrieve from our database and alumni matching that that ID. Note that the method `alumni.getAlumniById` is something that you have to set up. 224 | 225 | ``` 226 | alumni.getAlumniById = function(req,res) { 227 | var id = req.params.id; 228 | models.alumni.find({_id: id}, function(err,doc) { 229 | if(err) { 230 | res.send(err); 231 | } 232 | else { 233 | res.send({alumni: doc}); 234 | } 235 | }); 236 | } 237 | ``` 238 | 239 | ##Angular 240 | ![Angular](imgs/AngularJS-large.png) 241 | 242 | We already discussed Angular in depth above, but to re-iterate. Angular is an MV* javascript framework that allows us to organize our applications more and more. There are certain conventions that are set in place to make this organization easy. It also takes care of a lot of things for us, so we can concentrate on writing our application. 243 | 244 | 245 | ###Resources 246 | For learning Node, Express and MongoDB. 247 | 248 | MongoDB - [MongoDB University](https://university.mongodb.com) 249 | All Three - [Scotch.io](https://scotch.io/) 250 | Node - [Nodeschool](http://nodeschool.io/) 251 | 252 | Build something! Take a simple idea and build something with it. 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | -------------------------------------------------------------------------------- /imgs/AngularJS-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rchristiani/js-frameworks/1997c9e51aae37ac0b4b7f3b0dfce318b9785005/imgs/AngularJS-large.png -------------------------------------------------------------------------------- /imgs/backbone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rchristiani/js-frameworks/1997c9e51aae37ac0b4b7f3b0dfce318b9785005/imgs/backbone.png -------------------------------------------------------------------------------- /imgs/ember.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rchristiani/js-frameworks/1997c9e51aae37ac0b4b7f3b0dfce318b9785005/imgs/ember.png -------------------------------------------------------------------------------- /imgs/express.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rchristiani/js-frameworks/1997c9e51aae37ac0b4b7f3b0dfce318b9785005/imgs/express.png -------------------------------------------------------------------------------- /imgs/mongodb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rchristiani/js-frameworks/1997c9e51aae37ac0b4b7f3b0dfce318b9785005/imgs/mongodb.png -------------------------------------------------------------------------------- /imgs/mvcbase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rchristiani/js-frameworks/1997c9e51aae37ac0b4b7f3b0dfce318b9785005/imgs/mvcbase.png -------------------------------------------------------------------------------- /imgs/nodejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rchristiani/js-frameworks/1997c9e51aae37ac0b4b7f3b0dfce318b9785005/imgs/nodejs.png -------------------------------------------------------------------------------- /imgs/npm-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rchristiani/js-frameworks/1997c9e51aae37ac0b4b7f3b0dfce318b9785005/imgs/npm-logo.png -------------------------------------------------------------------------------- /imgs/npm-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | npm-logo 4 | Created with Sketch (http://www.bohemiancoding.com/sketch) 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /imgs/react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rchristiani/js-frameworks/1997c9e51aae37ac0b4b7f3b0dfce318b9785005/imgs/react.png -------------------------------------------------------------------------------- /react/README.md: -------------------------------------------------------------------------------- 1 | #Getting started 2 | 3 | You will need the `react-tools` to get started playing with this little app. 4 | 5 | To install run `npm install -g react-tools` this will add the `jsx` cli tool. 6 | 7 | To run this run `jsx -x jsx -w . .` this will look for anything `.jsx` and convert it to .js. It will also watch your files in the current directory and place them in the current directory. -------------------------------------------------------------------------------- /react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React Example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /react/script.js: -------------------------------------------------------------------------------- 1 | //TODO: Finish react version 2 | 3 | var apiKey = 'pUaGTYo5'; 4 | var apiUrl = 'https://www.rijksmuseum.nl/api/en/collection/?key=' + apiKey; 5 | //Get info 6 | var getArtData = function(query) { 7 | var query = query === undefined ? '' : query; 8 | return $.get(apiUrl + '&q=' + query,{ 9 | dataType: 'jsonp' 10 | }); 11 | }; 12 | 13 | var Gallery = React.createClass({displayName: "Gallery", 14 | getInitialState: function() { 15 | return { 16 | items: [] 17 | } 18 | }, 19 | 20 | componentDidMount: function() { 21 | getArtData().then(function(res) { 22 | var art = res.artObjects; 23 | this.createItems(art); 24 | }.bind(this), function(err) { 25 | console.log(err); 26 | }); 27 | }, 28 | 29 | componentWillReceiveProps: function(obj) { 30 | this.createItems(obj.data); 31 | }, 32 | 33 | createItems: function(art) { 34 | this.state.items = []; 35 | art.map(function(el, i) { 36 | if(el.webImage !== null) { 37 | this.state.items.push( 38 | React.createElement("div", {className: "art-item", key: el.id}, 39 | React.createElement("img", {src: el.webImage.url}), 40 | React.createElement("p", null, el.longTitle) 41 | ) 42 | ); 43 | } 44 | if(i === (art.length - 1)) { 45 | this.setState({ 46 | items : this.state.items 47 | }) 48 | } 49 | }.bind(this)); 50 | }, 51 | 52 | render: function() { 53 | return ( 54 | React.createElement("div", null, this.state.items) 55 | ); 56 | } 57 | }); 58 | 59 | var HeaderComponent = React.createClass({displayName: "HeaderComponent", 60 | getInitialState: function() { 61 | return { 62 | searchValue: '' 63 | } 64 | }, 65 | searchArt: function(e) { 66 | e.preventDefault(); 67 | getArtData(this.state.searchValue).then(function(res) { 68 | this.props.onSearch(res.artObjects); 69 | this.setState({ 70 | searchValue: '' 71 | }); 72 | }.bind(this)); 73 | }, 74 | onChange: function(e) { 75 | this.setState({ 76 | searchValue: e.target.value 77 | }); 78 | }, 79 | render: function() { 80 | return ( 81 | React.createElement("header", {className: "main-header"}, 82 | React.createElement("h1", null, "Rijks Museum"), 83 | React.createElement("div", {className: "search-box"}, 84 | React.createElement("form", {onSubmit: this.searchArt}, 85 | React.createElement("input", {type: "text", value: this.state.searchValue, onChange: this.onChange}), 86 | React.createElement("input", {type: "submit", value: "search"}) 87 | ) 88 | ) 89 | ) 90 | ); 91 | } 92 | }); 93 | 94 | var App = React.createClass({displayName: "App", 95 | getInitialState: function() { 96 | return { 97 | data: [] 98 | }; 99 | }, 100 | searchHandler: function(data) { 101 | this.setState({ 102 | data: data 103 | }); 104 | }, 105 | render: function() { 106 | 107 | return ( 108 | React.createElement("main", {className: "main-section"}, 109 | React.createElement(HeaderComponent, {onSearch: this.searchHandler}), 110 | React.createElement("section", {className: "art-work"}, 111 | React.createElement(Gallery, {data: this.state.data}) 112 | ) 113 | ) 114 | ); 115 | } 116 | }); 117 | //Render data 118 | React.render(React.createElement(App, null), document.getElementsByTagName('body')[0]); 119 | -------------------------------------------------------------------------------- /react/script.jsx: -------------------------------------------------------------------------------- 1 | //TODO: Finish react version 2 | 3 | var apiKey = 'pUaGTYo5'; 4 | var apiUrl = 'https://www.rijksmuseum.nl/api/en/collection/?key=' + apiKey; 5 | //Get info 6 | var getArtData = function(query) { 7 | var query = query === undefined ? '' : query; 8 | return $.get(apiUrl + '&q=' + query,{ 9 | dataType: 'jsonp' 10 | }); 11 | }; 12 | 13 | var Gallery = React.createClass({ 14 | getInitialState: function() { 15 | return { 16 | items: [] 17 | } 18 | }, 19 | 20 | componentDidMount: function() { 21 | getArtData().then((res) => { 22 | var art = res.artObjects; 23 | this.createItems(art); 24 | }, function(err) { 25 | console.log(err); 26 | }); 27 | }, 28 | 29 | componentWillReceiveProps: function(obj) { 30 | this.createItems(obj.data); 31 | }, 32 | 33 | createItems: function(art) { 34 | this.state.items = []; 35 | art.map((el, i) => { 36 | if(el.webImage !== null) { 37 | this.state.items.push( 38 |
39 | 40 |

{el.longTitle}

41 |
42 | ); 43 | } 44 | if(i === (art.length - 1)) { 45 | this.setState({ 46 | items : this.state.items 47 | }) 48 | } 49 | }); 50 | }, 51 | 52 | render: function() { 53 | return ( 54 |
{this.state.items}
55 | ); 56 | } 57 | }); 58 | 59 | var HeaderComponent = React.createClass({ 60 | getInitialState: function() { 61 | return { 62 | searchValue: '' 63 | } 64 | }, 65 | searchArt: function(e) { 66 | e.preventDefault(); 67 | getArtData(this.state.searchValue).then(res => { 68 | this.props.onSearch(res.artObjects); 69 | this.setState({ 70 | searchValue: '' 71 | }); 72 | }); 73 | }, 74 | onChange: function(e) { 75 | this.setState({ 76 | searchValue: e.target.value 77 | }); 78 | }, 79 | render: function() { 80 | return ( 81 |
82 |

Rijks Museum

83 |
84 |
85 | 86 | 87 |
88 |
89 |
90 | ); 91 | } 92 | }); 93 | 94 | var App = React.createClass({ 95 | getInitialState: function() { 96 | return { 97 | data: [] 98 | }; 99 | }, 100 | searchHandler: function(data) { 101 | this.setState({ 102 | data: data 103 | }); 104 | }, 105 | render: function() { 106 | 107 | return ( 108 |
109 | 110 |
111 | 112 |
113 |
114 | ); 115 | } 116 | }); 117 | //Render data 118 | React.render(, document.getElementsByTagName('body')[0]); 119 | --------------------------------------------------------------------------------