MEAN




Download node and npm
Check javascript promises to avoid chrismas tree and cleanup structure of javascript callbacks


Express Server

$ mkdir mean-demo
$ cd mean-demo
$ vim package.json   # like a manifest file
{
"name": "mean-demo",
"version": "0.0.1"
}
$ vim myserver.js
console.log("Hello from node")
$ node myserver

$ vim myserver.js   # delete the console line and make the most basic express server we can
var express = require("express"), 
      app = express();

app.get('/', function(req, res){
res.sendfile(__dirname + '/client/views/index.html');
});

app.listen(3000, function(){
console.log('I\'m listenning...');
});
npm install express  --save     # --save will save it to package.json
$ cat package.json     # express has been saved in the package.json
{                                                                                                                                       
  "name": "mean-demo",                                                                                                                  
  "version": "0.0.1",                                                                                                                   
  "dependencies": {                                                                                                                     
    "express": "^4.9.8"                                                                                                                 
  }                                                                                                                                     
}      

$ mkdir -p client/views
$ vim client/views/index.html
<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title></title>  
    </head>
    <body>
        <p>Hello!</p>
    </body>
</html>

$ node myserver.js

browse to http://localhost:3000

Angular js is a client side MVC and allows us to build more dynamic web applications. by separating concerns model/view/controller.
declarative UI bindings
embeddable. use it or not you won't change whole website
testable

$ vim client/views/index.html          # add angular script to html. we added angular.js instead of angular.min.js so that we get more friendly error messages if we need to debug. start to use these declarations to tell angular that this html page is an application. angular is named after these angular brackets. and ng is short for angular
<!doctype html>                                                                                                                  
<html ng-app="meetupApp">                                                                                                                                  
    <head>                                                                                                                              
        <meta charset="utf-8">                                                                                                          
        <meta http-equiv="X-UA-Compatible" content="IE=edge">                                                                           
        <meta name="description" content="">                                                                                            
        <meta name="viewport" content="width=device-width, initial-scale=1">                                                            
        <title></title>                                                                                                                 
    </head>                                                                                                                             
    <body>                                                                                                                              
                                                                                                                                        
         <!-- This is our Meetups View, but usually we have them in a separate file and include them here. the value of the meetupsCount
                                                                                                                                        
       <div ng-controller="meetupsController">                                                                                          
               <h1> there are {{meetupsCount}} meetups </h1>                                                                            
       </div>                                                                                                                           
                                                                                                                                        
       <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0/angular.js"></script>  
                
    </body>                                                                                                                             
</html>   

$ mkdir -p client/js/controllers         # js directory will include controllers and filters and services and etc
$ vim 
client/js/controllers/meetups-controller.js    
# angular will look for a function with the name of our controller and it will inject the scope. The scope is what's used to talk between the view and the controller so that's how angular manages to handle the binding between these two things and allow them to talk. so on the scope we want to have something called meetupsCount.


angular.module('meetupApp', [])
    .controller('meetupsController', function($scope) {
                 $scope.meetupsCount = 10;
});

so we need to include this script, we made it in its own file and its not part of our client. its not downloaded in the browser yet

$ vim client/views/index.html  
<!doctype html >                                                                                                                  
<html ng-app="meetupApp">                                                                                                                                  
    <head>                                                                                                                              
        <meta charset="utf-8">                                                                                                          
        <meta http-equiv="X-UA-Compatible" content="IE=edge">                                                                           
        <meta name="description" content="">                                                                                            
        <meta name="viewport" content="width=device-width, initial-scale=1">                                                            
        <title></title>                                                                                                                 
    </head>                                                                                                                             
    <body>                                                                                                                              
                                                                                                                                        
         <!-- This is our Meetups View, but usually we have them in a separate file and include them here. the value of the meetupsCount
                                                                                                                                        
       <div ng-controller="meetupsController">                                                                                          
               <h1> there are {{meetupsCount}} meetups </h1>                                                                            
       </div>                                                                                                                           
       <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0/angular.js"></script>
       <script type="text/javascript" src="/js/controllers/meetups-controller.js"></script>                  
                 
    </body>                                                                                                                             
</html>   

 because we couldn't actually request this javascript file anyways. because we haven't added a route in our server, we've only added a route for our base URL. so we need to add a route so our server can answer back with javascript files and we'll do a little shortcut and cut that client out so we don't have to say client/js everywhere

$ vim myserver.js   # use a middleware so any time anyone asks for /js we can use this helper function express.static and what we'll do is we reply with /client/js. this means any file requested inside js will be returned statically from express, and I'm just shortcutting it so that we dont use client. so now you can reply back for any script we these static files
var express = require("express"), 
      app = express();

app.get('/', function(req, res){
res.sendfile(__dirname + '/client/views/index.html');
});

app.use('/js', express.static(__dirname + '/client/js'));

app.listen(3000, function(){
console.log('I\'m listenning...');
});

browse to http://localhost:3000
T
here is a bug and it returns 
there are {{meetupsCount}} meetups
Ctrl+Shift+i to open developer tools in chrome. in console tab you see
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost:3000/js/controllers/meetups-controller.js

tht's because I didn't restart my server. I created a new route but it says hey i don't have it.

So it delivered the script, angular created the bound and you get the result in the scope


display meetup names:


$ vim client/views/index.html
<!doctype html>                                                                                                                  
    <ul>
        <li ng-repeat="meetup in meetups"> {{meetup.name}} </li>    
    </ui>      
                                                              
       </div>                                                                                                                           
$ vim client/js/controllers/meetups-controller.js    
angular.module('meetupApp', [])
    .controller('meetupsController', function($scope) {
                 $scope.meetupsCount = 10;
      $scope.meetups = [{name:"SF Developers"}, {name:"Some other meetup"}]
});


Form to add meetup


$ vim client/views/index.html    
# add attribute ng-model to the input of the form. Anything we do here is going to have two way binding to the controller, actually scope on the controller. So anything we type in the inout text box will have acess on our controller through the scope. Have a submit button. Now we could do a click event on the button  and handle it and pass that into our controller but I like tot use ng-submit and angular would do the submission of the form back to your controller and call a function. The function doesn't take any arguments because we will have access to that variable that we defined through ng-model.
</script> $ vim client/js/controllers/meetups-controller.js    
angular.module('meetupApp', [])
.controller('meetupsController', function($scope) {
$scope.meetupsCount = 10;
$scope.meetups = [{name:"SF Developers"}, {name:"Some other meetup"}]
$scope.createMeetup = function(){
$scope.meetups.push({name:$scope.meetupName})
}
});


Calculate the count accordingly and clear the input text after add


$ vim client/views/index.html    
# add attribute ng-model to the input of the form. Anything we do here is going to have two way binding to the controller, actually scope on the controller. So anything we type in the inout text box will have acess on our controller through the scope. Have a submit button. Now we could do a click event on the button  and handle it and pass that into our controller but I like tot use ng-submit and angular would do the submission of the form back to your controller and call a function. The function doesn't take any arguments because we will have access to that variable that we defined through ng-model.

<!doctype html>
<html ng-app="meetupApp">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge"> 
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title></title> 
</head> 
<body>
   <!-- This is our Meetups View, but usually we have them in a separate file and include them here. the value of the meetupsCount -->
   <div ng-controller="meetupsController">
     <h1> there are {{meetups.length}} meetups </h1>
     <ul>
        <li ng-repeat="meetup in meetups"> {{meetup.name}} </li>
    </ui>
    <form ng-submit="createMeetup()">
        <input type="text" placeholder="Meetup name" ng-model="meetupName"></input>
        <button type="submit">Add</button>
    </form>
</div> 
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0/angular.js"></script> 
<script type="text/javascript" src="/js/controllers/meetups-controller.js"></script> 
</body> 
</html> 

$ vim client/js/controllers/meetups-controller.js    
angular.module('meetupApp', [])
.controller('meetupsController', function($scope) {
$scope.meetupsCount = 10;
$scope.meetups = [{name:"SF Developers"}, {name:"Some other meetup"}]
$scope.createMeetup = function(){
$scope.meetups.push({name:$scope.meetupName})
                $scope.meetupName = ''
}
});




Store in mongodb

$ ./mongo
> use mongo-db
> show collections
>  db.meetups.insert({name:"MEAN SF Developers"})
> show collections
> db.meetups.find()
db.meetups.insert({name:"MEAN SF Developers", speaker: "Mike Moser")
> db.meetups.find()

ng-resource does that. it's a module you can add on but adding angular-resource. and we can use it in our application, but we haven't defined an angular application and we need to tell our application that it has a dependency on ng-resource so it can be injected in our modules and controllers.
To create an angulae application I like to create it in my js folder and I like to call it app.js and this is where we can define our angular module and I like to call it app. So I have an app on the server side and app on client side. but we're just defining an angular module and we're calling it meetupApp and we tell it it has dependency on ngResource that is downloaded into our client 

$ vim client/js/app.js
var app = angular.module('meetupApp', ['ngResource']);

now that we have our module we have our controller just sitting in the space and it's not a part of this application. so now that we have an app defined we want to add this controller to the application so that it has access to the ngResource. add dependencies (scope and resource) and add a funciton and name those dependencies i like to use the same name

$ vim client/js/controllers/meetups-controller.js    
app.controller('meetupsController', ['$scope', 
'$resource', 
function($scope, $resource){
$scope.meetups = [{name:"SF Developers"}, {name:"Some other meetup"}]
$scope.createMeetup = function(){
$scope.meetups.push({name:$scope.meetupName})
$scope.meetupName = ''
}
}
]);

So we said on our app there is a controller named metupsController it has a dependency on scope and dependency on resource so angular will inject those in to the function parameters. So we should be able to take our code out of the controller down there and put it inside the function.

So we added app.js so we need to use app.js and bootup our app. 

$ vim client/views/index.html    

<!doctype html>
<html ng-app="meetupApp">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge"> 
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title></title> 
</head> 
<body>
   <!-- This is our Meetups View, but usually we have them in a separate file and include them here. the value of the meetupsCount -->
   <div ng-controller="meetupsController">
     <h1> there are {{meetups.length}} meetups </h1>
     <ul>
        <li ng-repeat="meetup in meetups"> {{meetup.name}} </li>
    </ui>
    <form ng-submit="createMeetup()">
        <input type="text" placeholder="Meetup name" ng-model="meetupName"></input>
        <button type="submit">Add</button>
    </form>
</div> 
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0/angular.js"></script> 
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0/angular-resource.js"></script> 
<script type="text/javascript" src="/js/app.js"></script> 
<script type="text/javascript" src="/js/controllers/meetups-controller.js"></script> 
</body> 
</html> 

So now it knows to use this particular meetupApp angular module which is our application and we've configured our controller on it so it will also be able to see our controller and do all the things it needs to do.


So now controller has access to ngResource.
ngResource is equivalent of model I guess. The way it works is if you give it a base URL for your server, for meetups for example, it knows how to do all ReSTfull operations. get post put delete, ... and as long as the server follows a standard API, it makes you not to have to deal with not much code in the client side it handles all the http requests and gives you a nice syntax around it.

So one way to use it is: meetup = a new resource and all we have to do is say what the base URL of our server, of our restful service is. we will create the restful service.    /api/meetups will be the base url of our rest resource for our model essentially 

app.controller('meetupsController', ['$scope', '$resource', function($scope, $resource){
var Meetup = $resource('/api/meetups')
$scope.meetups = [{name:"SF Developers"}, {name:"Some other meetup"}]
$scope.createMeetup = function(){
var meetup = new Meetup();
meetup.name = $scope.meetupName;
meetup.$save();
}
]);


So this means when you say add this meetup, call this function on the controller on the client side and our client side will call the restful service on our server and try to save that there

browse to http://localhost:3000 and you'll see that it gives an error of not finding the api path. you can open the network tab of developer tools and see it is a post request and see the json of the meetup you wanted to add there.

So let's go to our server and add the path. app. verb name for our route and we're going to name the path. and give it a call back function and we can read values from request and respond back to the user.

$ vim myserver.js  
var express = require("express"), 
app = express();

app.get('/', function(req, res){
res.sendfile(__dirname + '/client/views/index.html');
});

app.post('/api/meetups', function(req, res){
})

app.listen(3000, function(){
console.log('I\'m listenning...');
});

now I don't like to code everything in one file so I create a directory for server side code.

$ mkdir -p server/controllers
$ vim server/controllers/meetup-controller.js 
module.exports.create = function(req, res){
console.log(req.body);
}

so this is just a node module and we're going to require it from our server so that we can use this module. So the way that node works is if you want somebody to have access to your module and have access to something like a function then you need to export it. so we export a function called create. So I'm going to take the newly function added above to here. just to reorganize my code.

now I go back to the server and the server need to pass that to the controller. I would normally create a namespace so inside controller i create index.html or js and I have different name spaces for differnet controllers. So yu can require diffenret controllers folder and you could say controller.meetups, controller.this, bla bla but for this demo we just do something a little bit straight forward. so once we have that we can say meetupControllers.create  (the exported function)

$ vim myserver.js  
var express       = require("express"), 
app               = express(),
meetupController = require('./server/controllers/meetup-controller');

app.get('/', function(req, res){
res.sendfile(__dirname + '/client/views/index.html');
});

app.post('/api/meetups', meetupController.create)

app.use('/js', express.static(__dirname + '/client/js'));

app.listen(3000, function(){
console.log('I\'m listenning...');
});


So that just says when somebody calls this route call this function on the meetups controller.
now restart the server because we added some routes 

browse to http://localhost:3000  and you'll note you get undefined for req.body, but we did see the json in the request above. The reason it says it's undefined is because express doesn't use a body parser by default. So it doesn't recognize the body of hte request as json  and it doesn't parse it.

$ npm install body-parser --save

$ vim myserver.js  
var express       = require("express"), 
app               = express(),
        bodyParser = require('body-parser'),
meetupController = require('./server/controllers/meetup-controller');

app.use(bodyParser());  //in the middleware inject body-parser. make sure middleware is added before routes. otherwise it goes to route before hitting middleware.

app.get('/', function(req, res){
res.sendfile(__dirname + '/client/views/index.html');
});

app.post('/api/meetups', meetupController.create)

app.use('/js', express.static(__dirname + '/client/js'));

app.listen(3000, function(){
console.log('I\'m listenning...');
});

http://localhost:3000  and it correctly displays body of request.







So now on our server we want to store this. So we look at our controller that's handling this we want to store this in our mongodb databse. So what do we do for that? we could use a native mongodb drivers for node, I like to use something called mongoose, it's an ODM (Object Data Mapping) and it allows you to define your schema in your application and gives you some nice syntax around the driver for that. it's not always the right choice there are some performance overhaed to it, so make your own choice 

$ npm install mongoose --save

So we are doing MVC on the server and on the client. so I wouldn't use MVC right in the controller. I would create a model object 

$ mkdir server/models
$ vim server/models/meetup.js

This will be the meetup model for mongoose.
so I just create mongoose. and I use the simplest way to define schema but yo can do a lot better. like validation, ... So again this is just a module and people gotta have access to it. so export. Meetup will be its name

var mongoose  = require('mongoose');

module.exports = mongoose.model('Meetup', {
   name: String
})


usually I use name space like models.meetup .... but now I just use directly.

$ vim server/controllers/metups-controller.js
var Meetup = require('../models/meetup')

module.exports.create = function(req, res){
    var meetup = new Meetup(req.body);   // it will just create a new obj as it is json
    meetup.save();
}

I need to connect to the databse. so in server:
$ vim myserver.js  
var express       = require("express"), 
app               = express(),
        bodyParser           = require('body-parser'),
        mongoose             = require('mongoose'),
meetupController = require('./server/controllers/meetup-controller');

mongoose.connect('mongodb://localhost:27017/mongo-demo');

app.use(bodyParser());  //in the middleware inject body-parser. make sure middleware is added before routes. otherwise it goes to route before hitting middleware.

app.get('/', function(req, res){
res.sendfile(__dirname + '/client/views/index.html');
});

app.post('/api/meetups', meetupController.create)

app.use('/js', express.static(__dirname + '/client/js'));

app.listen(3000, function(){
console.log('I\'m listenning...');
});


save DONE. now have a call back on save

$ vim server/controllers/metups-controller.js
var Meetup = require('../models/meetup')

module.exports.create = function(req, res){
    var meetup = new Meetup(req.body);   // it will just create a new obj as it is json
    meetup.save(function(err, result){
        res.json(result)
    }
);
}

$vim ./client/js/controllers/meetups-controller.js

app.controller('meetupsController', ['$scope', '$resource', function($scope, $resource){
var Meetup = $resource('/api/meetups')
$scope.meetups = [{name:"SF Developers"}, {name:"Some other meetup"}]
$scope.createMeetup = function(){
var meetup = new Meetup();
meetup.name = $scope.meetupName;
meetup.$save(function(result){          //for this guy i just do result without error
                     $scope.meetups.push(result);
                     $scope.meetupName = '';
               });
}
]);

so when you add a new meetup, it stores it in the data bse, responds back to the client and adds it to the collection! awesome!



pull all the data back from DB instead of the hardcoded ones in clinet 

$vim ./client/js/controllers/meetups-controller.js

app.controller('meetupsController', ['$scope', '$resource', function($scope, $resource){
var Meetup = $resource('/api/meetups')
        Meetup.query(function(results){
             $scope.meetups = results;
         })

$scope.meetups = []

$scope.createMeetup = function(){
var meetup = new Meetup();
meetup.name = $scope.meetupName;
meetup.$save(function(result){          //for this guy i just do result without error
                     $scope.meetups.push(result);
                     $scope.meetupName = '';
               });
}
]);




So we need to define the REST route of meetups to the server

 $ vim myserver.js  
var express       = require("express"), 
app               = express(),
        bodyParser           = require('body-parser'),
        mongoose             = require('mongoose'),
meetupController = require('./server/controllers/meetup-controller');

mongoose.connect('mongodb://localhost:27017/mongo-demo');

app.use(bodyParser());  //in the middleware inject body-parser. make sure middleware is added before routes. otherwise it goes to route before hitting middleware.

app.get('/', function(req, res){
res.sendfile(__dirname + '/client/views/index.html');
});

// REST API
app.get('/api/meetups', meetupController.list)
app.post('/api/meetups', meetupController.create)

app.use('/js', express.static(__dirname + '/client/js'));

app.listen(3000, function(){
console.log('I\'m listenning...');
});


$ vim server/controllers/metups-controller.js
var Meetup = require('../models/meetup')

module.exports.create = function(req, res){
    var meetup = new Meetup(req.body);   // it will just create a new obj as it is json
    meetup.save(function(err, result){
        res.json(result)
    });
}

module.exports.list = function(req, res){
      Meetup.find({}, function(err, results){ //mongoose 
             res.json(results);
       });
    });
}












Comments