User Tools

Site Tools


android-labs-s16:mean_stack

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
android-labs-s16:mean_stack [2016/03/04 12:22]
mbarboi
android-labs-s16:mean_stack [2016/03/04 13:47] (current)
mbarboi
Line 1: Line 1:
 +You will nead to install mongodb and nodejs for this tutorial. Check out the instructions [[http://​learn.mean.io/​ | here]]. Do this first-- the components can take some time to download and install.
 +
 +You will need to use your favorite text editor for this tutorial. If you don't have a favorite text editor, you should find one! [[https://​www.sublimetext.com/​ | Sublime Text]] is a popular choice. ​
 +
 ====== The MEAN Stack tutorial ====== ====== The MEAN Stack tutorial ======
 Through this tutorial we wanted to touch base on how full stack development looks like. Essentially,​ we will be creating a MEAN stack application,​ which is nothing but a combination of MongoDB, Express, Angular and Node.js. So, let's get on it! Through this tutorial we wanted to touch base on how full stack development looks like. Essentially,​ we will be creating a MEAN stack application,​ which is nothing but a combination of MongoDB, Express, Angular and Node.js. So, let's get on it!
Line 49: Line 53:
   -**cd**- Change directory to another relative to the current directory ​   -**cd**- Change directory to another relative to the current directory ​
   -**ls**- list files in this directory. On windows this command is **dir**   -**ls**- list files in this directory. On windows this command is **dir**
 +  -**mkdir**- make a directory relative to the current directory
  
 +Commands you run in a terminal run until they'​re finished. Depending on the command they may finish immediately,​ run for a while, or run forever. To kill a command, press **ctrl + c** on Linux and Windows, **cmd + c** on OSX. 
  
 ===== Configuration & Setup ===== ===== Configuration & Setup =====
  
-You will nead to install mongodb and nodejs for this tutorial. Check out the instructions [[http://​learn.mean.io/​ | here]]. +Once you'​ve ​finished installing node and mongo as directed in the first sectinoopen a CLI session and make sure node is installed
- +
-Once you'​ve ​followed ​the instructions listed there, make sure node is installed+
  
 <​code>​ <​code>​
Line 64: Line 68:
 If you dont see a version number listed, node is not correctly installed. ​ If you dont see a version number listed, node is not correctly installed. ​
  
-Install necessary packages via NPM (Node Package Manager). NPM is automatically installed with Node.+Install necessary packages via NPM (Node Package Manager). NPM is automatically installed with Node and allows you to install packages from the internet to your computer
  
   * Bower – A manager for client-side packages   * Bower – A manager for client-side packages
Line 77: Line 81:
 Let’s create the app template and initialize a Git repository. Let’s create the app template and initialize a Git repository.
 <​code>​ <​code>​
-mkdir mean-tutorial && cd $_+mkdir mean-tutorial && cd mean-tutorial
 yo angular-fullstack mean-tutorial yo angular-fullstack mean-tutorial
 </​code>​ </​code>​
  
-You will be presented with several options. ​ +You will be presented with several options. ​The yeoman generator allows you to customize your newly scaffolded application by swapping out functionality or technology. ​Hit enter for every question, selecting the defaults.
- +
-Hit enter for every question, selecting the defaults, except the following:  +
-<​code>​ +
-Include bootstrap: Y +
-Include UI Bootstrap: Y +
-Scaffold out authentication boilerplate:​ Y +
-</​code>​ +
- +
-After creating your files, it should automatically run an npm install for youIf this fails (which seems to happen pretty often), remove your node_modules folder and install again by running the following (Note: make sure you are in the correct directory for your project!) +
- +
-ONLY RUN THE FOLLOWING 3 COMMANDS IF YOUR INSTALL FAILED! +
- +
-<​code>​ +
-rm -rf node_modules +
-npm install --dev +
-</​code>​ +
- +
-We also need to install our bower dependencies with +
- +
-<​code>​ +
-bower install +
-</​code>​+
  
-Now we are ready to run our app!+After creating your files, it should automatically ​run an npm install for you. This will take some time. Once the terminal output stops, Yeoman is done working. The result is a working MEAN stack server and frontend. Lets run it!
  
-Running Our App+==== Running Our App ====
  
 To run our app, we will need to start processes for our database as well as our server To run our app, we will need to start processes for our database as well as our server
Line 119: Line 101:
  
 <​code>​ <​code>​
-grunt serve+$ grunt  
 +grunt serve
 </​code>​ </​code>​
  
 If this fails, mongo is most likely not correctly installed. If this fails, mongo is most likely not correctly installed.
 +
 <​code>​ <​code>​
 Go to localhost:​9000 Go to localhost:​9000
-Go to localhost:900/api/things+Go to localhost:9000/api/things
 </​code>​ </​code>​
  
-You should now be able to navigate to http://​localhost:​3000/ in your browser and you should see this page +You should now be able to navigate to http://​localhost:​9000/ in your browser and you should see this page.
-{{:​android-labs-s16:​mean-tutorial-login-page.png}}+
  
-Your MEAN stack app is up and running! Yay! Since you are not logged in, you have been redirected to /login. Click “home” on the navbar to see the homepage 
 {{:​android-labs-s16:​mean-tutorial-home-page.png}} {{:​android-labs-s16:​mean-tutorial-home-page.png}}
  
-==== Push to Github ==== +Check out the login pageeither by clicking the link on the landing pageor going to http://​localhost:​9000/​login
-Let’s push our code up to GitHub… You want the world to see your awesome appsright? Create a new repo on GitHub (if you don’t know how to do this, don’t worry. It’s super easy, just go to GitHub, create and account, and follow ​the directions). Once that’s readylet’s initialize a Git repo on our local machine, commit our code, add our remote (which is our GitHub repo), and push our code to GitHub! (NoteIf you server is still running, use CTRL-C to stop it).+
  
-When you add your remote, be sure to use the URL for your own GitHub repository!+{{:​android-labs-s16:​mean-tutorial-login-page.png}}
  
-<​code>​ +Your MEAN stack app is up and running! Yay! Yeoman includes a default API under http://localhost:​9000/api/things.  
-git init + 
-git add -A +Running **grunt serve** will run a server locally on your computer forever. Grunt is pretty smart and will update the live site at localhost:​9000 when you update ​code. You should leave the terminal session that you ran **grunt serve** in open for the rest of this tutorial and execute further commands in another terminal window. Make sure you **cd** in the same directory you were working in before!
-git commit -m "​initial scaffolding"​ +
-git remote add origin https://github.com/AJFunk/mean-tutorial.git +
-git push -u origin master +
-</code>+
  
 === Server logic === === Server logic ===
 +
 CRUD(create,​ read, update, delete) video games CRUD(create,​ read, update, delete) video games
   * Each game will have a platform and genre   * Each game will have a platform and genre
Line 156: Line 134:
 ===== Adding CRUD Endpoint ===== ===== Adding CRUD Endpoint =====
  
- +The first thing we need to do is create our endpoints on our server. Again, yeoman will ask you for defaults. Hit enter to select the defaults
-The first thing we need to do is create our endpoints on our server.+
  
 <​code>​ <​code>​
 yo angular-fullstack:​endpoint game yo angular-fullstack:​endpoint game
-? What will the url of your endpoint to be? /api/games 
-</​code>​ 
- 
-While we’re at it, let’s remove the default “thing” endpoint that was created with our scaffolding 
-The first thing we need to do is create our endpoints on our server 
- 
-<​code>​ 
-rm -rf server/​api/​thing 
 </​code>​ </​code>​
  
Line 175: Line 144:
 An endpoint is like a special slot that is designed to do 3 things: First, it receives your data. If it likes the data you sent it (meaning your data is properly formed), it does something with your data (creates a new game in this instance). Lastly, it responds back to you – either “Success, your game was created!” or “Failure, something went wrong!”. Of course, these response messages are more verbose than this, but that is the general idea. An endpoint is like a special slot that is designed to do 3 things: First, it receives your data. If it likes the data you sent it (meaning your data is properly formed), it does something with your data (creates a new game in this instance). Lastly, it responds back to you – either “Success, your game was created!” or “Failure, something went wrong!”. Of course, these response messages are more verbose than this, but that is the general idea.
  
-If you try to run the app, it will crash with this error: +In server/​config/​seed.js,​ we are populating our database whenever the server is started. ​Let’s make it automatically seed the database with a some games (as well as keeping the default users and things).
-<​code>​ +
-module.js:​340 +
-    throw err; +
-          ^ +
-Error: Cannot find module '​../​api/​thing/​thing.model'​ +
-    at Function.Module._resolveFilename (module.js:​338:​15) +
-    at Function.Module._load (module.js:​280:​25) +
-    at Module.require (module.js:​364:​17) +
-    at require (module.js:​380:​17) +
-    at Object.<​anonymous>​ (/​Users/​ajfunk/​mean-tutorial/​server/​config/​seed.js:​8:​13) +
-    at Module._compile (module.js:​456:​26) +
-    at Object.Module._extensions..js (module.js:​474:​10) +
-    at Module.load (module.js:​356:​32) +
-    at Function.Module._load (module.js:​312:​12) +
-    at Module.require (module.js:​364:​17) +
-    at require (module.js:​380:​17) +
-    at Object.<​anonymous>​ (/​Users/​ajfunk/​mean-tutorial/​server/​app.js:​18:​21) +
-    at Module._compile (module.js:​456:​26) +
-    at Object.Module._extensions..js (module.js:​474:​10) +
-    at Module.load (module.js:​356:​32) +
-    at Function.Module._load (module.js:​312:​12) +
-</​code>​ +
- +
-Uh oh, we broke it! Time to debug. +
- +
-The problem seems to be that the module for thing.model cannot be found. Since that is a file we just deleted, that makes sense! Let’s fix this. In server/​config/​seed.js,​ we are populating our database whenever the server is started. ​We’ve remove “things”,​ but let’s make it automatically seed the database with a some games (as well as keeping the default users).+
  
 First, we need to create a Game schema. In server/​api/​game/​game.model.js we need to define what the data for a Game should look like First, we need to create a Game schema. In server/​api/​game/​game.model.js we need to define what the data for a Game should look like
Line 219: Line 162:
 module.exports = mongoose.model('​Game',​ GameSchema);​ module.exports = mongoose.model('​Game',​ GameSchema);​
 </​code>​ </​code>​
-Now we will update seed.js to populate our Mongo database+ 
 +Now we will update seed.js to populate our Mongo database. In server/​config/​seeds.js:​ 
 <​code>​ <​code>​
 +'use strict';​
 +import User from '​../​api/​user/​user.model';​
 +import Game from '​../​api/​game/​game.model';​
 +
 +User.find({}).removeAsync()
 +  .then(() => {
 +    User.createAsync({
 +      provider: '​local',​
 +      name: 'Test User',
 +      email: '​test@example.com',​
 +      password: '​test'​
 +    }, {
 +      provider: '​local',​
 +      role: '​admin',​
 +      name: '​Admin',​
 +      email: '​admin@example.com',​
 +      password: '​admin'​
 +    })
 +    .then(() => {
 +      console.log('​finished populating users'​);​
 +    });
 +  });
 +
 'use strict';​ 'use strict';​
  
-var Game = require('​../​api/​game/​game.model'​);​ 
-var User = require('​../​api/​user/​user.model'​);​ 
  
 Game.find({}).remove(function() { Game.find({}).remove(function() {
   Game.create({   Game.create({
-    name : 'Halo 5', +    name: 'Halo 5', 
-    platform : 'Xbox One',+    platform: 'Xbox One',
     genre: '​Shooter'​     genre: '​Shooter'​
-  },{ +  }, { 
-    name : '​Fallout 4', +    name: '​Fallout 4', 
-    platform : '​PlayStation 4',+    platform: '​PlayStation 4',
     genre: '​Role-Playing'​     genre: '​Role-Playing'​
-  },{ +  }, { 
-    name : 'Super Smash Bros.',​ +    name: 'Super Smash Bros.',​ 
-    platform : 'Wii U',+    platform: 'Wii U',
     genre: '​Fighting'​     genre: '​Fighting'​
-  },{ +  }, { 
-    name : '​Pokemon X', +    name: '​Pokemon X', 
-    platform : '​3DS',​+    platform: '​3DS',​
     genre: '​Role-Playing'​     genre: '​Role-Playing'​
-  },{ +  }, { 
-    name : 'Halo 4', +    name: 'Halo 4', 
-    platform : 'Xbox 360',+    platform: 'Xbox 360',
     genre: '​Shooter'​     genre: '​Shooter'​
   });   });
 }); });
  
-User.find({}).remove(function() { 
-  User.create({ 
-    provider: '​local',​ 
-    name: 'Test User', 
-    email: '​test@test.com',​ 
-    password: '​test'​ 
-  }, { 
-    provider: '​local',​ 
-    role: '​admin',​ 
-    name: '​Admin',​ 
-    email: '​admin@admin.com',​ 
-    password: '​admin'​ 
-  }, function() { 
-      console.log('​finished populating users'​);​ 
-    } 
-  ); 
-}); 
 </​code>​ </​code>​
  
-We also need to remove our Things routes in server/​routes.js.+ 
 +Check out server/​routes.js. ​This files defines how express, our web server, responds to different URLs. Notice that our routes for Games are already included? Grunt is pretty great. If your “grunt serve” is still running, it will detect the change when you save the file and restart the server. You should be able to run the app again with 
  
 <​code>​ <​code>​
-'use strict';​ +grunt build 
- +grunt serve
-var errors = require('​./​components/​errors'​);​ +
- +
-module.exports = function(app) { +
- +
-  // Insert routes below +
-  app.use('/​api/​games',​ require('​./​api/​game'​));​ +
-  app.use('/​api/​users',​ require('​./​api/​user'​));​ +
- +
-  app.use('/​auth',​ require('​./​auth'​));​ +
-   +
-  // All undefined asset or api routes should return a 404 +
-  app.route('/:​url(api|auth|components|app|bower_components|assets)/​*'​) +
-   ​.get(errors[404]);​ +
- +
-  // All other routes should redirect to the index.html +
-  app.route('/​*'​) +
-    .get(function(req,​ res) { +
-      res.sendfile(app.get('​appPath'​) + '/​index.html'​);​ +
-    }); +
-};+
 </​code>​ </​code>​
-Notice that our routes for Games are already included? Grunt is pretty great. If your “grunt serve” is still running, it will detect the change when you save the file and restart the server. You should be able to run the app again. 
  
 === Client - Angular JS === === Client - Angular JS ===
- Now, it’s time to hook these up to our client-side using my personal favorite frontend framework, Angular 
  
-Let’s begin by letting Yeoman do some more work for us! We will begin with the “R” in CRUD, which stands for “Read”. Generate our route for viewing games with+Now, it’s time to hook these up to our client-side using Angular. 
 + 
 +Let’s begin by letting Yeoman do some more work for us! We will begin with the “R” in CRUD, which stands for “Read”. Generate our frontend ​route for viewing games with the following. As before, hit enter to accept defaults. ​
  
 <​code>​ <​code>​
-? Where would you like to create this route? (client/​app/​) client/​app +yo angular-fullstack:​route games
-? What will the url of your route be? (/games) /games+
 </​code>​ </​code>​
  
 This generates everything we need for a new view in our app, which can be seen by going to http://​localhost:​9000/​games This generates everything we need for a new view in our app, which can be seen by going to http://​localhost:​9000/​games
  
-This should display text that says “This is the games view.”+This should display text that says “This is the games view.” ​Kinda boring, no?
  
 We need a way to navigate to this page without manually typing in the URL.  We need a way to navigate to this page without manually typing in the URL. 
-Change our games.html to:+Open client/​components/​navbar/​navbar.html and replace the contents with the following code. Note we're only adding one line, a nav link that shows games. 
 <​code>​ <​code>​
 <div class="​navbar navbar-default navbar-static-top"​ ng-controller="​NavbarController">​ <div class="​navbar navbar-default navbar-static-top"​ ng-controller="​NavbarController">​
   <div class="​container">​   <div class="​container">​
     <div class="​navbar-header">​     <div class="​navbar-header">​
-      <button class="​navbar-toggle"​ type="​button"​ ng-click="​isCollapsed = !isCollapsed">​+      <button class="​navbar-toggle"​ type="​button"​ ng-click="​nav.isCollapsed = !nav.isCollapsed">​
         <span class="​sr-only">​Toggle navigation</​span>​         <span class="​sr-only">​Toggle navigation</​span>​
         <span class="​icon-bar"></​span>​         <span class="​icon-bar"></​span>​
Line 325: Line 255:
       <a href="/"​ class="​navbar-brand">​mean-tutorial</​a>​       <a href="/"​ class="​navbar-brand">​mean-tutorial</​a>​
     </​div>​     </​div>​
-    <div collapse="​isCollapsed"​ class="​navbar-collapse collapse"​ id="​navbar-main">​+    <div collapse="​nav.isCollapsed"​ class="​navbar-collapse collapse"​ id="​navbar-main">​
       <ul class="​nav navbar-nav">​       <ul class="​nav navbar-nav">​
-        <li ng-repeat="​item in menu" ​ng-class="{active: isActive(item.link)}">​ +        <li ng-repeat="​item in nav.menu" ​ui-sref-active="​active">​ 
-            <a ng-href="​{{item.link}}">​{{item.title}}</​a>​+          <a ui-sref="​{{item.state}}">​{{item.title}}</​a>​
         </li>         </li>
-        <li ng-show="​isAdmin()" ​ng-class="{active: isActive('/​admin'​)}"><​a ​href="/admin">​Admin</​a></​li>​+        <li ng-show="​nav.isAdmin()" ​ui-sref-active="​active"><​a ​ui-sref="​admin">​Admin</​a></​li>​
         <li ng-class="​{active:​ isActive('/​games'​)}"><​a href="/​games">​Games</​a></​li>​         <li ng-class="​{active:​ isActive('/​games'​)}"><​a href="/​games">​Games</​a></​li>​
       </ul>       </ul>
  
       <ul class="​nav navbar-nav navbar-right">​       <ul class="​nav navbar-nav navbar-right">​
-        <li ng-hide="​isLoggedIn()" ​ng-class="{active: isActive('/​signup'​)}"><​a ​href="/signup">​Sign up</​a></​li>​ +        <li ng-hide="​nav.isLoggedIn()" ​ui-sref-active="​active"><​a ​ui-sref="​signup">​Sign up</​a></​li>​ 
-        <li ng-hide="​isLoggedIn()" ​ng-class="{active: isActive('/​login'​)}"><​a ​href="/login">​Login</​a></​li>​ +        <li ng-hide="​nav.isLoggedIn()" ​ui-sref-active="​active"><​a ​ui-sref="​login">​Login</​a></​li>​ 
-        <li ng-show="​isLoggedIn()"><​p class="​navbar-text">​Hello {{ getCurrentUser().name }}</​p>​ </​li>​ +        <li ng-show="​nav.isLoggedIn()">​ 
-        <li ng-show="​isLoggedIn()" ​ng-class="{active: isActive('/​settings'​)}"><​a ​href="/settings"><​span class="​glyphicon glyphicon-cog"></​span></​a></​li>​ +          ​<p class="​navbar-text">​Hello {{ nav.getCurrentUser().name }}</​p>​ 
-        <li ng-show="​isLoggedIn()" ng-class="​{active:​ isActive('/​logout'​)}"><​a ​href=""​ ng-click="​logout()">​Logout</​a></​li>​+        ​</​li>​ 
 +        <li ng-show="​nav.isLoggedIn()" ​ui-sref-active="​active"><​a ​ui-sref="​settings"><​span class="​glyphicon glyphicon-cog"></​span></​a></​li>​ 
 +        <li ng-show="​nav.isLoggedIn()"><​a ​ui-sref="​logout">​Logout</​a></​li>​
       </ul>       </ul>
     </​div>​     </​div>​
Line 383: Line 315:
 If we take a look at the data, we received an array with 5 objects. These objects each represent a game in our database. Now we can display them on the screen If we take a look at the data, we received an array with 5 objects. These objects each represent a game in our database. Now we can display them on the screen
  
-In games.html, we will be using ng-repeat (an Angular directive) to display all of our data+In client/​app/​games/​games.html, we will be using ng-repeat (an Angular directive) to display all of our data. Head back to the /games page when you're done.  
 <​code>​ <​code>​
 <div class="​col-md-12">​ <div class="​col-md-12">​
Line 404: Line 337:
 </​code>​ </​code>​
  
-If you are new to Angular, the ng-repeat at bracket notation may look strange to you. ng-repeat is used to repeat an element over and over based off of the data you provide it. In this case, we use our games array which has 5 elements, each representing a game. It loops through all 5 of these, and create a new +If you are new to Angular, the ng-repeat at bracket notation may look strange to you. ng-repeat is used to repeat an element over and over based off of the data you provide it. In this case, we use our games array which has 5 elements, each representing a game. It loops through all 5 of these, and create a new HTML element ​with its children.
-with its children.+
  
 The brackets are template notation for binding our HTML to the data in our controller. {{game.name}} means “display the value for game.name here.” If we change the value of that variable in our controller, it will immediately change in our HTML. This is called data binding, and it is a very powerful feature in Angular. The brackets are template notation for binding our HTML to the data in our controller. {{game.name}} means “display the value for game.name here.” If we change the value of that variable in our controller, it will immediately change in our HTML. This is called data binding, and it is a very powerful feature in Angular.
Line 442: Line 374:
 </​div>​ </​div>​
 </​code>​ </​code>​
 +
 We have added a new table row that now has 3 input boxes which each represent a property of a game and a button to save it. Notice how each input box is bound to a variable with ng-model. We have also added placeholders in our ng-repeat that will be used to delete them later on. We have added a new table row that now has 3 input boxes which each represent a property of a game and a button to save it. Notice how each input box is bound to a variable with ng-model. We have also added placeholders in our ng-repeat that will be used to delete them later on.
  
-Notice that on our button, we have defined ng-click=’addNewGame()’. This is an Angular directive to handle click events. This says “if this button is clicked, execute the addNewGame function”. ​This function is located in our controller+Notice that on our button, we have defined ng-click=’addNewGame()’. This is an Angular directive to handle click events. This says “if this button is clicked, execute the addNewGame function”. ​Edit client/​app/​games/​games.controller.js 
 <​code>​ <​code>​
 'use strict';​ 'use strict';​
Line 472: Line 406:
  
   });   });
- </​code>​+</​code>​ 
 When our button is clicked, we will send a POST request to our server, along with our newGame object. On success (which means it was successfully saved), we will add our new game to our games array (Angular will automatically recognize this and add another row via ng-repeat), and then set the newGame object to be empty so we can empty out our input boxes. Again, if there is an error, alert the user. When our button is clicked, we will send a POST request to our server, along with our newGame object. On success (which means it was successfully saved), we will add our new game to our games array (Angular will automatically recognize this and add another row via ng-repeat), and then set the newGame object to be empty so we can empty out our input boxes. Again, if there is an error, alert the user.
  
 Enter some new information in the input boxes and hit “Add New”. Voila! A new game has been added to the database! Enter some new information in the input boxes and hit “Add New”. Voila! A new game has been added to the database!
 +
 {{:​android-labs-s16:​mean-tutorial-screenshot-5.png}} {{:​android-labs-s16:​mean-tutorial-screenshot-5.png}}
-Note that whenever your server is restarted, it will be reseeded with the data from seed.js+ 
 +Note that whenever your server is restarted, it will be reseeded with the data from seed.js ​and wipe any changes to models you've made.  
 We can create and read data, but I don’t want Assassin’s Creed in my database anymore. Let’s delete it We can create and read data, but I don’t want Assassin’s Creed in my database anymore. Let’s delete it
  
 First, we will replace our placeholders with some nice delete buttons. First, we will replace our placeholders with some nice delete buttons.
 <​code>​ <​code>​
- 
 <div class="​col-md-12">​ <div class="​col-md-12">​
   <​h1>​Games</​h1>​   <​h1>​Games</​h1>​
Line 513: Line 450:
 </​div>​ </​div>​
 </​code>​ </​code>​
 +
 Notice that when we click the delete button, it is calling the function “deleteGame” but this time it is passing it the $index variable. $index is the index value of that specific ng-repeat that correlates to the index of the array it is looping through. In other words, when we click the delete button we are saying “remove the element at location $index from the $scope.games array. Notice that when we click the delete button, it is calling the function “deleteGame” but this time it is passing it the $index variable. $index is the index value of that specific ng-repeat that correlates to the index of the array it is looping through. In other words, when we click the delete button we are saying “remove the element at location $index from the $scope.games array.
  
 games.controller.js now includes our deleteGame function games.controller.js now includes our deleteGame function
 +
 <​code>​ <​code>​
 'use strict';​ 'use strict';​
Line 553: Line 492:
  
   });   });
-  ​</​code>​ +</​code>​ 
-  This time we use $http.delete,​ which sends a DELETE request to our server. On success (which means the game was successfully deleted), we can remove that game from our array of games.+ 
 +This time we use $http.delete,​ which sends a DELETE request to our server. On success (which means the game was successfully deleted), we can remove that game from our array of games.
  
 You may wondering why the URL is formatted differently than our POST request when we create a game. To answer this, let’s take a look at server/​api/​game/​index.js You may wondering why the URL is formatted differently than our POST request when we create a game. To answer this, let’s take a look at server/​api/​game/​index.js
 +
 <​code>​ <​code>​
 'use strict';​ 'use strict';​
Line 574: Line 515:
 module.exports = router; module.exports = router;
 </​code>​ </​code>​
 +
 In this file, we define our endpoints. Notice that the post endpoint does not have any parameters in the URL. This means we must pass our data inside the body of our request. However, the delete endpoint (along with a few others) have a :id parameter. This means that the required id must be passed inside the URL. If we wanted to change this, we could – but this is a discussion for a later tutorial. If this is a bit beyond your grasp right now, don’t worry! Continue on, and know that it will make sense later on down the road. In this file, we define our endpoints. Notice that the post endpoint does not have any parameters in the URL. This means we must pass our data inside the body of our request. However, the delete endpoint (along with a few others) have a :id parameter. This means that the required id must be passed inside the URL. If we wanted to change this, we could – but this is a discussion for a later tutorial. If this is a bit beyond your grasp right now, don’t worry! Continue on, and know that it will make sense later on down the road.
  
Line 625: Line 567:
 </​div>​ </​div>​
 </​code>​ </​code>​
 +
 Look carefully at what is happening here. Each has 2 versions: A normal text display, and an input field. The text version uses ng-hide, while the input uses ng-show, and both are bound the the same variable. ng-hide and ng-show are triggered based on the boolean value they are bound to. If the game.edit is true, we will hide the text and show the input. Look carefully at what is happening here. Each has 2 versions: A normal text display, and an input field. The text version uses ng-hide, while the input uses ng-show, and both are bound the the same variable. ng-hide and ng-show are triggered based on the boolean value they are bound to. If the game.edit is true, we will hide the text and show the input.
  
 The buttons also have this same behavior. Clicking the edit button will toggle game.edit, which will then hide the delete and edit buttons and show the save and cancel buttons. Let’s add the necessary JavaScript to our controller and try it out. The buttons also have this same behavior. Clicking the edit button will toggle game.edit, which will then hide the delete and edit buttons and show the save and cancel buttons. Let’s add the necessary JavaScript to our controller and try it out.
 +
 <​code>​ <​code>​
 'use strict';​ 'use strict';​
Line 669: Line 613:
   });   });
 </​code>​ </​code>​
 +
 {{:​android-labs-s16:​mean-tutorial-screenshot-7.png}} {{:​android-labs-s16:​mean-tutorial-screenshot-7.png}}
  
Line 676: Line 621:
  
 The last thing to do is to add the ability to save our changes. All we need to do is add a function to attach to the ng-click event for our save button. The last thing to do is to add the ability to save our changes. All we need to do is add a function to attach to the ng-click event for our save button.
 +
 <​code>​ <​code>​
 'use strict';​ 'use strict';​
Line 726: Line 672:
  
   });   });
- </​code>​ +</​code>​ 
-Now you can edit away! However, we should note some more flaws that still exist.+ 
 +Now you can edit and save! However, we should note some more flaws that still exist.
  
 What happens if I make a new game with empty input fields? What happens if I make a new game with empty input fields?
Line 733: Line 680:
 When I save a new game, why can’t I edit it without reloading? When I save a new game, why can’t I edit it without reloading?
 While these are all small issues, they are things that need to be addressed as they will result in poor user experience. Again, it is not within the scope of this tutorial to solve these problems for you – you will learn more by figuring them out on your own! If you are stuck and need help, please ask the TAs. While these are all small issues, they are things that need to be addressed as they will result in poor user experience. Again, it is not within the scope of this tutorial to solve these problems for you – you will learn more by figuring them out on your own! If you are stuck and need help, please ask the TAs.
 +
 +All of our CRUD functions are complete! In the next and final part of this tutorial, we will learn how to display games by a specific platform or genre, as well as deploy our app so the whole world can see it!
  
 ===== Deployment ===== ===== Deployment =====
Line 739: Line 688:
 We begin with our HTML. First, we change the data for genres and platforms to be tags with an empty href. This makes them “look clickable.” In other words, it adds default styling (blue text with an underline) and makes the cursor become a pointer on hover. We attach an ng-click handler to both of these, and pass in the appropriate values. We begin with our HTML. First, we change the data for genres and platforms to be tags with an empty href. This makes them “look clickable.” In other words, it adds default styling (blue text with an underline) and makes the cursor become a pointer on hover. We attach an ng-click handler to both of these, and pass in the appropriate values.
  
-On the top of the page, we add a button to reset the filter, and a line to display what the current filter is.+On the top of client/​app/​games/​games.html ​page, we add a button to reset the filter, and a line to display what the current filter is. 
 <​code>​ <​code>​
 <div class="​col-md-12">​ <div class="​col-md-12">​
Line 788: Line 738:
 </​div>​ </​div>​
 </​code>​ </​code>​
 +
 {{:​android-labs-s16:​mean-tutorial-screenshot-8.png}} {{:​android-labs-s16:​mean-tutorial-screenshot-8.png}}
 +
 Now we need to add functions to our controller to handle our ng-click events. We will be filtering our data by using Array.filter to keep the elements that we want, and remove the ones that don’t match our criteria. Now we need to add functions to our controller to handle our ng-click events. We will be filtering our data by using Array.filter to keep the elements that we want, and remove the ones that don’t match our criteria.
 <​code>​ <​code>​
Line 865: Line 817:
   });   });
 </​code>​ </​code>​
 +
 Take some time to review this code. Try to figure out what’s going on here before you continue. Take some time to review this code. Try to figure out what’s going on here before you continue.
  
Line 876: Line 829:
  
 Load up the page, and click on a platform or genre. Success! You can even reset the filter using the “Clear Filter” button.” Load up the page, and click on a platform or genre. Success! You can even reset the filter using the “Clear Filter” button.”
 +
 {{:​android-labs-s16:​mean-tutorial-screenshot-9.png}} {{:​android-labs-s16:​mean-tutorial-screenshot-9.png}}
-The world wants to see your app, so let’s deploy it to Heroku where it will be publicly accessible! If you haven’t already created an account Heroku, go do that. You will also need to setup Heroku Toolbelt+ 
 +The world wants to see your app, so let’s deploy it to Heroku where it will be publicly accessible! If you haven’t already created an account ​on Heroku, go do that. You will also need to [[https://​toolbelt.heroku.com/​ |setup Heroku Toolbelt]]. 
 + 
 +Once installed, log in to heroku. The first time heroku will take some time to install.  
 + 
 +<​code>​ 
 +$ heroku login 
 +Enter your Heroku credentials. 
 +Email: [YOUREMAIL] 
 +Password (typing will be hidden):  
 +Logged in as [YOUREMAIL] 
 +</​code>​ 
 + 
 +Yeoman knows how to set up our project for deployment to heroku very easily. Select the defaults when prompted. Leave the name blank to get a random name (Heroku has the best names!) 
 <​code>​ <​code>​
 yo angular-fullstack:​heroku yo angular-fullstack:​heroku
 </​code>​ </​code>​
  
-Leave the name blank to get random name (Heroku has the best names!) cd into the dist folder and open the app+We have site upLets open it.  
 <​code>​ <​code>​
 cd dist && heroku open cd dist && heroku open
 </​code>​ </​code>​
 +
 Your app URL should open in your browser. However, there seems to be an error Your app URL should open in your browser. However, there seems to be an error
 +
 {{:​android-labs-s16:​mean-tutorial-screenshot-10.png}} {{:​android-labs-s16:​mean-tutorial-screenshot-10.png}}
 +
 Let's check heroku logs Let's check heroku logs
 +
 <​code>​ <​code>​
-heroku logs --app <​put-app-name>​+cd dist 
 +heroku logs
 </​code>​ </​code>​
 +
 This displays our recent logs, and we quickly see the error message: This displays our recent logs, and we quickly see the error message:
 +
 <​code>​ <​code>​
 2015-11-19T20:​57:​05.686770+00:​00 app[web.1]: ​      throw er; // Unhandled '​error'​ event 2015-11-19T20:​57:​05.686770+00:​00 app[web.1]: ​      throw er; // Unhandled '​error'​ event
Line 913: Line 889:
 2015-11-19T20:​57:​06.448849+00:​00 heroku[web.1]:​ State changed from starting to crashed 2015-11-19T20:​57:​06.448849+00:​00 heroku[web.1]:​ State changed from starting to crashed
 </​code>​ </​code>​
 +
 It seems we can’t connect to our MongoDB, which makes sense! We never created one, nor did we tell our app which db to connect to. So let’s do that! It seems we can’t connect to our MongoDB, which makes sense! We never created one, nor did we tell our app which db to connect to. So let’s do that!
 +
 +You may run into an error here if you just made a new heroku account. Heroku does not allow unverified accounts to add services, and verification requires adding a credit card. Don't worry, heroku will never charge you for anything unless you specifically upgrade a running site. 
 +
 +If you don't want to enter info for heroku, you're all done. You won't be able to see the final site running, but be assured it would be running securely over the internet. ​
 +
 <​code>​ <​code>​
-heroku addons:​create mongolab ​--app limitless-wildwood-4228+heroku addons:​create mongolab
 </​code>​ </​code>​
 +
 Refresh the page, and it should be up and running! Congratulations,​ you’ve successfully created and deployed your first CRUD application using the MEAN stack! Refresh the page, and it should be up and running! Congratulations,​ you’ve successfully created and deployed your first CRUD application using the MEAN stack!
android-labs-s16/mean_stack.txt · Last modified: 2016/03/04 13:47 by mbarboi