首先,我们来看一下工程结构目录:
我们可以看到scripts中分列了一下几个文件夹:controllers、services、directives、filters、vendor,以及两个文件app.js和main.js。
controllers里面主要就是控制器
services里面是服务,为控制器提供数据服务支持,与后台交互的代码就在其中
directives是指令,用于生成自定义标签的html模板
filters是过滤器,用于处理显示数据的形式
vendor文件夹中存放的是引用的第三方js插件
跟scripts同级的是styles和views文件夹,styles存放css样式文件,view存放html文件。
我们先来看一下index.html中的主要代码:
My AngularJS App
我们可以看到导入的js只有一个require.js,但是这个script中有个这样的属性
data-main="scripts/main"
这个属性就将主js指向了scripts/main.js。
下面来看main.js,其中,分为了两块,第一块是对第三方的引用和依赖配置:
// the app/scripts/main.js file, which defines our RequireJS config require.config({ paths:{ angular:'vendor/angular.min', jquery:'vendor/jquery', bootstrap:'vendor/bootstrap.min', domReady:'vendor/domReady', ngGrid:'vendor/ng-grid.debug' }, shim:{ angular:{ deps:[ 'jquery'], exports:'angular' }, ngGrid:{ deps:[ 'jquery'] }, bootstrap:{ deps:[ 'jquery'] } } });
第二块就是主要的引用和route信息了:
require([ 'angular', 'app', 'domReady', 'bootstrap', 'services/userService', 'controllers/rootController', 'directives/ngbkFocus', 'controllers/user/listController', 'controllers/user/editController', 'controllers/user/newController', 'ngGrid' // Any individual controller, service, directive or filter file // that you add will need to be pulled in here. ], function (angular, app, domReady) { 'use strict'; app.config(['$routeProvider', function ($routeProvider) { $routeProvider.when('/', { templateUrl:'views/user/list.html',//当跳转到该路径时,将此html嵌入到ng-view属性的dom中 controller:'ListCtrl'//当前templateUrl的controller为ListCtrl }) .when('/edit/:name/:allowance', { templateUrl:'views/user/form.html', controller:'EditCtrl' }) .when('/new', { templateUrl:'views/user/form.html', controller:'NewCtrl' }) .when('/root', { templateUrl:'views/root.html', controller:'RootCtrl' }); } ]); domReady(function () { angular.bootstrap(document, ['MyApp']);//app名为MyApp // The following is required if you want AngularJS Scenario tests to work $('html').addClass('ng-app: MyApp');//给html添加ng-app属性 }); } );
以list的route为示例,我们来讲解controller,先看一张list效果图(此处我们引用了ng-grid插件):
我们来看引入的controllers/user/listController:
define(['controllers/controllers', 'services/userService'], function (controllers) { controllers.controller('ListCtrl', ['$scope', '$http', '$location', 'UserService',//此处数组中的参数依次对应下面回调方法中的参数 function ($scope, $http, $location, UserService) { $scope.mySelections = []; $scope.filterOptions = { filterText:"", useExternalFilter:true }; $scope.totalServerItems = 0; $scope.pagingOptions = { pageSizes:[250, 500, 1000], pageSize:250, currentPage:1 }; $scope.setPagingData = function (data, page, pageSize) { var pagedData = data.slice((page - 1) * pageSize, page * pageSize); $scope.myData = pagedData; $scope.totalServerItems = data.length; if (!$scope.$$phase) { $scope.$apply(); } }; $scope.getPagedDataAsync = function (pageSize, page, searchText) { setTimeout(function () { var data; if (searchText) { var ft = searchText.toLowerCase(); UserService.getServerData(function (largeLoad) { data = largeLoad.filter(function (item) { return JSON.stringify(item).toLowerCase().indexOf(ft) != -1; }); $scope.setPagingData(data, page, pageSize); }); } else { UserService.getServerData(function (largeLoad) { $scope.setPagingData(largeLoad, page, pageSize); }); } }, 100); }; $scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage); $scope.$watch('pagingOptions', function (newVal, oldVal) { if (newVal !== oldVal && newVal.currentPage !== oldVal.currentPage) { $scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage, $scope.filterOptions.filterText); } }, true); $scope.$watch('filterOptions', function (newVal, oldVal) { if (newVal !== oldVal) { $scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage, $scope.filterOptions.filterText); } }, true); $scope.clickme = function (v) { $location.path('/edit/' + v.name + "/" + v.allowance); } $scope.gridOptions = { data:'myData', enablePaging:true, showFooter:true, totalServerItems:'totalServerItems', pagingOptions:$scope.pagingOptions, filterOptions:$scope.filterOptions, selectedItems:$scope.mySelections, showSelectionCheckbox:true, columnDefs:[ { field:"name", pinned:true } , { field:"allowance", cellTemplate:'{{COL_FIELD | currency}}'} , { field:"entity", pinned:true, cellTemplate:'{{$parent.row.entity | myfilter}}' } , { field:"name", width:100, cellTemplate:''} ]/*, multiSelect:false*/ }; }]); });
其中定义了该controller名为ListCtrl,$scope为局部变量,其中定义的数据与方法可在当前Ctrl作用域内生效,我们来看一下生效的list.html是怎么样的
就是这么一句话,定义了grid的div,其余的都交给了controller和ng-grid插件
listController中引用了services/userService,service层用来与后台交互,管理数据:
define(['services/services'], function (services) { services.factory('UserService', ['$http', function ($http) { return { getData:function () { return [ { name:"Moroni", age:50, birthday:"Oct 28, 1970", salary:"60,000" }, { name:"Tiancum", age:43, birthday:"Feb 12, 1985", salary:"70,000" }, { name:"Jacob", age:27, birthday:"Aug 23, 1983", salary:"50,000" }, { name:"Nephi", age:29, birthday:"May 31, 2010", salary:"40,000" }, { name:"Enos", age:34, birthday:"Aug 3, 2008", salary:"30,000" }, { name:"Moroni", age:50, birthday:"Oct 28, 1970", salary:"60,000" }, { name:"Tiancum", age:43, birthday:"Feb 12, 1985", salary:"70,000" }, { name:"Jacob", age:27, birthday:"Aug 23, 1983", salary:"40,000" }, { name:"Nephi", age:29, birthday:"May 31, 2010", salary:"50,000" }, { name:"Enos", age:34, birthday:"Aug 3, 2008", salary:"30,000" }, { name:"Moroni", age:50, birthday:"Oct 28, 1970", salary:"60,000" }, { name:"Tiancum", age:43, birthday:"Feb 12, 1985", salary:"70,000" }, { name:"Jacob", age:27, birthday:"Aug 23, 1983", salary:"40,000" }, { name:"Nephi", age:29, birthday:"May 31, 2010", salary:"50,000" }, { name:"Enos", age:34, birthday:"Aug 3, 2008", salary:"30,000" } ]; }, getOptions:function () { return { data:'myData', enablePinning:true, columnDefs:[ { field:"name", width:120, pinned:true }, { field:"age", width:120 }, { field:"birthday", width:120 }, { field:"salary", width:120 } ] } }, getServerData:function (success) { $http.get('jsonFiles/largeLoad.json').success(success); }, saveData:function (data, success) { $http.post('/users/save', data).success(success); } }; }]); });
其中引用的$http是用来管理ajax的对象,类似的对象还有$location,用来管理url地址,类似的对象还有很多,详细可以参见官方API文档。
至此,一个前端MVC框架基本构建完成,在之后的一篇中我们来着重介绍下还没讲过的filters和directives。