转载地址:http://www.imooc.com/wenda/detail/236998
【angularjs】【学习心得】路由
AngularJS自带有路由模块ngRoute,但是有经验的老师都推荐我们使用功能更完善更强大的ui-router来做路由。那到底什么是路由呢?我自己的理解是:路由可以看作一个总控制器,它会根据页面的不同状态来填充页面的内容,这就是路由的主要用处。前端路由能极大地减少对服务器资源的请求数量,因此在前端做路由显得尤为重要。
-----------------------------------------------------------------
由于路由是控制整个应用的显示状态的,所以我们要让路由模块第一时间接管整个应用。
使用ui-router很简单,下载它的包,在index.html中引入js文件,然后在模块的依赖中引入即可。
1
|
var routerApp = angular.module('routerApp', ['ui.router']);
|
ui-router的本质其实是向我们预留的部分填充模板,它会在页面中去寻找ui-view这条指令,然后根据当前页面状态把对应的模板填充到ui-view所在的区块中。
比如一个典型的页面是nav+footer固定不变,中间主要部分作为内容显示区域时常发生变化。对应的页面结构就是
1
2
3
4
5
|
<
body
>
<
nav
>This is nav</
nav
>
<
div
ui-view></
div
>
<
footer
>This is footer</
footer
>
</
body
>
|
然后我们的路由根据页面的状态,选择不同的html模板填充到ui-view的这个div里面。
在js中具体怎么使用ui-router呢
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
routerApp.config(
function
($stateProvider,$urlRouterProvider) {
$urlRouterProvider.otherwise(
'/index'
);
$stateProvider
.state(
'index'
,{
url :
'/index'
,
templateUrl :
'tpls/index.html'
})
.state(
'list'
,{
url :
'/list'
,
templateUrl :
'tpls/list.html'
})
.state(
'detail'
,{
url :
'/detail'
,
templateUrl :
'tpls/detail.html'
});
});
|
以上就是angular路由最基本的模板。但是要注意一个问题,state的第一个参数并不是匹配规则,而是叫状态名,也就是说这个参数事实上可以是任意的,它用来给当前状态增加一个名字,触发路由规则还是看url的内容。比如这样
1
2
3
4
|
.state(
'home'
,{
url :
'/index'
,
templateUrl :
'tpls/index.html'
})
|
我们必须要地址栏输入www.xx.com/index,才会加载对应的模板而不是输入home,它仅仅是一个名字而已。
但是说,这个名字也是有它的作用的。看一个例子就明白了。
index.html
1
2
3
|
<
nav
>This is the nav</
nav
>
<
div
ui-view></
div
>
<
footer
>This is the footer</
footer
>
|
list.html
1
2
3
4
|
<
div
>
<
p
>this is the list page</
p
>
<
div
ui-view></
div
>
</
div
>
|
list-main.html
1
2
3
|
<
div
>
<
p
>This is list-main Page</
p
>
</
div
>
|
如果我们的路由这么写
1
2
3
4
5
6
7
8
|
.state(
'aaa'
,{
url :
'/list'
,
templateUrl :
'tpls/list.html'
})
.state(
'bbb'
,{
url :
'/list/main'
,
templateUrl :
'tpls/list-main.html'
})
|
那当我们在浏览器中输入www.xx.com/list/main的时候,页面结果为
如果我们的路由这么写
1
2
3
4
5
6
7
8
|
.state(
'aaa'
,{
url :
'/list'
,
templateUrl :
'tpls/list.html'
})
.state(
'aaa.bbb'
,{
url :
'/main'
,
templateUrl :
'tpls/list-main.html'
})
|
那当我么输入www.xx.com/list/main的时候,页面结果为
也就是说state的的第一个参数为页面定义了一个名字,这个页面的模板只能放到它的父级中的ui-view去。如果没有.这种写法,那么默认的父级是index.html,所以第一种写法bbb模板的父级是index.html,所以输入/list/main时会把对应模板加载到index.html中的ui-view里面。而第二种写法aaa.bbb,这意思是说bbb的父级是aaa,所以bbb的模板要放到aaa模板(也就是list.html)中的ui-view中。这时的url是在aaa页面的url后的url。
当然还有页面的嵌套,这部分其实大漠老师在视频中已经讲得很清楚了。
这是路由需要注意的一个作用域问题。
-----------------------------------------------------------------------
当然路由还有很多其他问题,因为路由是angularjs中很重要的一部分,但基本的用法就是上面所讲的了,如果把所有静态页面都写好了,那么用以上的内容就已经能做出一个精美的web了。现在学习了路由的基本用法,其它问题的话我们碰到一个再解决一个吧。
今天还是来说一下angular中的路由模块。我们实际项目中,各个页面的切换是经常会与Auth相关的。比如我网站的后台,是需要登录过的用户才能进去,那么我们用angularJS做前端路由的时候应该怎么完成这个功能呢
------------------------------------------------------------------------
我们还是先设想一个最简单的场景吧。我们的应用有两个页面,登录页面后内容页面,要求是必须要验证登录成功后才能进入内容页面,下面我们一起来实现一下这个例子吧。当然我觉得我的方法可能会比较Low,但是学习阶段我们能先把功能做出来比什么都重要。
首先用bootstrap来写一个简单的登录页面吧。具体bootstrap代码我就不说了,我们关注的是angular在这里面如何用起来
<
div
class
=
"col-md-offset-3 col-md-4"
>
<
form
class
=
"form"
role
=
"form"
name
=
"loginForm"
ng-submit
=
"loginCheck()"
>
<
div
class
=
"form-group"
>
<
label
class
=
"control-label"
>用户名</
label
>
<
input
type
=
"text"
class
=
"form-control"
required
placeholder
=
"请输入管理员账号"
ng-model
=
"admin.username"
>
</
div
>
<
div
class
=
"form-group"
>
<
label
class
=
"control-label"
>密码</
label
>
<
input
type
=
"password"
class
=
"form-control"
ng-model
=
"admin.pwd"
required
placeholder
=
"请输入密码"
>
</
div
>
<
div
ng-show
=
"showError"
class
=
"alert alert-danger alert-dismissible"
role
=
"alert"
>
<
button
ng-click
=
"showError=false"
type
=
"button"
class
=
"close"
data-dismiss
=
"alert"
><
span
aria-hidden
=
"true"
>×</
span
><
span
class
=
"sr-only"
>关闭</
span
></
button
>
用户名或密码错误!!你还有一次机会
</
div
>
<
input
type
=
"submit"
class
=
"btn btn-primary btn-lg"
value
=
"登录"
ng-disabled
=
"loginForm.$invalid"
>
</
form
>
</
div
>
效果如下
当然我之前还有一些css的布局,粘代码过去可能会出错哦,至少得在最外层加一个div class="row"
还可以看见,我给登录按钮加了个ng-disabled,当表单没有通过验证的时候是不能点登录的。
然后我加了一个提示的tips,用到了ng-show,在controller里会有一个showError的属性来控制它的显示,当验证账号密码错误时showError为true。
当我们验证出错的时候
接着我们来看一下路由
var
myApp = angular.module(
'myApp'
, [
'ui.router'
,
'myModule'
]);
myApp.run(
function
($rootScope, $state, $stateParams){
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
$rootScope.$state.isLogin =
false
;
});
myApp.config(
function
($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise(
'/login'
);
$stateProvider
.state(
'login'
,{
url :
'/login'
,
templateUrl :
'tpls/login.html'
,
controller :
'LoginController'
})
.state(
'index'
,{
url :
'/index'
,
templateUrl :
'tpls/index.html'
,
controllerProvider :
function
($rootScope){
if
($rootScope.$state.isLogin ==
false
){
$rootScope.$state.go(
'login'
);
}
return
function
(){};
}
});
}
因为在整个页面中我们都会用到登录状态,所以我把登录状态绑定到rootscope中,isLogin刚开始是false表示未登录。
接着看路由里面,login这个很简单,主要看index页面。
关键的一步就是index的controller,在这里我选择用controllerProvider的方式来生成controller,可以看到我们最后实际上是返回的一个空的function,但是在返回空controller之前(index页面还没有解析),我可以做一些事情,那就是验证权限啦!
如果$rootScope.$state.isLogin为false也就是还没有登录,那就直接跳转到登录页面。跳转用到了$state里面的go方法,go中的变量就是我们每个页面的状态名,也就是state的第一个参数。我是go('login'),它就跳转到state的第一个参数是login的那个页面去了,也就是登录页面。换句话说,如果我们登录提交后验证没有成功,当我们在地址栏输入/index的时候会跳到登录页面的哦。
那么再来看看我们的验证模块。
myModule
.controller(
'LoginController'
,
function
($scope,$rootScope,$http){
$scope.showError =
false
;
$scope.loginCheck =
function
(){
var
username = $scope.admin.username;
var
pwd = $scope.admin.pwd;
var
loginSuccess =
false
;
http.get(
'/acm-admin/data/user.json'
)
.success(
function
(response){
for
(
var
i=0; i<response.length; i++){
if
(response[i].username == username && response[i].pwd == pwd){
$rootScope.$state.isLogin =
true
;
loginSuccess =
true
;
$rootScope.$state.go(
'index'
);
}
}
if
(!loginSuccess){
$scope.showError =
true
;
}
});
}
})
初始化我们给showError一个值为false,不要显示错误提示框。然后来看看验证登录的这个方法。首先获取到用户输入的用户名和密码,设置登录成功的状态的false。然后通过$http.get,到指定的地方去取一个json文件,很显然这是个假数据,我们把预设的用户名和密码存放到这个json文件中。取出预设的用户名和密码之后就和用户输入的来进行对比,相信这都很简单,大家肯定能看明白。如果用户名和密码都对上了,那么就把登录状态设置为true,登录成功也设置为true。然后用$state的go方法跳转到Index页面。
如果登录信息验证失败,那么就把showError赋为true,页面上就会显示提示信息咯。
----------------------------------------------------------------------------
怎么样,很简单吧。当然还有其它实现这一功能的方法,而且我上述的方法很可能还有诸多安全隐患,而且模块的分工也是不明确的,淡然实际部署不推荐这么写了。我们可以优化改进的地方很多,比如验证的模块是不是应该独立出去呢,或者用户有没有什么办法能轻易绕过这个登录保护呢?这就留待小伙伴们自己探究了……继续学习angular去了,大家晚安!!
今天来说一点angularjs中看起来很简单但是实践起来又有不少问题的ng-class吧
------------------------------------------------------------------------
不过还是先说一下和angular无关的一个js的小坑,不知道大家遇到过没有,就是json格式的文件。之前一直都在js中定义json数组,或者从php后台用json_encode编码后直接返回,从没有自己写过json格式的数据,今天就遇到点麻烦。
在js中定义json数组这么写就好了:
var
arr = [{a:1,b:2},{a:5,b:10}];
但是在json文件中千万不能这么写
[{a:1,b:2},{a:5,b:10}]
必须给key加上引号才行哦。今天调试好久才发现这个问题,也算个教训吧……
------------------------------------------------------------------------
然后说ng-class这个东西,老师讲的时候一句带过,用的时候还是需要费些时间的。
我总结了一下通过ng-class给元素动态地加class有4种做法,下面一个一个来说。
先看一下官方的说明
不知道大家能不能看清,给个链接吧https://code.angularjs.org/1.3.0-beta.11/docs/api/ng/directive/ngClass
大致翻译一下就是说ng-class指令有3中操作方式,通过ng-class等于的表达式计算出来的值的类型来决定是哪种
方式1: 当它的值为一个字符串时,它就会把用空格分开的字符串加到class中
方式2: 当值为一个数组时,它每个字符串元素都会被加到class中
方式3: 当值为一个对象时(key=>value),把value为true的key加到class中
首先是最不推荐的
<
div
ng-class
=
"{{myclass}}"
></
div
>
....
<
script
>
function someController($scope){
$scope.myclass = "xxx";
}
</
script
>
上面这种方法效果上来说没问题,但是完全没必要用ng-class,普通的也能实现这个效果。而且在controller中控制样式总感觉有点儿别扭……
然后说另一种用法
<
div
ng-class
=
"{true :'red', false :'green'}[someVariable]"
></
div
>
这种用法就是说variable为true时,就给元素加上red这个class,如果variable为false就加上green这个class,这个在逻辑比较简单的时候还是蛮好用的。
下一种适合需要添加多个类的时候,也就是ng-class的值为一个对象
<
p
ng-class
=
"{strike: deleted, bold: important, red: error}"
>Map Syntax Example</
p
>
<
input
type
=
"checkbox"
ng-model
=
"deleted"
> deleted (apply "strike" class)
<
br
>
<
input
type
=
"checkbox"
ng-model
=
"important"
> important (apply "bold" class)
<
br
>
<
input
type
=
"checkbox"
ng-model
=
"error"
> error (apply "red" class)
上面代码ng-class就是一个对象,我们把deleted,important,error进行双向数据绑定,当我们在checkbox勾选时,它们变为true,然后对应的key就被加到class中,效果图
还有一种就是数组类型的,数组都每个字符串元素都会被加到class中
<
p
ng-class
=
"[style1, style2, style3]"
>Using Array Syntax</
p
>
<
input
ng-model
=
"style1"
placeholder
=
"Type: bold, strike or red"
>
<
br
>
<
input
ng-model
=
"style2"
placeholder
=
"Type: bold, strike or red"
>
<
br
>
<
input
ng-model
=
"style3"
placeholder
=
"Type: bold, strike or red"
>
当我们在样式中定义好bold,strike,red;类的样式后,我们输入这些字符串就会出现效果
大概就是这几种用法,我推荐大家用对象来添加对象,那样最好控制逻辑也清楚。
------------------------------------------------------------------------------
今天就分享这些吧,希望对大家有些帮助
总结得很好,谢谢分享出来。哈哈
其实路由的功能是比较复杂的,我们实际应用中页面的状态也是非常多的,上面简单的路由是肯定不能满足我们的需求的,所以我们必须要更深入地了解下路由以及它更有用的一些用法。
--------------------------------------------------------------------
首先来说一下templateUrl属性,上面说了它的值是对应模板的地址,比如
...
templateUrl :
'tpls/index.html'
,
...
但事实上我们也可以用一个函数作为值,但是这个函数必须返回模板的地址,比如
...
templateUrl :
function
(){
return
'tpls/index.html'
;
},
...
这两段代码的效果其实是一模一样的,很容易理解。但是我们在实际应用的时候会经常有这样的需求:在url里拼接get参数,然后后台利用get参数去读数据库然后返回相应的内容。这时候用第一种方法实现起来可能就不那么容易(但是也是能实现的),而用第二种方法就很容易了。
那么在这之前需要介绍一个很有用的东西叫做$stateParams,看名字也很容易理解,状态参量。这个东西就存储了页面状态的有关信息,我们通过一个例子来看看这到底是神马东东。
这是我们的list.html页面
<
div
>
<
p
>This is the list page</
p
>
<
a
ui-sref
=
"detail({articleId:'111'})"
>文章详情</
a
>
</
div
>
这是detail.html页面
<
div
>
<
p
>This is the detail page</
p
>
</
div
>
这是路有部分了,注意看哟
.state(
'list'
,{
url :
'/list'
,
templateUrl :
'tpls/list.html'
})
.state(
'detail'
,{
url :
'/detail/{articleId}'
,
templateUrl :
function
($stateParams){
console.log($stateParams);
return
'tpls/detail.html'
;
}
})
先来看看效果,当我们进入list页面时是下面这个样子
我们点击文章详情这个链接后,页面变成了detail,并且下面打印出了$stateParams
这里我们发现它是一个对象,并且包含了我们所传递的参数。小伙伴们看看我上面是怎么在url里传递参数的哦,就是加一个括号,里面放一个我们要传递的对象即可。传递对象参数的时候需要注意,路由中url后面跟了多少参数你就只能传递那么多参数,比如
路由中这么写
.state(
'detail'
,{
url :
'/detail/{articleId}/{else}'
,
templateUrl :
function
($stateParams){
console.log($stateParams);
return
'tpls/detail.html'
;
}
})
html页面中这么写
<
div
>
<
p
>This is the list page</
p
>
<
a
ui-sref
=
"detail({articleId:'111',else:'imooc'})"
>文章详情</
a
>
</
div
>
那我们输出的效果就是这样的
怎么能用它呢?小伙伴们记得路由中的另一个参数controller,它为模板指定了一个controller,事实上我们可以把$stateParams传入controller中,看个例子吧
首先我们在index.html引入controller.js
list页面和上面没有变化
<
div
>
<
p
>This is the list page</
p
>
<
a
ui-sref
=
"detail({articleId:'111',else:'imooc'})"
>文章详情</
a
>
</
div
>
路由中我们还是这么写,但是给detail页面指定了一个controller
然后我们在controller.js里面
//注意,我们需要在路由最开头的依赖中加上myModule哦
var
myModule = angular.module(
'myModule'
,[]);
myModule
.controller(
'DetailController'
,
function
($scope,$stateParams){
console.log($stateParams);
});
当我们在list页面中点击链接时,进入detail页面,效果如下
我们在controller中也能打印出传递的参数,这就很有意思了。我们可以在controller中根据传递过来的参数和后台服务器进行通信然后把返回的结果绑定在scope上,页面是不是就可以显示出来了呢,比如这样
myModule.controller(
'DetailController'
,
function
($scope,$stateParams){
console.log($stateParams);
$http.post(
'API_URL'
,{params:$stateParams})
.success(
function
(data,status,headers,config){
$scope.content = data;
});
})
我们通过http服务把$stateParams发送到后台,然后把返回的数据绑定在$scope.content上,detail.html中的{{content}}是不是就可以跟着变化了呢?
------------------------------------------------------------------------
今天就暂时写这么多,以上是最近实践路由的一些经验,但是路由的用法还可以更灵活,这需要我们掌握更多这方面的内容。
PS:今天有门课期末前两天不得不预习了整本书,缺了两天,但是我觉得应该反思,养成一个好习惯不容易,不应该为自己找各种理由,而且目测一般都挂科了……时间虽然紧,但挤挤肯定还是有的!