博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
对一个前端使用AngularJS后端使用ASP.NET Web API项目的理解(3)
阅读量:6817 次
发布时间:2019-06-26

本文共 16700 字,大约阅读时间需要 55 分钟。

 

chsakell分享了一个前端使用AngularJS,后端使用ASP.NET Web API的项目。

 

源码: https://github.com/chsakell/spa-webapi-angularjs

文章:http://chsakell.com/2015/08/23/building-single-page-applications-using-web-api-and-angularjs-free-e-book/

 

这里记录下对此项目的理解。分为如下几篇:

 

● 

● 

● 对一个前端使用AngularJS后端使用ASP.NET Web API项目的理解(3)--主页面布局

● 

 

Home/Index.cshtml视图

 

创建一个有关ui的module:spa/modules/common.ui.js

 

(function () {    'use strict';     angular.module('common.ui', ['ui.bootstrap', 'chieffancypants.loadingBar']); })();

 

创建一个有关功能的module:spa/modules/common.ui.js

 

(function () {    'use strict';     angular.module('common.core', ['ngRoute', 'ngCookies', 'base64', 'angularFileUpload', 'angularValidator', 'angucomplete-alt']); })();

 

Home/Index.cshtml视图摘要:

 

            

 

homeCinema是一个主module,依赖common.ui和common.core这2个module,定义在了spa/app.js中,具体如下:

 

//config传入名称为config的函数//run传入名称为run的函数//执行顺序:app.config()→app.run()→directive compile functions if found→app.controller→directive's link funciton if foundangular.module('homeCinema', ['common.core', 'common.ui'])    .config(config)    .run(run);        //为config函数注入参数    config.$inject = ['$routeProvider'];//路由设置,让controller和页面匹配function config($routeProvider) {    $routeProvider        .when("/", {            templateUrl: "scripts/spa/home/index.html",            controller: "indexCtrl"        })        .when("/login", {            templateUrl: "scripts/spa/account/login.html",            controller: "loginCtrl"        })        .when("/register", {            templateUrl: "scripts/spa/account/register.html",            controller: "registerCtrl"        })        .when("/customers", {            templateUrl: "scripts/spa/customers/customers.html",            controller: "customersCtrl"        })        .when("/customers/register", {            templateUrl: "scripts/spa/customers/register.html",            controller: "customersRegCtrl",            //注入到cotroller中的依赖,controller会等到resolve的动作结束后再初始化            resolve: { isAuthenticated: isAuthenticated }        })        .when("/movies", {            templateUrl: "scripts/spa/movies/movies.html",            controller: "moviesCtrl"        })        .when("/movies/add", {            templateUrl: "scripts/spa/movies/add.html",            controller: "movieAddCtrl",            resolve: { isAuthenticated: isAuthenticated }        })        .when("/movies/:id", {            templateUrl: "scripts/spa/movies/details.html",            controller: "movieDetailsCtrl",            resolve: { isAuthenticated: isAuthenticated }        })        .when("/movies/edit/:id", {            templateUrl: "scripts/spa/movies/edit.html",            controller: "movieEditCtrl"        })        .when("/rental", {            templateUrl: "scripts/spa/rental/rental.html",            controller: "rentStatsCtrl"        }).otherwise({ redirectTo: "/" });}    //为resolve的函数注入参数isAuthenticated.$inject = ['membershipService', '$rootScope', '$location'];//resolve执行的函数function isAuthenticated(membershipService, $rootScope, $location) {    if (!membershipService.isUserLoggedIn()) {        $rootScope.previousState = $location.path();        $location.path('/login');    }}//为run函数注入参数run.$inject = ['$rootScope', '$location', '$cookieStore', '$http'];//一些初始化工作function run($rootScope, $location, $cookieStore, $http) {    // handle page refreshes    //rootScope.repository    //rootScope.repository.loggedUser    $rootScope.repository = $cookieStore.get('repository') || {};    if ($rootScope.repository.loggedUser) {        $http.defaults.headers.common['Authorization'] = $rootScope.repository.loggedUser.authdata;    }    $(document).ready(function () {        $(".fancybox").fancybox({            openEffect: 'none',            closeEffect: 'none'        });        $('.fancybox-media').fancybox({            openEffect: 'none',            closeEffect: 'none',            helpers: {                media: {}            }        });        $('[data-toggle=offcanvas]').click(function () {            $('.row-offcanvas').toggleClass('active');        });    });}

 

side-bar

 

 

在界面中的适用方法:<side-bar></side-bar>

 

在common.ui这个module中自定义了一个directive。在spa/layout/sideBar.directive.js

 

(function(app) {    'use strict';     app.directive('sideBar', sideBar);     function sideBar() {        return {            restrict: 'E',            replace: true,            templateUrl: '/scripts/spa/layout/sideBar.html'        }    } })(angular.module('common.ui'));

 

spa/layout/sidebar.html摘要:

 

 

其中,userData.isUserLoggedIn肯定是homeCinema这个module的controller,定义在了spa/home/rootCtrl.js中。

 

(function (app) {    'use strict';    app.controller('rootCtrl', rootCtrl);    rootCtrl.$inject = ['$scope','$location', 'membershipService','$rootScope'];        function rootCtrl($scope, $location, membershipService, $rootScope) {        //userData对象        $scope.userData = {};                //$scope.userData.displayUserInfo方法显示用户        $scope.userData.displayUserInfo = displayUserInfo;                //方法登出        $scope.logout = logout;        //$scope.userData.isUserLoggedIn,布尔类型        //$scope.username        function displayUserInfo() {            $scope.userData.isUserLoggedIn = membershipService.isUserLoggedIn();            if($scope.userData.isUserLoggedIn)            {                //主页面初始化的时候就定义在$rootScope.repository.loggedUser.username了                $scope.username = $rootScope.repository.loggedUser.username;            }        }        function logout() {            membershipService.removeCredentials();            $location.path('#/');            $scope.userData.displayUserInfo();        }        $scope.userData.displayUserInfo();    }})(angular.module('homeCinema'));

 

以上,注入了membershipService这个服务,另外还有一个apiService,notificationService等服务被放在了common.core模块中,都是以factory的方式创建的服务。

 

spa/services/notificationService.js这个服务基于toastr.js管理所有的通知。

 

(function (app) {    'use strict';     //以工厂的方式创建服务    app.factory('notificationService', notificationService);     function notificationService() {         toastr.options = {            "debug": false,            "positionClass": "toast-top-right",            "onclick": null,            "fadeIn": 300,            "fadeOut": 1000,            "timeOut": 3000,            "extendedTimeOut": 1000        };         var service = {            displaySuccess: displaySuccess,            displayError: displayError,            displayWarning: displayWarning,            displayInfo: displayInfo        };         return service;         function displaySuccess(message) {            toastr.success(message);        }         function displayError(error) {            if (Array.isArray(error)) {                error.forEach(function (err) {                    toastr.error(err);                });            } else {                toastr.error(error);            }        }         function displayWarning(message) {            toastr.warning(message);        }         function displayInfo(message) {            toastr.info(message);        }     } })(angular.module('common.core'));

 

spa/services/apiService.js服务用来管理GET和POST请求。

 

(function (app) {    'use strict';     app.factory('apiService', apiService);     apiService.$inject = ['$http', '$location', 'notificationService','$rootScope'];     function apiService($http, $location, notificationService, $rootScope) {        var service = {            get: get,            post: post        };         function get(url, config, success, failure) {            return $http.get(url, config)                    .then(function (result) {                        success(result);                    }, function (error) {                        if (error.status == '401') {                            notificationService.displayError('Authentication required.');                            $rootScope.previousState = $location.path();                            $location.path('/login');                        }                        else if (failure != null) {                            failure(error);                        }                    });        }         function post(url, data, success, failure) {            return $http.post(url, data)                    .then(function (result) {                        success(result);                    }, function (error) {                        if (error.status == '401') {                            notificationService.displayError('Authentication required.');                            $rootScope.previousState = $location.path();                            $location.path('/login');                        }                        else if (failure != null) {                            failure(error);                        }                    });        }         return service;    } })(angular.module('common.core'));

 

top bar

 

自定义的top bar放在了common.ui模块中,spa/layout/topBar.directive.js

 

(function(app) {    'use strict';     app.directive('topBar', topBar);     function topBar() {        return {            restrict: 'E',            replace: true,            templateUrl: '/scripts/spa/layout/topBar.html'        }    } })(angular.module('common.ui'));

 

spa/layout/topBar.html摘要:

 

 

以上,username, userData.isUserLoggedIn都是homeCinema这个模块中rootCtrl控制器的变量。

 

Latest Movies 

 

 

 

首先要写一个继承ApiController的类,用来处理登录异常。

 

namespace HomeCinema.Web.Infrastructure.Core{    public class ApiControllerBase : ApiController    {        protected readonly IEntityBaseRepository
_errorsRepository; protected readonly IUnitOfWork _unitOfWork; public ApiControllerBase(IEntityBaseRepository
errorsRepository, IUnitOfWork unitOfWork) { _errorsRepository = errorsRepository; _unitOfWork = unitOfWork; } public ApiControllerBase(IDataRepositoryFactory dataRepositoryFactory, IEntityBaseRepository
errorsRepository, IUnitOfWork unitOfWork) { _errorsRepository = errorsRepository; _unitOfWork = unitOfWork; } protected HttpResponseMessage CreateHttpResponse(HttpRequestMessage request, Func
function) { HttpResponseMessage response = null; try { response = function.Invoke(); } catch (DbUpdateException ex) { LogError(ex); response = request.CreateResponse(HttpStatusCode.BadRequest, ex.InnerException.Message); } catch (Exception ex) { LogError(ex); response = request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message); } return response; } //把错误报错到数据库中去 private void LogError(Exception ex) { try { Error _error = new Error() { Message = ex.Message, StackTrace = ex.StackTrace, DateCreated = DateTime.Now }; _errorsRepository.Add(_error); _unitOfWork.Commit(); } catch { } } }}

 

以上, ApiControllerBase定义了一个重要的方法CreateHttpResponse,不但可以处理请求响应,还可以进行异常处理,把异常记录到数据库。

 

接着定义MoviesController,继承ApiControllerBase基类。

 

[Authorize(Roles = "Admin")][RoutePrefix("api/movies")]public class MoviesController : ApiControllerBase{    private readonly IEntityBaseRepository
_moviesRepository; public MoviesController(IEntityBaseRepository
moviesRepository,IEntityBaseRepository
_errorsRepository, IUnitOfWork _unitOfWork) : base(_errorsRepository, _unitOfWork) { _moviesRepository = moviesRepository; } ...}

 

首页展示6个Moview,针对此写一个aciton方法。

 

[AllowAnonymous][Route("latest")]public HttpResponseMessage Get(HttpRequestMessage request){    return CreateHttpResponse(request, () =>    {        HttpResponseMessage response = null;                //获取6个        var movies = _moviesRepository.GetAll().OrderByDescending(m => m.ReleaseDate).Take(6).ToList();        //转换成视图模型        IEnumerable
moviesVM = Mapper.Map
, IEnumerable
>(movies); //创建响应 response = request.CreateResponse
>(HttpStatusCode.OK, moviesVM); return response; });}

 

界面如何展示出来呢?在spa/app.js中的路由已经有了定义。

 

$routeProvider    .when("/", {        templateUrl: "scripts/spa/home/index.html",        controller: "indexCtrl"    })    ...

 

也就是在根地址下,使用indexCtrl这个controller,路由到scripts/spa/home/index.html这里。

 

scripts/spa/home/index.html

 

{
{movie.Title}}
{
{movie.Description | limitTo: 70}}...
Trailer

 

以上,其实定义了2个directive,一个是available-movie,用来显示Movie的状态,可能是Available,也可能是Not Available;另一个是component-rating,用来显示星级,基于 raty.js文件开发。

 

先来看控制器:spa/home/indexCtrl.js

 

(function (app) {    'use strict';    app.controller('indexCtrl', indexCtrl);    indexCtrl.$inject = ['$scope','apiService', 'notificationService'];    function indexCtrl($scope, apiService, notificationService) {        $scope.loadingMovies = true;        $scope.latestMovies = [];        $scope.loadData = loadData;        function loadData() {            apiService.get('/api/movies/latest', null,                        moviesLoadCompleted,                        moviesLoadFailed);        }        function moviesLoadCompleted(result) {            $scope.latestMovies = result.data;            $scope.loadingMovies = false;        }        function moviesLoadFailed(response) {            notificationService.displayError(response.data);        }        loadData();    }})(angular.module('homeCinema'));

 

再来看是否显示Available Movie的这个自定义directive,在页面中是这样使用的:

 

 

实际是在spa/directives/availableMovie.directive.js中定义的。

 

(function (app) {    'use strict';     //注意这里的惯例,这里的availableMovie相当于界面上的available-movie    app.directive('availableMovie', availableMovie);     function availableMovie() {        return {            restrict: 'E',            templateUrl: "/Scripts/spa/directives/availableMovie.html",            link: function ($scope, $element, $attrs) {                           //getAvailbleClass供html中调用               //attrs表示属性,注意这里的惯例:isAvailable相当于界面上的is-available                $scope.getAvailableClass = function () {                    if ($attrs.isAvailable === 'true')                        return 'label label-success'                    else                        return 'label label-danger'                };                                //getAvailability根据属性的布尔值返回不同的字符串                $scope.getAvailability = function () {                    if ($attrs.isAvailable === 'true')                        return 'Available!'                    else                        return 'Not Available'                };            }        }    } })(angular.module('common.ui'));

 

最终,在spa/directives/availableMovie.html中:

 

 

在Movie的显示中,还定义了一个directive,用来显示星级,在界面中按如下:

 

 

在spa/directives/componentRating.directive.js中:

 

(function(app) {    'use strict';     app.directive('componentRating', componentRating);     function componentRating() {        return {            restrict: 'A', //A说明directive以属性的方式            link: function ($scope, $element, $attrs) {                $element.raty({                    score: $attrs.componentRating, //componentRating相当于界面中的component-rating,接收component-rating属性值                    halfShow: false,                    readOnly: $scope.isReadOnly,//表明是只读的星级                    noRatedMsg: "Not rated yet!",                    starHalf: "../Content/images/raty/star-half.png",                    starOff: "../Content/images/raty/star-off.png",                    starOn: "../Content/images/raty/star-on.png",                    hints: ["Poor", "Average", "Good", "Very Good", "Excellent"],                    click: function (score, event) {                        //Set the model value                        $scope.movie.Rating = score;                        $scope.$apply();                    }                });            }        }    } })(angular.module('common.ui'));

 

点击首页Movie缩略图,弹出窗口

 

 

 

关于缩略图的html部分就在scripts/spa/home/index.html中,具体为:

 

 

根据类名fancybox,使用jquery语法调用jquery.fancybox.js的语法,具体调用是在app.js中调用的:

 

function run($rootScope, $location, $cookieStore, $http) {    ...    $(document).ready(function () {        $(".fancybox").fancybox({            openEffect: 'none',            closeEffect: 'none'        });        ...    });}

 

待续~

 

转载地址:http://taszl.baihongyu.com/

你可能感兴趣的文章