<form.io> provides developers an easy drag & drop interface that creates both forms and the REST API's in one easy step!
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew install node
brew install mongodb
npm install -g grunt-cli
npm install -g bower
gem install compass
mongod
"API first"
Initialize the app
mkdir server
cd server
npm init
Only provide the Name. Press enter through all the questions.
name: (server) MeanApp
version: (1.0.0)
description: A M.E.A.N application.
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /Users/travistidwell/Documents/projects/meanapp/server/package.json:
{
"name": "MeanApp",
"version": "1.0.0",
"description": "A M.E.A.N application.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
npm install --save express
npm install --save mongoose
npm install --save resourcejs
npm install --save method-override
npm install --save body-parser
npm install --save lodash
server/index.jsvar express = require('express'); var mongoose = require('mongoose'); var bodyParser = require('body-parser'); var methodOverride = require('method-override'); var _ = require('lodash');
// Create the application. var app = express();
// Add Middleware necessary for REST API's app.use(bodyParser.urlencoded({extended: true})); app.use(bodyParser.json()); app.use(methodOverride('X-HTTP-Method-Override'));
// Connect to MongoDB mongoose.connect('mongodb://localhost/meanapp'); mongoose.connection.once('open', function() {
console.log('Listening on port 3000...'); app.listen(3000); });
Shell
node index.js
Output
Listening on port 3000...
If you to to http://localhost:3000 you will see the server is running, but nothing shows up. This is expected.
Let's add CORS support for RESTful interfaces.
server/index.js
...
...
app.use(methodOverride('X-HTTP-Method-Override'));
// CORS Support
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
mongoose.connect('mongodb://localhost/meanapp');
...
...
server/index.js
...
...
app.use('/hello', function(req, res, next) {
res.send('Hello World!');
next();
});
...
...
Now go to http://localhost:3000/hello in your browser.
This is the fundamental idea at registering REST endpoints at certain paths.
New file @ server/models/Movie.jsvar mongoose = require('mongoose');
// Create the MovieSchema. var MovieSchema = new mongoose.Schema({
title: { type: String, required: true }, url: { type: String, required: true } });
// Export the model. module.exports = mongoose.model('movie', MovieSchema);
New file @ server/models/index.js
module.exports = {
movie: require('./Movie')
};
server/index.js
...
...
mongoose.connection.once('open', function() {
// Load the models.
app.models = require('./models/index');
...
...
New file @ server/controllers/MovieController.jsvar Resource = require('resourcejs');
module.exports = function(app, route) {
// Setup the controller for REST; Resource(app, '', route, app.models.movie).rest();
// Return middleware. return function(req, res, next) { next(); }; };
New file @ server/routes.js
module.exports = {
'movie': require('./controllers/MovieController')
};
server/index.js... ... app.models = require('./models/index'); // Load the routes. var routes = require('./routes');
_.each(routes, function(controller, route) {
app.use(route, controller(app, route)); }); ... ...
https://github.com/travist/meanapp/tree/resourcejs/server
cd server
node index.js
in AngularJS
http://yeoman.io/
mkdir client
cd client
npm install -g yo
npm install -g generator-angular
yo angular
yo angular:route movies
Remove controllerAs
statements
These are only needed for nested views.
app/app.js
.when('/movies', {
templateUrl: 'views/movies.html',
controller: 'MoviesCtrl',
})
app/views/movies.html<table class="table table-striped"> <thead> <th>Title</th> <th>URL</th> </thead> <tbody>
<tr ng-repeat="movie in movies">
<td>{{ movie.title }}</td> <td>{{ movie.url }}</td>
</tr> </tbody> </table>
app/scripts/controllers/movies.js
angular.module('clientApp')
.controller('MoviesCtrl', function ($scope) {
$scope.movies = [
{
title: 'A New Hope',
url: 'http://youtube.com/embed/1g3_CFmnU7k'
},
{
title: 'The Empire Strikes Back',
url: 'http://youtube.com/embed/96v4XraJEPI'
},
{
title: 'Return of the Jedi',
url: 'http://youtube.com/embed/5UfA_aKBGMc'
}
];
});
Let's hook up $scope.movies with our server.
Use Bower to add Restangular to your project
bower install --save restangular
Verify it adds it to app/index.html
app/scripts/app.jsangular .module('clientApp', [ 'ngRoute', 'restangular' ]) .config(function ( $routeProvider,
RestangularProvider ) {
RestangularProvider.setBaseUrl('http://localhost:3000'); ... ...
app/scripts/app.js.factory('MovieRestangular', function(Restangular) { return Restangular.withConfig(function(RestangularConfigurer) { RestangularConfigurer.setRestangularFields({ id: '_id' }); }); })
.factory('Movie', function(MovieRestangular) { return MovieRestangular.service('movie'); })
app/controllers/movies.js
.controller('MoviesCtrl', function ($scope, Movie) {
$scope.movies = Movie.getList().$object;
});
yo angular:route movie-add --uri=create/movie
yo angular:route movie-view --uri=movie/:id
yo angular:route movie-delete --uri=movie/:id/delete
yo angular:route movie-edit --uri=movie/:id/edit
Remove controllerAs
statements
These are only needed for nested views.
app/app.js
.when('/create/movie', {
templateUrl: 'views/movie-add.html',
controller: 'MovieAddCtrl'
})
.when('/movie/:id', {
templateUrl: 'views/movie-view.html',
controller: 'MovieViewCtrl'
})
.when('/movie/:id/delete', {
templateUrl: 'views/movie-delete.html',
controller: 'MovieDeleteCtrl'
})
.when('/movie/:id/edit', {
templateUrl: 'views/movie-edit.html',
controller: 'MovieEditCtrl'
})
app/views/movie-add.html
app/scripts/controllers/movie-add.js
.controller('MovieAddCtrl', function ($scope, Movie, $location) {
$scope.movie = {};
$scope.saveMovie = function() {
Movie.post($scope.movie).then(function() {
$location.path('/movies');
});
};
});
app/views/movie-view.html
{{ movie.title }}
{{ movie.url }}
app/scripts/controllers/movie-view.js
.controller('MovieViewCtrl', function (
$scope,
$routeParams,
Movie
) {
$scope.viewMovie = true;
$scope.movie = Movie.one($routeParams.id).get().$object;
});
app/views/movie-edit.html
app/scripts/controllers/movie-edit.js
.controller('MovieEditCtrl', function (
$scope,
$routeParams,
Movie,
$location
) {
$scope.editMovie = true;
$scope.movie = {};
Movie.one($routeParams.id).get().then(function(movie) {
$scope.movie = movie;
$scope.saveMovie = function() {
$scope.movie.save().then(function() {
$location.path('/movie/' + $routeParams.id);
});
};
});
});
app/views/movie-delete.html
app/scripts/controllers/movie-delete.js
.controller('MovieDeleteCtrl', function (
$scope,
$routeParams,
Movie,
$location
) {
$scope.movie = Movie.one($routeParams.id).get().$object;
$scope.deleteMovie = function() {
$scope.movie.remove().then(function() {
$location.path('/movies');
});
};
$scope.back = function() {
$location.path('/movie/' + $routeParams.id);
};
});
Create: app/views/movie-nav.html
app/views/movie-view.html
{{ movie.title }}
{{ movie.url }}
app/views/movie-edit.html
app/views/movies.html
Create Movie
Title
URL
Operations
{{ movie.title }}
{{ movie.url }}
app/scripts/app.js.directive('youtube', function() { return { restrict: 'E', scope: { src: '=' }, templateUrl: 'views/youtube.html' }; })
.filter('trusted', function ($sce) { return function(url) { return $sce.trustAsResourceUrl(url); }; });
app/views/youtube.html
app/views/movie-view.html
{{ movie.title }}