AngularJS 1.4.6 实践 一 —— 单页应用登录验证
一、多视图路由及路由嵌套
1、多视图路由
目的:将项目主页视图与登录页面视图分离
例如:
当访问 /sigin 时将登录页面加载到 ui-view 之中;
当访问 / 时将项目页面加载到 ui-view 之中;
实现一个 ui-view 根据路由的不同加载不同页面。
关键代码
index.html
<div ui-view="" ></div>
app.tpl.html
<div class=app>
<header></header>
<div class="container-fluid-sdx">
<div class="left-menu"></div>
<div class="container content" ui-view="container"></div>
</div>
</div>
routers.js
$stateProvider
.state('sigin', {
url: 'sigin',
templateUrl: config.tpl('sigin/sigin'),
controllerUrl: 'ctrl/sigin/sigin',
controller: 'sigin-sigin'
})
.state('app', {
url: '/',
views: {
"@": {
templateUrl: config.tpl('app/app')
},
"container@app": { // 加载content中的container视图
templateUrl: config.tpl('search/list'),
controllerUrl: 'ctrl/search/list',
controller: 'search-list'
}
},
});
难点:本项目采用的是经典的后台管理系统布局,包括不变的头部header,左侧菜单栏Side Bar 和根据 菜单栏动态变化的内容 container。因此需要采用路由嵌套来实现动态改变一个页面内部的某一部分。
2、路由嵌套
目的:根据左侧的菜单栏动态改变右侧内容模块
例如:
当访问 /search 时将search页面加载到 ui-view = “container” 之中;
当访问 /test 时将test 页面加载到 ui-view = “container” 之中;
关键代码
routers.js
.state('app', {
url: '/',
views: {
"@": { // 将整体布局 app.tpl.html 加载到 index.html 的 ui-view 之中
templateUrl: config.tpl('app/app')
},
"container@app": { // search视图加载到app.tpl.html 中的 ui-view = "content" 之中
templateUrl: config.tpl('search/list'),
controllerUrl: 'ctrl/search/list',
controller: 'search-list'
}
},
})
.state('app.search', {
url: 'search',
views: {
"container@app": { // search视图加载到app.tpl.html 中的 ui-view = "content" 之中
templateUrl: config.tpl('search/list'),
controllerUrl: 'ctrl/search/list',
controller: 'search-list'
}
}
})
.state('app.test', {
url: 'test',
views: {
"container@app": { // test视图加载到app.tpl.html 中的 ui-view = "content" 之中
templateUrl: config.tpl('test/index'),
controllerUrl: 'ctrl/test/index',
controller: 'test-index'
}
}
})
二、路由检测登录状态
1、添加路由改变监听事件
关键代码
routers.js
// 路由改变监听事件
$rootScope.$on('$stateChangeStart', function (event, toState) {
if (toState.name === 'sigin') {
return;
}
if (angular.isUndefined($localStorage.get('user'))) {
event.preventDefault(); // 取消默认跳转行为
$state.go('sigin'); // 跳转到登录界面
}
});
2、非常重要,解决未知路径无限循环
关键代码
routers.js
$urlRouterProvider.otherwise(function ($injector) {
var $state = $injector.get('$state');
$state.go('app.search');
});
三、整体代码
1、index.html
<!DOCTYPE html>
<html>
<head>
<title>语义搜索-管理系统</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<link href="/css/base.css" rel="stylesheet" type="text/css">
<link href="/css/main.css" rel="stylesheet" type="text/css">
<link href="/css/login.css" rel="stylesheet" type="text/css">
<link href="//cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.1/semantic.min.css" rel="stylesheet">
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon"/>
<base href="/">
</head>
<body ng-cloak="">
<div ui-view="" class="app"></div>
<script data-main="./js/main.js?v=20180919"
src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.2.0/require.min.js"></script>
</body>
</html>
2、sigin.tpl.html
<div class="login-view">
<div class="login-wrapper">
<div class="login-box">
<div class="login-title">
<div class="company-name">
<img src="/imgs/logo.png">
<span>语义搜索</span>
</div>
<div class="systme-name">
<span>Snack Petty Vendor</span>
</div>
</div>
<div class="login-form">
<div class="form-group form-input">
<input type="text" class="normal-input" id="user-name" ng-model="username" placeholder="用户名">
<div class="common-error-tips">
<div></div>
</div>
</div>
<div class="form-group form-input">
<input type="password" class="normal-input" id="user-pwd" ng-model="password" placeholder="密码">
<div class="common-error-tips">
<div></div>
</div>
</div>
<div class="form-group form-btn">
<input type="button" class="normal-btn" ng-click="sigin()" value="登录">
</div>
<div class="form-group form-forget">
<label class="error-tips">登录失败</label>
</div>
</div>
</div>
</div>
</div>
3、app.tpl.html
<div class="app">
<header init-navbar>
<div class="h-banner">
<ul class="tools">
<li><a href="javascript:;" ng-click="logout();">退出登陆</a></li>
</ul>
</div>
</header>
<div class="container-fluid-sdx">
<div class="left-menu">
<ul class="menu-ul">
<li ng-repeat="v in active.nav.list">
<a class="menu-ul-a" href="javascript:;" ng-click="toggle=!toggle">
{
{v.name}} <span class="arrow"></span>
</a>
<ul class="menu-ul-ul">
<li ng-class="{active:active.sub_navid==l.id}" data-id="{
{l.id}}"
ng-repeat="l in v.list" ng-show="l.hasPermission && toggle">
<a class="menu-ul-second" ui-sref="{
{l.href}}">
{
{l.text}}
</a>
</li>
</ul>
</li>
</ul>
</div>
<div class="container content" ui-view="container"></div>
</div>
</div>
4、routers.js
define(['oa', 'config', 'srv/local-storage'], function (pms, config) {
pms.run(['$state', '$stateParams', '$rootScope', '$localStorage', function ($state, $stateParams, $rootScope, $localStorage) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
$rootScope.active = {};
// 路由改变监听事件
$rootScope.$on('$stateChangeStart', function (event, toState) {
if (toState.name === 'sigin') {
return;
}
if (angular.isUndefined($localStorage.get('user'))) {
event.preventDefault(); // 取消默认跳转行为
$state.go('sigin'); // 跳转到登录界面
}
});
}]);
pms.config(['$stateProvider', '$urlRouterProvider', '$locationProvider', function ($stateProvider, $urlRouterProvider, $locationProvider) {
// 非常重要,解决无限循环
$urlRouterProvider.otherwise(function ($injector) {
var $state = $injector.get('$state');
$state.go('app.search');
});
$stateProvider
.state('sigin', {
url: 'sigin',
templateUrl: config.tpl('sigin/sigin'),
controllerUrl: 'ctrl/sigin/sigin',
controller: 'sigin-sigin'
})
.state('app', {
url: '/',
views: {
"@": {
templateUrl: config.tpl('app/app')
},
"container@app": { // 加载content中的container视图
templateUrl: config.tpl('search/list'),
controllerUrl: 'ctrl/search/list',
controller: 'search-list'
}
},
})
.state('app.search', {
url: 'search',
views: {
"container@app": { // app里name为container的view加载模版
templateUrl: config.tpl('search/list'),
controllerUrl: 'ctrl/search/list',
controller: 'search-list'
}
}
})
.state('app.test', {
url: 'test',
views: {
"container@app": {//app里name为container的view加载模版
templateUrl: config.tpl('test/index'),
controllerUrl: 'ctrl/test/index',
controller: 'test-index'
}
}
})
;
$locationProvider.html5Mode(true).hashPrefix('!');
}]);
});
5、local-storage.js
define(['oa'], function (pms) {
pms.service('$localStorage', ['$window', function ($window) {
return {
//读取单个属性
set: function (key, value) {
$window.localStorage[key] = value;
},
//存储对象,以JSON格式存储
get: function (key, defaultValue) {
return $window.localStorage[key] || defaultValue;
},
//读取对象
setObject: function (key, value) {
$window.localStorage[key] = JSON.stringify(value);//将对象以字符串保存
},
//获取字符串并解析成对象
getObject: function (key) {
return JSON.parse($window.localStorage[key] || '{}');
},
deleteLocalStorage: function (key) {
return $window.localStorage.removeItem(key);
}
}
}])
});
7、sigin.js
define(['oa', 'config'], function (oa) {
oa.controller('sigin-sigin', ['$rootScope', '$state', '$scope', '$localStorage', function ($rootScope, $state, $scope, $localStorage) {
$scope.sigin = function () {
if (!$scope.username) {
alert('请输入用户名');
}
if(!$scope.passwod){
alert('请输入密码');
}
$localStorage.set('user', {
username: $userName.val()
});
$state.go('search');
};
}]);
});