MEAN 全栈开发 ——实现简单博客

最近在学习MEAN全栈开发的过程中,写了一个小程序就当练练手熟悉一下怎么去组合强大的功能。

废话不多说,直接上文件预览:

整体文件结构:

其中,public文件夹存放了img,js,css文件,其中的index.js文件用于配置前台路由,routes文件夹存放了后台路由的配置文件,views文件夹存放静态视图文件,app.js文件作为程序的入口,相当于main函数一样。

前台路由设置:

public/javascripts/index.js

/*前端路由处理*/
//创建服务
var blogServices = angular.module('blogServices', ['ngResource']);
//Blog代表上述服务
blogServices.factory('Blog', ['$resource',
    function($resource){
        return $resource('/api/list/:_id', {_id: '@id'},
            {
                //默认提供5种方法,在此可以自定义方法;
            }
        );
}]);

//在模板中注入模块
var app = angular.module('app', [
    'ngRoute',
    'blogServices'
]);

app.directive('focus', function () {
    return {
        restrict: 'A',//限制只可通过属性调用
        link: function (scope, element, attr) {
            element[0].focus();
        }
    }
});


app.config(function ($routeProvider) {
    $routeProvider.
        //自动忽略前面的#
        when('/', {
            templateUrl: 'posts.html',// 当打开链接为 "/", 载入posts.html
            controller: postsCtrl
        }).
        //add
        when('/api/add', {
            templateUrl: 'add.html',
            controller: postCtrl
        }).
        //read
        when('/api/list/:_id',{
            templateUrl: 'read.html',
            controller: readCtrl
        }).
        //modify
        when('/api/modify/:_id', {
            templateUrl: 'modify.html',
            controller: editCtrl
        }).
        //delete
        when('/api/del/:_id',{
            templateUrl: 'posts.html',
            controller: deleteCtrl
        });
    //otherwise({
    //    redirectTo: '/' // 其他情况,跳到链接"/"
    //});
});

/* 每个路由对应的控制器 */
// 文章列表控制器
// 注入Blog服务
function postsCtrl($scope,Blog) {
    //去后端访问route.get('/list'),返回查找的数据
    $scope.posts = Blog.query();
}

// 发布文章控制器
function postCtrl($scope, Blog, $location) {  // 注入$location服务
    $scope.form = {};   // 初始化一个数据模型
    // 提交操作函数
    $scope.form.submit = function () {
        Blog.save({}, $scope.form,function(){
            $location.url('/'); // 返回首页;
        });
    };
}

// 读取文章控制器
function readCtrl($scope, Blog, $routeParams){
    // 将获取到的数据 通过$scope绑定成NG的数据模型
    $scope.post = Blog.get({_id:$routeParams._id});
}

//删除文章控制器
function deleteCtrl($scope, Blog, $location, $routeParams){
    Blog.delete({_id:$routeParams._id},function(){
        $location.url('/'); // 返回首页;
    });
}

// 修改文章控制器
function editCtrl($scope, Blog, $routeParams, $location) {
    //向后台申请数据,写入post模型
    $scope.post = Blog.get({_id:$routeParams._id});
    // 提交操作函数
    $scope.submit = function () {
        Blog.save({_id: $routeParams._id},$scope.post,function(){
            $location.url('/'); // 返回首页
        });
    };
}

/* $http方法 */
/* 文章列表控制器
function postsCtrl($scope, $http) {    // 注入$Http服务,类似于jquery的ajax
    //去后端访问route.get('/list'),返回查找的数据
    $http.get('/api/list').success(function (data) {
        $scope.posts = data; // 将获得的数据保存到NG的数据模型posts里
    });
}
 发布文章控制器
function postCtrl($scope, $http, $location) {  // 注入$location服务
    $scope.form = {};   // 初始化一个NG数据模型
    // 提交操作函数
    $scope.form.submit = function () {
        $http.post('/api/add', $scope.form).success(function () {
            $location.url('/'); // 返回首页
        });
    };
}
// 读取文章控制器
function readCtrl($scope,$http, $routeParams){
    $http.get('/api/list/' + $routeParams._id).success(function(data){
        $scope.post = data; // 将获取到的数据 通过$scope绑定成NG的数据模型
    });
}
// 修改文章控制器
function editCtrl($scope, $http, $routeParams, $location) {  // 注入$location服务
    //向后台申请数据
    $http.get('/api/modify/' + $routeParams._id).success(function (data){
        //将数据存入post
        $scope.post = data;
    });

    $scope.form = {};   // 初始化一个NG数据模型
    // 提交操作函数
    $scope.form.submit = function () {
        $http.post('/api/modify/' + $routeParams._id, $scope.post).success(function () {
            $location.url('/'); // 返回首页
        });
    };
}

//删除文章控制器
function deleteCtrl($scope, $http, $location, $routeParams){
    $http.get('/api/del/' +  $routeParams._id).success(function () {
        $location.url('/'); // 返回首页
    });
}*/

// 启动模块
angular.bootstrap(document, ['app']);

用了两种方法去实现,开始用了$http去写路由(见注释部分),最后改为使用$resource去管理API,这里需要注意一点,只用服务端按照RESTful的方式工作的时候,才可以使用Angular资源。

服务端路由

/routes/index.js

'use strict';
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var model = require('./model');
var Demo = model.Demo;
mongoose.connect('mongodb://localhost/monkey');
/*这里的路由是用来处理访问为'xxx'的请求*/
/* 查找数据库数据.*/
router.get('/api/list',function(req,res,next){
		console.log("This is finding data!");
		Demo.find({}).sort({createTime: -1}).exec(function (err, docs) {
			res.json(docs);
		});
});

/*跳转到添加页面,创建新纪录*/
router.post('/api/list',function(req, res){
	var demo = new Demo(req.body);
	demo.save(function(err,doc){
		if (err) {
			console.log(err);
			res.send(err);
		}
		else {
			console.log('create');
			console.log(doc);
			res.json({status: 'done'});
		}
	});
});

//根据id查找相应的记录
router.get('/api/list/:_id',function(req,res){
	var id = req.params._id;
	console.log(id);
	console.log('Now start to read!');
	if(id) {
		//返回文档
		Demo.findOne({_id: id}).exec(function (err, docs) {
			console.log(docs);
			res.json(docs);
		});
	}
});

// 根据id删除相应的记录
router.delete('/api/list/:_id',function(req, res){
	var id = req.params._id;
	console.log('delete');
	console.log(id);
	if(id) {
		console.log('delete id = ' + id);
		Demo.findByIdAndRemove(id, function(err, docs) {
			console.log(docs);
			res.json(docs);
		});
	}
});

/*查询对应修改记录,并跳转到修改页面
router.get('/api/modify/:_id',function(req, res) {
	var id = req.params._id;
	console.log('id = ' + id);
	console.log('Now start to modify!');
	Demo.findOne({_id: id}).exec(function (err, docs) {
		console.log(docs);
		res.json(docs);
	});
});*/

//修改相应的值
router.post('/api/list/:_id',function(req, res) {
	var demo = new Demo(req.body);
	var id = req.params._id; //因为是post提交,所以不用query获取id
	if(id) {
		console.log('----update');
		console.log(demo);
		Demo.findByIdAndUpdate(id, demo,function(err, docs) {
			res.json({status: 'done'});
		});
	}
});

module.exports = router;

  这里的作用是来处理对应URL的前台访问,作为对应的处理函数。

定义数据库

/routes/model.js

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var demoSchema = new Schema({
	uid : {
		required:true,
		type:String,
		unique:true,
		trim:true
	},
	title: {
		type:String,
		required:true
	},
	content: {
		type:String,
		required:true
	},
	createTime : {
		type: Date,
		default: Date.now
	}
});

Demo = mongoose.model('Demo',demoSchema);
exports.Demo = Demo;

视图文件

views//index.html

<!DOCTYPE HTML>
<html lang="zh-CN">
<head>
	<meta charset="UTF-8">
	<title>MAEN BLOG</title>
    <link rel="stylesheet" href="stylesheets/common.css" />
    <link rel="stylesheet" href="stylesheets/index.css" />
</head>
<body>
    <header>
        <h1 id="logo"><a href="#">BLOG List</a></h1>
    </header>

    <div id="main">
        <!-- 路由区域 -->
        <ng-view />
    </div>

	<!-- javascript-->
	<script src="javascripts/plugins/angular/angular.min.js" type="text/javascript"></script>
	<script src="javascripts/plugins/angular-route/angular-route.min.js" type="text/javascript"></script>
    <script src="javascripts/plugins/angular-resource/angular-resource.js" type="text/javascript"></script>
    <script src="javascripts/index.js"></script>
</body>
</html>

views/add.html

<form id="post" ng-submit="form.submit()">
    <input ng-model="form.uid" focus type="text" placeholder="文章ID" />
    <input ng-model="form.title"  type="text" placeholder="标题" />
    <textarea ng-model="form.content" placeholder="正文"></textarea>
    <div>
        <button class="btn btn-danger pull-right" type="submit">提交发布</button>
    </div>
</form>

views/modify.html

<form id="modify" ng-submit="submit()">
    <div>
        <div>{{post.createTime|date: 'yyyy-MM-dd HH:mm:ss'}}</div>
        <input ng-model="post.title" focus type="text">
        <textarea ng-model="post.content"></textarea>
        <div>
            <button class="btn btn-danger pull-right" type="submit">save</button>
        </div>
    </div>
</form>

views/posts.html

<table>
    <tr>
        <th>题目 -
            <button class="btn btn-danger"><a href="/#/api/add">发表文章</a></button>
        </th>
        <th>时间</th>
        <th></th>
        <th></th>
    </tr>
    
    <!-- ng-repeat可以根据NG数据模型遍历数据,相当于forEeach -->
    <tr ng-repeat="post in posts">
        <td><a href="/#/api/list/{{post._id}}">{{ post.title }}</a></td>
        <!-- 用filter过滤器,转换了显示的时间格式 -->
        <td class="text-muted">{{ post.createTime|date: 'yyyy-MM-dd HH:mm:ss'}}</td>
        <td><a href="/#/api/modify/{{post._id}}">修改</a></td>
        <td><a href="/#/api/del/{{post._id}}">删除</a></td>
    </tr>

   <!-- ng-hide表示,当文章列表有内容,将不显示这里 -->
    <tr ng-hide="posts.length">
        <td colspan="2" class="text-muted text-center">没有文章哦</td>
    </tr>
</table>

views/read.html

<form id="read" ng-submit="form.submit()">
    <div>
        <div>
            {{post.createTime|date: 'yyyy-MM-dd HH:mm:ss'}}
        </div>
        <div>
            <input ng-model="post.title" focus type="text">
        </div>
        <textarea ng-model="post.content"></textarea>
        <div>
            <a href="#" class="pull-right">back</a>
        </div>
    </div>
</form>

引导文件

app.js

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./routes');
var user = require('./routes/user');
var http = require('http');
var ejs = require('ejs');
var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');
app.engine('html',ejs.__express);

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'views')));
app.use('/',routes);
app.use('/users',user.list);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});
module.exports = app;

  

现在的程度只能做到这样子,希望以后的学习中会不断优化。

原文地址:https://www.cnblogs.com/nanchen/p/4786877.html