2015-04-21 147 views
2

我正在處理一個管理用戶登錄的應用程序。像許多應用程序,我想改變的標題,當用戶登錄英寸 我有它使用ng-include爲包括header.html

我發現了兩個解決方案(我是新來的角主文件(index.html)這樣既可能是錯誤的):

1)使用$ rootScope.broadcast()

所以,當我在用戶登錄廣播(在auth.js,那就是被截獲factory)的消息內標題中的控制器。

auth.js

$rootScope.$broadcast('logged',user); 

controller.js

$scope.$on('logged', function(evnt, message){ 
     $scope.user = message; 
    }); 

header.html

<div class="header" ng-controller="GcUserCtrl as gcUserCtrl"> 
... 
    <li><a ng-show="user" href="#">User: {{user.name}}</a></li> 

2)設置一個$ rootScope可變

據我所知$rootScope是所有範圍的根(命名非常聰明),並且所有$scope都有權訪問它。

(這裏不再控制器)的auth.js

$rootScope.user=user; 

heaeder.html

<div class="header"> 
... 
    <li><a ng-show="user" href="#">User: {{user.name}}</a></li> 

現在,什麼來處理它的正確方法?

  • 第一似乎更有點貴因爲廣播可能必須做很多檢查。
  • 第二..嗯,我不是全局變量的粉絲..

編輯

3)使用服務

亞歷克斯我添加評論後這個選項,即使我不能使它工作。(here the plunkr不無事件

index.html

... 
<ng-include src="'header.html'"></ng-include> 
... 

header.html

爲1號)

controller.js

工作
.controller('GcUserCtrl', ['$scope','my.auth','$log', function ($scope, auth, $log) { 
    $scope.user = auth.currentUser(); 
    }]); 

my.auth.js

.factory('my.auth', ['$rootScope', '$log', function ($rootScope, $log, localStorageService) { 
    var currentUser = undefined; 

    return { 

     login: function (user) { 
     currentUser = user; 
     ... 

     }, 

    ... 
     currentUser: function() { 
     return currentUser; 
     } 
    }; 
    }]); 

這裏的問題是,控制器被稱爲僅第一次並沒有什麼登錄後會發生。

+4

更好的選擇是使用登錄服務[documentation here](https://docs.angularjs.org/guide/services)。這樣你可以根據需要注入不同模塊的服務。 – alex

+0

@alex - 這可能是答案... OP正在尋找正確的方式做到這一點...也許你應該充實和發佈回答... – DrCord

+0

我添加了服務,這是我的第一個想法,但我不能使它工作 – EsseTi

回答

1

事件是傳達行動的首選方式需要採取別的東西。這種行爲發生了,其他事情可能會對其採取行動感興趣。正如你所提到的那樣,它也能減少範圍污染

在這種情況下使用服務的評論只是部分準確的。所有的登錄邏輯都可以並且應該被放入一個專門用於登錄和註銷的服務中。該服務會在發生登錄時廣播該事件。

module.service('LoginHelper', function($rootScope) { 
    this.loginUser = function(username, password) { 
     // on success 
     $rootScope.broadcast('loggedIn', logginUserData) 
    } 
    this.logout = function() { 
     // on success 
     $rootScope.broadcast('loggedOut') 
    } 
}) 

登錄的數據應該存儲並可由服務訪問。

或者,可以在$ rootScope上使用$ emit。那麼您將只能夠在$ rootScope上觀察'loggedIn'事件,因爲這會稍微減少開銷。

+0

到底正確的解決方法是這樣的。 – EsseTi

4

正如我前面說過的,您將希望使用服務來存儲用戶的信息。將用戶信息附加到此服務中,只要您對用戶進行身份驗證即可。如果您有關於認證的最佳方式的問題,這將是一個單獨的問題,但您可能需要考慮使用實際身份驗證(以及任何授權)的Login Factory。然後,您可以將登錄服務注入該工廠。我創建了一個Plunker here作爲參考。

var app = angular.module('myApp', []); 

app.service('SessionService', function() { 
    this.attachUser = function(userId, fName){ 
    this.userId = userId; 
    this.fName = fName; 
    } 
}); 


app.controller('MainCtrl', function($scope, SessionService){ 
    // You will want to invoke attachUser some other way (perhaps on authentication), this is for test purposes only 
    SessionService.attachUser(1234, 'John'); 

    $scope.userName = SessionService.fName; 
}); 

上面的代碼是您的服務的一個示例。這將充當會話處理程序並存儲有關用戶的重要信息。然後控制器MainCtrl可以使用依賴注入來調用SessionService中的屬性。我在本文開頭提到的部分SessionService.attachUser(userId, fName)很可能住在登錄工廠。

這是最好的選擇是因爲它解耦了你的應用程序。它將會話(這實際上就是你在全局變量中存儲的內容)放在指定存儲數據的地方。它使它可維護。例如,您不需要查找每個出現的$ rootScope。

編輯: New plunker使用rootScope廣播/上捕獲變更

+0

我做了類似的事情,但是當我做'auth.login(user)'時,顯然沒有任何反應。看起來頭控制器只在第一次被調用。 'auth.login'由頁面中的另一個控制器調用。 – EsseTi

+0

@EsseTi可以將它發佈在Plunker上嗎 – alex

+0

這就是簡單的在服務中存儲值的問題。沒有通知該值已更改,這意味着手錶需要用戶名值。對於每個值或對包含所有用戶數據的對象的深入監視,當一個事件對其進行換位而沒有任何開銷時,就會過多。 – Enzey

0

避免手錶

的事件將是合適的方式去爲這種需求,怎麼樣alex指出。一個普拉克展示一個例子:http://plnkr.co/edit/v6OXjOXZzF9McMvtn6hG?p=preview

但對於這種特殊的情況下,我不認爲「角辦法」「的方式」。鑑於$broadcast和/或$emit如何工作(即默認方式事件工作在角度),我會避免它們...(閱讀docs以瞭解原因)。簡而言之,這些機制旨在觸發聽衆(附加到某個範圍)向上/向下的範圍層次。你並不需要那麼多。 (Ref code for $emit

我通常會依賴其他事件傳播機制(考慮這種需求模式)。

app.controller('MainCtrl', function($scope, SessionService, $document){ 
    // You will want to invoke attachUser some other way (perhaps on authentication), this is for test purposes only 
    $scope.isLoggedIn = false; 
    $document.bind('$loggedin', function(){ 
     $scope.isLoggedIn = true; 
     $scope.user = SessionService.fName; 
    }); 
    $scope.logout = function() { 
    SessionService.attachUser(null, null); 
    $scope.isLoggedIn = false; 
    }; 
}); 

app.controller('LoginCtrl', function($scope, SessionService, $document){ 
    $scope.doLogin = function() { 
    console.log('doLogin'); 
    SessionService.attachUser(1234, $scope.username); 
    var doc = $document[0]; 
    var evt = new Event('$loggedin'); 
    doc.dispatchEvent(evt); 
    }; 
}); 

Plunk

當然,當你用這個觀點,總是清理完成。處理設備上那些控制器的範圍$destroy事件和取消綁定事件處理程序...

$scope.$on('$destroy', function() { 
    $document.unbind('$loggedin'); 
}; 

參考MDN更多關於如何觸發使用DOM事件。

更新:[9月24日]

這裏是一個小指令設置,其中說明了這一點:

app.directive('ngNest', function($parse, $compile, $timeout){ 
    var end = false; 
    var level = 0; 

    var fnPostLink = function(scope, element, attrs) {  
    //console.log('start:', ++fnPostLink.count); 
    var lvl = attrs.level; 
    if(!lvl) { 
     throw 'Level not specified'; 
    } 
    var create = document.createElement.bind(document); 

    var level = parseInt(lvl); 
    var count = 0; 
    var div = create('div'); 
    div.setAttribute('ng-controller', 'DummyCtrl'); 
    var cls = function() { 
     return 'margin ' + (count % 2 ? 'even' : 'odd'); 
     //return 'margin even'; 
    }; 
    div.setAttribute('class', cls()); 
    var node = div; 
    while(count++ < level - 1) { 
     var child = create('div'); 
     child.setAttribute('ng-controller', 'DummyCtrl'); 
     child.setAttribute('class', cls()); 
     node.appendChild(child); 
     node = child;  
    } 

    node.setAttribute('ng-controller', 'FinalCtrl'); 
    node.innerHTML = 'foo'; 
    var $new = $compile(div)(scope); 
    var el = element; 
    el.append($new); 
    }; 

    fnPostLink.count = 0; 
    var fnPreLink = function(scope, element, attrs) {  
    //console.log('prelink');  
    }; 

    var api = {  
    link: { 
     post: fnPostLink, 
     pre: fnPreLink 
    }, 
    template: '<div></div>', 
    scope: {}, 
    restrict: 'E',  
    replace: true 
    }; 

    return api; 

}); 

它簡單地嵌套的div附加控制器給它。我附加了這兩個控制器:

app.controller('DummyCtrl', function($scope){ 
}); 

app.controller('FinalCtrl', function($scope, $document){ 
    $scope.$on('$myEvt', function(){ 
    console.log('$myEvt', $scope.$id, new Date().getTime()); 
    }); 

    $document.bind('$myEvt', function(){ 
    console.log('$myEvt', $scope.$id, new Date().getTime()); 
    }); 
}); 

FinalCtrl被添加到尾部; DummyCtrl被添加到其餘的。

在HTML模板我做這樣的事情:

<ng-nest level="10"></ng-nest> 

也有在HTML文件中嵌套的標記被手動放在那裏......

整個代碼可以在這裏找到:https://gist.github.com/deostroll/a9a2de04d3913f021f13

這裏是我獲得從我的瀏覽器中運行的結果:

Live reload enabled. 
$broadcast 1443074421928 
$myEvt 14 1443074421929 
$myEvt 19 1443074421930 
DOM 1443074426405 
$myEvt 14 1443074426405 
$myEvt 19 1443074426405 

當我完成$廣播時,您可以注意到滴答的差異。我在$ rootScope上完成了$廣播;因此沿着作用域樹深度優先,並觸發那些監聽器附加相應的作用域,以這種順序 ... $ stuff & $廣播源代碼也驗證了這一事實。

+0

如果您打算推薦使用$ broadcast和$發佈標準JS事件,請包括理性或關於此的寫作鏈接。 – Enzey

+0

@Enzey更新後。檢查$ emit的源代碼開始...你會發現它最終... :) – deostroll

+0

我確信我可以搞清楚,但是SO是關於知識共享的。沒有解釋說這個評論對於那些希望瞭解何時應該或不應該使用Angular事件的人來說並不是非常有用。 – Enzey