【angularjs】【學習心得】路由

轉載地址: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的時候,頁面結果爲

      5472d6ee0001fc2803350157.jpg                                                

    如果我們的路由這麼寫

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的時候,頁面結果爲

    5472d6fd0001ebcf03690177.jpg

    也就是說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了。現在學習了路由的基本用法,其它問題的話我們碰到一個再解決一個吧。


 


2014-11-24 2082 瀏覽7 回答

    今天還是來說一下angular中的路由模塊。我們實際項目中,各個頁面的切換是經常會與Auth相關的。比如我網站的後臺,是需要登錄過的用戶才能進去,那麼我們用angularJS做前端路由的時候應該怎麼完成這個功能呢

------------------------------------------------------------------------

    我們還是先設想一個最簡單的場景吧。我們的應用有兩個頁面,登錄頁面後內容頁面,要求是必須要驗證登錄成功後才能進入內容頁面,下面我們一起來實現一下這個例子吧。當然我覺得我的方法可能會比較Low,但是學習階段我們能先把功能做出來比什麼都重要。

    首先用bootstrap來寫一個簡單的登錄頁面吧。具體bootstrap代碼我就不說了,我們關注的是angular在這裏面如何用起來

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<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">&times;</span><span class="sr-only">關閉</span></button>
              用戶名或密碼錯誤!!你還有一次機會
        </div>
 
        <input type="submit" class="btn btn-primary btn-lg" value="登錄" ng-disabled="loginForm.$invalid">
    </form>      
</div>

    效果如下

        54788faf000197b405000227.jpg

    當然我之前還有一些css的佈局,粘代碼過去可能會出錯哦,至少得在最外層加一個div class="row"

    還可以看見,我給登錄按鈕加了個ng-disabled,當表單沒有通過驗證的時候是不能點登錄的。

    然後我加了一個提示的tips,用到了ng-show,在controller裏會有一個showError的屬性來控制它的顯示,當驗證賬號密碼錯誤時showError爲true。

    當我們驗證出錯的時候

        547892c90001675104860298.jpg

    

    接着我們來看一下路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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的時候會跳到登錄頁面的哦。

    那麼再來看看我們的驗證模塊。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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去了,大家晚安!!

2014-11-28
回覆 2

    今天來說一點angularjs中看起來很簡單但是實踐起來又有不少問題的ng-class吧

------------------------------------------------------------------------

    不過還是先說一下和angular無關的一個js的小坑,不知道大家遇到過沒有,就是json格式的文件。之前一直都在js中定義json數組,或者從php後臺用json_encode編碼後直接返回,從沒有自己寫過json格式的數據,今天就遇到點麻煩。

    在js中定義json數組這麼寫就好了:

1
    var arr = [{a:1,b:2},{a:5,b:10}];

    但是在json文件中千萬不能這麼寫

1
    [{a:1,b:2},{a:5,b:10}]

    必須給key加上引號才行哦。今天調試好久才發現這個問題,也算個教訓吧……

------------------------------------------------------------------------

    然後說ng-class這個東西,老師講的時候一句帶過,用的時候還是需要費些時間的。

    我總結了一下通過ng-class給元素動態地加class有4種做法,下面一個一個來說。

    先看一下官方的說明

    547747ed00019d2705000171.jpg

    不知道大家能不能看清,給個鏈接吧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中


    首先是最不推薦的

1
2
3
4
5
6
7
    <div ng-class="{{myclass}}"></div>
    ....
    <script>
        function someController($scope){
            $scope.myclass = "xxx";
        }
    </script>

    上面這種方法效果上來說沒問題,但是完全沒必要用ng-class,普通的也能實現這個效果。而且在controller中控制樣式總感覺有點兒彆扭……


    然後說另一種用法

1
    <div ng-class="{true :'red', false :'green'}[someVariable]"></div>

    這種用法就是說variable爲true時,就給元素加上red這個class,如果variable爲false就加上green這個class,這個在邏輯比較簡單的時候還是蠻好用的。


    下一種適合需要添加多個類的時候,也就是ng-class的值爲一個對象

1
2
3
4
5
6
    <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中,效果圖

    547749e500016b1f02880112.jpg

    

    還有一種就是數組類型的,數組都每個字符串元素都會被加到class中

1
2
3
4
5
6
    <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;類的樣式後,我們輸入這些字符串就會出現效果

    54774a860001390801760135.jpg

    大概就是這幾種用法,我推薦大家用對象來添加對象,那樣最好控制邏輯也清楚。

------------------------------------------------------------------------------

    今天就分享這些吧,希望對大家有些幫助

2014-11-28
回覆 1

總結得很好,謝謝分享出來。哈哈

2014-11-26
回覆 0

    其實路由的功能是比較複雜的,我們實際應用中頁面的狀態也是非常多的,上面簡單的路由是肯定不能滿足我們的需求的,所以我們必須要更深入地瞭解下路由以及它更有用的一些用法。

--------------------------------------------------------------------

    首先來說一下templateUrl屬性,上面說了它的值是對應模板的地址,比如

1
2
3
        ...
        templateUrl : 'tpls/index.html',
        ...

    但事實上我們也可以用一個函數作爲值,但是這個函數必須返回模板的地址,比如

1
2
3
4
5
        ...
        templateUrl : function(){
                        return 'tpls/index.html';
                      },
        ...

    這兩段代碼的效果其實是一模一樣的,很容易理解。但是我們在實際應用的時候會經常有這樣的需求:在url裏拼接get參數,然後後臺利用get參數去讀數據庫然後返回相應的內容。這時候用第一種方法實現起來可能就不那麼容易(但是也是能實現的),而用第二種方法就很容易了。

    那麼在這之前需要介紹一個很有用的東西叫做$stateParams,看名字也很容易理解,狀態參量。這個東西就存儲了頁面狀態的有關信息,我們通過一個例子來看看這到底是神馬東東。

        這是我們的list.html頁面

1
2
3
4
5
<div>
    <p>This is the list page</p>
    <a ui-sref="detail({articleId:'111'})">文章詳情</a>
</div>   

        這是detail.html頁面

1
2
3
<div>
    <p>This is the detail page</p>
</div>

        這是路有部分了,注意看喲

1
2
3
4
5
6
7
8
9
10
11
.state('list',{
    url        : '/list',
    templateUrl : 'tpls/list.html'
})
.state('detail',{
    url        : '/detail/{articleId}',
    templateUrl : function($stateParams){
                      console.log($stateParams);
                      return 'tpls/detail.html';
                  }
})

    先來看看效果,當我們進入list頁面時是下面這個樣子

    5475f3190001e56403160176.jpg

    我們點擊文章詳情這個鏈接後,頁面變成了detail,並且下面打印出了$stateParams

    5475f3190001aba903240258.jpg

    

    這裏我們發現它是一個對象,並且包含了我們所傳遞的參數。小夥伴們看看我上面是怎麼在url裏傳遞參數的哦,就是加一個括號,裏面放一個我們要傳遞的對象即可。傳遞對象參數的時候需要注意,路由中url後面跟了多少參數你就只能傳遞那麼多參數,比如

    路由中這麼寫

1
2
3
4
5
6
7
.state('detail',{
    url        : '/detail/{articleId}/{else}',
    templateUrl : function($stateParams){
                      console.log($stateParams);
                      return 'tpls/detail.html';
                  }
})

    html頁面中這麼寫

1
2
3
4
<div>
    <p>This is the list page</p>
    <a ui-sref="detail({articleId:'111',else:'imooc'})">文章詳情</a>
</div>

    那我們輸出的效果就是這樣的

    5475f62c0001ec4303800275.jpg


    怎麼能用它呢?小夥伴們記得路由中的另一個參數controller,它爲模板指定了一個controller,事實上我們可以把$stateParams傳入controller中,看個例子吧

    首先我們在index.html引入controller.js

    list頁面和上面沒有變化

1
2
3
4
<div>
    <p>This is the list page</p>
    <a ui-sref="detail({articleId:'111',else:'imooc'})">文章詳情</a>
</div>

    路由中我們還是這麼寫,但是給detail頁面指定了一個controller

    然後我們在controller.js裏面

1
2
3
4
5
6
    //注意,我們需要在路由最開頭的依賴中加上myModule哦
    var myModule = angular.module('myModule',[]);
    myModule
        .controller('DetailController',function($scope,$stateParams){
            console.log($stateParams);
        });

     當我們在list頁面中點擊鏈接時,進入detail頁面,效果如下

    5475fa040001985b03310231.jpg

    我們在controller中也能打印出傳遞的參數,這就很有意思了。我們可以在controller中根據傳遞過來的參數和後臺服務器進行通信然後把返回的結果綁定在scope上,頁面是不是就可以顯示出來了呢,比如這樣

    

1
2
3
4
5
6
7
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:今天有門課期末前兩天不得不預習了整本書,缺了兩天,但是我覺得應該反思,養成一個好習慣不容易,不應該爲自己找各種理由,而且目測一般都掛科了……時間雖然緊,但擠擠肯定還是有的!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章