MEAN Stack – Thiết lập các module và tạo layout với AngularJS

MEAN Stack

Trong phần này, mình sẽ hướng dẫn các bạn thiết lập cấu trúc các module của AngularJS để tạo layout cơ bản trong ứng dụng MEAN Stack, chúng ta sẽ tạo các view cho việc thêm, xóa, sửa và hiển thị danh sách các bài viết.

Ở đây mình sẽ tạo các giao diện rất đơn giản vì mình chủ yếu muốn tập trung vào phần xử lý chứ không phải giao diện. Lan man thế đủ rồi, giờ bắt tay vào làm nào 

>> MEAN Stack- Tạo cấu trúc cơ bản và chạy ứng dụng lần đầu tiên

>> Chia sẻ mã nguồn website hỏi đáp xây dựng từ MEAN Stack

>> AngularJS - Filter trong AngularJS – Hướng dẫn filter AngularJS

Chúng ta sẽ xây dựng tiếp trên cấu trúc ứng dụng mà ta đã làm ở bài trước nhé nếu bạn nào chưa làm phần trước thì hãy quay lại bài Tạo cấu trúc cơ bản và làm từ đầu nhé.

Các xử lý chính của chúng ta sẽ tập trung vào thư mục public/js và thư mục public/view. Đầu tiên hãy tạo trang index.html, đây là trang sẽ handle toàn bộ request của người dùng.

<!DOCTYPE html>

<html lang="vi" ng-app="Mean">

 <head>

 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

 <meta http-equiv="X-UA-Compatible" content="IE=edge">

 <meta name="viewport" content="width=device-width, initial-scale=1">

 <title>MEAN Tutorial</title>

 <link rel="stylesheet" type="text/css"

  href="https://fonts.googleapis.com/css?family=Roboto:400,700&subset=all">

 <!-- CSS -->

 <link rel="stylesheet" href="libs/bootstrap/dist/css/bootstrap.min.css">

 <link rel="stylesheet" href="libs/font-awesome/css/font-awesome.min.css">

 <link rel="stylesheet" href="css/style.css" type="text/css" media="all">


 </head>

 <body>

 <div ng-controller="baseController">

 <nav class="navbar navbar-default" role="navigation">

 <!-- Brand and toggle get grouped for better mobile display -->

 <div class="navbar-header">

 <button type="button" class="navbar-toggle" data-toggle="collapse"

  data-target=".navbar-ex1-collapse">

 <span class="sr-only">Toggle navigation</span>

 <span class="icon-bar"></span>

 <span class="icon-bar"></span>

 <span class="icon-bar"></span>

 </button>

 <a class="navbar-brand" ui-sref="home">Mean Tutorial</a>

 </div>

 <!-- Collect the nav links, forms, and other content for toggling -->

 <div class="collapse navbar-collapse navbar-ex1-collapse">

 <ul class="nav navbar-nav">

 <li><a ui-sref="list">Danh sách bài viết</a></li>

 <li><a ui-sref="create">Thêm bài viết mới</a></li>

 </ul>

 </div><!-- /.navbar-collapse -->

 </nav>

 <div id="content">

 <div class="container">

 <div id="main-content">

 <div ui-view class="ui-view-container"></div>

 </div>

 </div>

 </div>

 <div flash-alert="error" active-class="in alert-flash" class="fade">

 <button type="button" class="close" ng-click="hide()">&times;</button>

 <strong class="alert-heading">Thông báo!</strong>

 <span class="alert-message">{{flash.message}}</span>

 </div>


 <div flash-alert="success" active-class="in alert-flash" class="fade ">

 <button type="button" class="close" ng-click="hide()">&times;</button>

 <strong class="alert-heading">Thông báo!</strong>

 <span class="alert-message" ng-bind-html="flash.message | unsafe"></span>

 </div>

 <footer>

 <p>Copyright &copy; 2015 by Sang Nguyen</p>

 </footer>

 </div>

 <!--[if lt IE 9]>

 <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.2/html5shiv.min.js">

 </script>

 <script src="//cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.js">

 </script>

 <![endif]-->


 <!-- JS -->


 <script src="libs/angular/angular.min.js"></script>

 <script src="libs/angular-ui-router/release/angular-ui-router.min.js">

 </script>

 <script src="libs/angular-sanitize/angular-sanitize.min.js"></script>

 <script src="libs/angular-bootstrap/ui-bootstrap-tpls.min.js"></script>


 <script src="js/controllers/baseCtrl.js"></script>

 <script src="js/controllers/postCtrl.js"></script>

 <script src="js/services/postService.js"></script>


 <script src="js/app.js"></script>

 <script src="js/appRoutes.js"></script>

 <script src="js/directives.js"></script>

 <script src="js/filters.js"></script>

 <script src="js/services.js"></script>


 <script src="libs/jquery/dist/jquery.min.js"></script>

 <script src="libs/bootstrap/dist/js/bootstrap.min.js">

 </script>

 </body>

 </html>

Ở đây chúng ta có một số cặp thẻ mà bạn cần quan tâm:

– ng-app = “Mean”: Đặt tên cho module chính của chúng ta.

– ng-controller = “baseController”: Thiết lập controller mặc định cho các view.

– ui-view : Đây là directive của Angular UI. Chúng ta sẽ sử dụng UI Router mà không sử dụng route mặc định của Angular vì mình thấy nó khá cùi và ít tính năng 

– ui-sref = “home” : Đây là directive tương tự như thẻ href nhưng không sử dụng đường dẫn thực mà sử dụng tên của route.

Tiếp theo hãy qua public/js/app.js và sửa lại thành:

var app = angular.module('Mean', [

 'ui.router',

 'ngSanitize',

 'ui.bootstrap',

 'appRoutes',

 'appDirectives',

 'appFilters',

 'appServices',

 'baseCtrl',

 'postCtrl',

 'postService'

]);

Ở đây chúng ta sẽ khai báo các module trong ứng dụng, trong đó ui.router, ngsanitize và ui.bootstrap là các module đã được xây dựng trước và chúng ta chỉ sử dụng, còn những module bên dưới thì ta sẽ lần lượt xây dựng.

Viết nội dung cho public/js/appRoutes.js

angular.module('appRoutes',[]).config([

 '$stateProvider',

 '$urlRouterProvider',

 '$locationProvider',

 function($stateProvider, $urlRouterProvider, $locationProvider) {

 /*Thiết lập các state*/

 $stateProvider

 .state('home', {

 url: "/",

 templateUrl: "views/home.html",

 controller: 'baseController'

 });

 $locationProvider.html5Mode({

 enabled: true,

 requireBase: false

 });

 $locationProvider.hashPrefix('!');

}]);

Module này sẽ thiết lập các route chính trên ứng dụng. Ở đây bạn cần quan tâm tới các thiết lập state, nó sẽ bao gồm:

– tên của state.

– url: đường dẫn thực tế của route.

– templateUrl: đường dẫn tới view.

– controller: tên controller sẽ xử lý chính trên route này.

– html5Mode: để thiết lập đường dẫn gọn hơn, nó sẽ loại bỏ ‘#!’ trên đường dẫn.

Tiếp theo tới module directives, filters, serivices, ở phần này chúng ta chỉ khai báo chứ chưa có xử lý gì trong các module này.

directives.js

angular.module('appDirectives', []);

filter.js

angular.module('appFilters', []);
 

services.js

angular.module('appServices', []);
 

Bây giờ ta sẽ qua controller nhé, tạo baseController trước nào. Trong thư mục controllers, hãy tạo file baseCtrl.js

 
angular.module('baseCtrl',[])
.controller('baseController',['$scope','$http', 
function($scope, $http) {
}]);

Tạo tiếp file postCtrl.js

 
angular.module('postCtrl',[])

.controller('ListPostController',['$scope', '$state','Post',

function($scope, $state, Post) {

}])

.controller('CreatePostController',['$scope', '$state','Post', 

function($scope, $state, Post) {

}])

.controller('DetailPostController',['$scope', '$state',

'$http', '$stateParams', 'Post',

 function($scope, $state, $http,$stateParams, Post)

 {

 }

]);

Tiếp theo là public/js/services/postService.js

 
angular.module('postService', [])

.factory('Post',['$http', function($http) {

return {

};

}]);

Vậy là đã tạm ổn với các module của AngularJs, bây giờ ta sẽ chuyển qua tạo một số view cơ bản trong thư mục public/views:

home.html

<div class="jumbotron">

 <div class="container">

 <h2>MEAN Tutorials</h2>

 <p>MEAN Stack bao gồm: M (MongoDB), E (ExpressJS), A (AngularJS), N (NodeJS).</p>

 <p>MEAN Stack là giải pháp hoàn hảo để xây dựng một mô hình SPA hoàn chỉnh nhất.</p>

 <p>

 <a class="btn btn-primary btn-lg" ui-sref="list">Bắt đầu tìm hiểu</a>

 </p>

 </div>

</div>
 

list.html

<div class="col-xs-6 col-xs-offset-3">

 <h2 class="text-center">Bài viết</h2>

 <div>

 <h4><a href="#">KrakenJS – Xác thực thành viên qua

  Facebook sử dụng Passport</a>

</h4>

 <p>Tiếp theo trong phần xác thực thành viên, mình sẽ hướng dẫn

 các bạn xác thực thành viên qua tài khoản Facebook.</p>

 <a href="#" class="btn btn-info">Sửa</a>

 <a href="#" class="btn btn-danger">Xóa</a>

 </div>

 <hr>

 <div>

 <h4><a href="#">KrakenJS – Xác thực thành viên với Passport</a></h4>

 <p>Trong bài viết này mình sẽ hướng dẫn các bạn xây dựng chức năng

 đăng ký và đăng nhập sử dụng passport trong KrakenJS.

 Thực tế thì bạn có thể sử dụng tương tự những gì mình trình bày

 trong bài viết này nếu bạn sử dụng project khác không phải

 KrakenJS nhưng có sử dụng ExpressJS.</p>

 <a href="#" class="btn btn-info">Sửa</a>

 <a href="#" class="btn btn-danger">Xóa</a>

 </div>

 <hr>

 </div>
 

create.html

 
<div class="col-xs-6 col-xs-offset-3">

 <h2 class="text-center">Thêm bài viết</h2>

 <form action="#" method="POST">

 <div class="form-group">

 <label for="title" class="control-label">Tiêu đề</label>

 <input class="form-control" name="title" />

 </div>

 <div class="form-group">

 <label for="description" class="control-label">Mô tả ngắn</label>

 <textarea class="form-control" name="description"></textarea>

 </div>

 <div class="form-group">

 <label for="content" class="control-label">Nội dung</label>

 <textarea class="form-control" name="content"></textarea>

 </div>

 <div class="form-group">

 <button type="submit" name="submit" class="btn btn-primary">Thêm</button>

 </div>

 </form>

 </div>

404.html

 
<div class="page-404-wrapper">

<p class="number-404">

 4<span><i class="fa fa-question-circle"></i></span>4

 </p>

 <p class="title-404">Không tìm thấy trang yêu cầu </p>

 <p class="text-404">

 Trang này không tồn tại. Hãy chắc chắn địa chỉ bạn gõ là chính xác! </p>

 <a ui-sref="home" class="btn btn-submit" style="margin-top: 15px;">

  <i class="fa fa-home"></i> Quay lại trang chủ

 </a>

</div>

Thêm 1 chút css trong public/css/style.css cho nó gọn chút 

 
html, body {

 position: relative;

 height: 100%;

 min-height: 100%;

}

body {

 font-family: Roboto;

 font-size: 13px;

}

#main-content {

 padding-bottom: 70px;

}


footer {

 height: 50px;

 border-top: 1px solid #dedede;

 padding: 5px 0px 15px 15px;

 font-size: 13px;

 color: #999;

 position: absolute;

 bottom: 0;

 right: 0;

 left: 0;

}

.page-404-wrapper {

 text-align:center;

 padding-top:50px;

 min-height: 400px;

}

.page-404-wrapper .number-404 {

 color: #191f2d;

 font-size: 32px;

 font-weight: 600;

}

.page-404-wrapper .number-404 span {

 display:inline-block;

 margin:0 5px;

 position:relative;

}

.page-404-wrapper .number-404 span i {

 color: #f39c12;

 font-size: 60px;

 position: relative;

 top: 8px;

 margin: 0 3px;

}

.page-404-wrapper .title-404 {

 color:#2c3e50;

 font-size:24px;

 margin:0;

}

.page-404-wrapper .text-404 {

 color:#8c97b2;

 font-size:16px;

 margin:0;

}

.page-404-wrapper .link-back {

 color:#3498db;

 font-size:16px;

}

Viết thêm các state để load các view nào. Hãy sửa lại file appRoutes.js thành:

angular.module('appRoutes', []).config([

 '$stateProvider',

 '$urlRouterProvider',

 '$locationProvider',

 function($stateProvider, $urlRouterProvider, $locationProvider) {

 /*Điều hướng 404*/

 $urlRouterProvider.otherwise("/404.html");

 /*Thiết lập các state*/

 $stateProvider

 .state('home', {

 url: "/",

 templateUrl: "views/home.html",

 controller: 'baseController'

 })

 .state('list', {

 url: "/list-post",

 templateUrl: "views/list.html",

 controller: 'ListPostController'

 })

 .state('create', {

 url: "/create-post",

 templateUrl: "views/create.html",

 controller: 'CreatePostController'

 })

 /*===============404 NOT FOUND================*/

 .state('404', {

 url: "/404.html",

 templateUrl : 'views/404.html',

 title: '404 - Không tìm thấy trang yêu cầu'

 });

 $locationProvider.html5Mode({

 enabled: true,

 requireBase: false

 });

 $locationProvider.hashPrefix('!');

}]);
 

Vậy là xong thiết lập cấu trúc cho các module của AngularJS, nhưng trước khi có thể chạy thử nó, chúng ta phải  sửa lại nội dung file app/routes/index.js:

 
module.exports = function (app) {

app.get('*', function(req, res){

res.sendfile('public/index.html');

});

};

Dòng này có nghĩa là tất cả request đến ứng dụng đều được đẩy tới view index.html xử lý.

Và thêm 2 dòng này vào file server.js trước dòng server.listen(port) nhé

 
var routes =require('./app/routes');
routes(app);

Bây giờ hãy chạy ứng dụng lên để xem kết quả nào. Mở cmd lên và cd vào thư mục project của chúng ta và gõ lệnh “node server”. Sau đó kiểm tra xem kết quả tại trang http://localhost:3000 có đúng như chúng ta mong muốn không.

Nếu các bạn làm theo đúng hướng dẫn thì sẽ thấy giao diện hiển thị gồm một số view chính như thế này 

Giao diện trang chủ - MEAN Tutorial

Giao diện trang chủ – MEAN Tutorial

Giao diện danh sách bài viết - MEAN Tutorial

Giao diện danh sách bài viết – MEAN Tutorial

Giao diện thêm bài viết - MEAN Tutorial

Giao diện thêm bài viết – MEAN Tutorial

Các bạn có thể download mã nguồn cho bài 1 và bài 2 tại đây nhé:

https://github.com/sangnguyenplus/mean-tutorial/tree/MT-01

Ở phần này chúng ta mới chỉ tạo giao diện thôi và dữ liệu áp cứng trong view, phần sau mình sẽ hướng dẫn các bạn tương tác với server ExpressJs và MongoDB để thực hiện các thao thác CRUD trong ứng dụng MEAN Stack. Chúc các bạn thành công.