指令从另一个模块访问范围

时间:2015-02-06 14:47:04

标签: javascript angularjs angularjs-directive angularjs-scope

所以,我对AngularJS很新,我可能会错误地解决这个问题,特别是在我使用指令的方式上。

我有一个主应用程序,我想放置一些常用代码,就像在html页面上显示一些消息的指令。我有一个控制器,它进行休息API调用以验证凭据,然后将一些数据分配给$ scope。我喜欢该指令使用该范围数据来显示html中的消息并进行一些格式化。根据我的研究,从控制器执行此操作并非最佳做法。我知道我可以直接在控制器中执行此操作,只需在html中添加div:

<div>{{validation.message}}</div>

html的简化版本是:

    <html data-ng-app="myApp" data-ng-strict-di>
        <body>
            <div data-ng-view data-ng-controller="MainController">
                <div data-ng-app="StoredAccountsModule" data-ng-controller="AccountController">
                    <button class="btn btn-default" data-ng-click="modalOptions.verify(newAccount)">Verify Credentials</button>
                    <div data-myappmessage></div>
                </div>
            </div>
        </body>
    </html>

指令是:

angular.module("myApp").directive("myappmessage", function () {
    //&#9989 : &#10006
    return {
        link: function postLink(scope, element, attrs) {

            element.html('<span>' + scope.validation.message + '</span>');
    }
    };
});

我确定我错过了控制器,模块和指令如何连接在一起的东西。

更新

好的,对于每个人的评论,我已经解决了很多关于我试图做的事情的问题。现在,我正在尝试使用按钮单击从引导模式启动帐户验证,该按钮单击启动API调用以返回验证信息。模式正在作为服务启动,如下所示:http://weblogs.asp.net/dwahlin/building-an-angularjs-modal-service。重要的模态窗口部分是:

<div data-ng-controller="MainController">
    <div data-simplevalidation></div>
</div>
<div class="modal-footer">
    <button class="btn btn-primary" data-ng-click="modalOptions.ok(newAccount)">{{modalOptions.actionButtonText}}</button>
    <button class="btn btn-warning" data-ng-click="modalOptions.close('cancel')">{{modalOptions.closeButtonText}}</button>
    <button data-ng-controller="MainController" class="btn btn-default" data-ng-click="verifyAccount(newAccount)">Verify Credentials</button>
</div>

该模式正在从:

启动
<div data-ng-controller="StoredAccountsIndexController" style="width:auto;text-align:center;">
    <table style="border:1px solid black;margin:auto;">
        <tfoot>
            <tr>
                <td colspan="2"><button type="button" class="btn btn-primary" data-ng-click="open()">Add New Stored Account</button></td>
            </tr>
        </tfoot>
    </table>
</div>

该指令现在是

angular.module("myApp").directive("simplevalidation", function () {
    return {
        template: '<button type="button" class="btn btn-default" data-ng-click=verifyAccount(newAccount)>Verify</button><span></span>',
        link: function ($scope, element, attrs) {
            $scope.$watchGroup(["validation.message", "validation.success"], function () {

                if ($scope.validation.success != null) {
                    if ($scope.validation.success) {
                        element.children("span").html($scope.validation.message + " " + $scope.validation.symbol);
                        element.children("span").css("color", "green")
                    }
                    else {
                        element.children("span").html($scope.validation.message + " " + $scope.validation.symbol);
                        element.children("span").css("color", "red")
                    }
                }

            }, true);
        }
    };
});

页面上只声明了一个应用程序myApp。我相信我遇到的问题在于模式的范围。如果我使用指令模板放置的按钮,一切都按照它应该的方式工作。如果我使用模态页脚中的按钮,API调用将触发,但我从未在我的范围上看到更新。处理该呼叫的控制器是:

angular.module("myApp").controller("MainController", ['$scope', '$timeout', "StoredAccountsFactory", function ($scope, $timeout, StoredAccountsFactory) {
    $scope.validation = {}

    $scope.verifyAccount = function (account) {
        $scope.validation = {};

        StoredAccountsFactory.validateStoredAccount(account).success(function (data, status, headers, config) {
            $scope.validation.message = "Account credentials verified";
            $scope.validation.success = true;
            $scope.validation.symbol = "&#9989;"
        }).error(function (data, status, headers, config) {
            $scope.validation.message = data;
            $scope.validation.success = false;
            $scope.validation.symbol = "&#10006;"
        });
    }
}]);

关于我在这里做错了什么的任何其他见解?

我也尝试过这种格式的指令,因为我认为这是处理指令范围的首选方式,而且结果没有任何变化。

angular.module("myApp").directive("simplevalidation", function () {
    //add these attributes to the directive element
    //data-validate="verifyAccount(newAccount)" data-validationmessage="validation.message" data-validationsuccess="validation.success"

    return {
        scope: {
            validate: '&',
            validationmessage: '=',
            validationsuccess: '='
        },
        template: '<button type="button" data-ng-click=validate()>Verify</button><span></span>',
        link: function ($scope, element, attrs) {
            $scope.$watchGroup(["validationmessage", "validationsuccess"], function () {

                if ($scope.validationsuccess != null) {
                    if ($scope.validationsuccess) {
                        element.children("span").html($scope.validationmessage + " " + " &#9989;");
                        element.children("span").css("color", "green");
                    }
                    else {
                        element.children("span").html($scope.validationmessage + " " + " &#10006;");
                        element.children("span").css("color", "red");
                    }
                }

            }, true);
    }

    };
});

2 个答案:

答案 0 :(得分:2)

你在这里误解了一些概念,我认为它使你想要做的事情过于复杂。

<强> 1。模块: 模块只是一种使用依赖项组织代码的方法。加载后,哪个模块托管哪个服务/控制器/指令并不重要。

<强> 2。应用和ng-app: 每页确实应该有一个应用程序。您可以有多个,但您需要手动angular.bootstrap它们,我相当肯定它们不能嵌套。此外,应用程序不会共享相同的服务或范围实例,因此应用程序实际上是一个独立的执行单元(从Angular的角度来看)。

因此,StoredAccountsModule应用myApp的嵌套应该

<html ng-app="myApp" >
  <body>
     <div data-ng-view data-ng-controller="MainController">
        <div data-ng-app="StoredAccountsModule">
           ...
        </div>
     </div>
  </body>
</html>

第3。控制器:

控制器定义和设置视图模型(通过$scope或使用Controller As方法)。你做的那个陈述:

  

我知道我可以直接在控制器中执行此操作,只需在html中添加div:

     

<div>{{validation.message}}</div>

暗示它违反了最佳实践(与创建指令相反)显然是错误的。

不鼓励(并且皱眉)正在操纵,访问或以其他方式对控制器中的视图(即DOM)做出任何假设。这是因为控制器只处理后端模型和ViewModel之间的数据转换和编组。

换句话说,控制器处理应用程序的体系结构和逻辑 - 而不是视图中的表现形式。

控制器和依赖注入(DI)的特性使控制器具有高度可测试性。

<强> 4。指令: 指令(在大多数情况下)是指可直接处理DOM的可重用功能。指令不应该围绕HTML或控制器进行任何假设(理想情况)。

Angular确实允许指令使用外部作用域,但它使它们的可重用性降低。在这方面,您的指令myappmessage并没有提供太多价值。

即使在编写指令时,也不应忘记Angular支持的MVVM原则。因此,指令还有一个范围和一个控制器,并且可以为其功能重用其他和内置指令。

<强> 5。范围: 范围独立于模块或控制器或指令。所以这个问题的标题是#34;从另一个模块访问范围&#34;毫无意义。 Angular为App创建根范围。

控制器和指令共享范围(除非指令创建隔离范围),因此将变量发布到范围的父控制器通过范围继承使变量可用于其子树。

范围继承只是控制器之间通信的一种方式(有些人认为这是一种不好的做法),但它可以在那里使用(如果考虑到范围的原型继承性质的所有约束而正确使用)

app.controller("ParentCtrl", function($scope){
   var VM = $scope.VM = ($scope.VM || {});

   VM.isBusy = false;
})
.controller("ChildCtrl", function($scope, $http){
   var VM = $scope.VM = ($scope.VM || {});

   $scope.loadSomething = function(){
      VM.isBusy = true;
      $http.get("something").then(function(d){
         VM.isBusy = false;
      });
   }
});

在视图中:

<div ng-controller="ParentCtrl">
  <div ng-show="VM.isBusy">loading...</div>
  <div ng-controller="ChildCtrl">
    <button ng-click="loadSomething()">load</button>
  </div>
</div>

(这些控制器,顺便说一句,可能很容易来自不同的模块)

答案 1 :(得分:0)

您应该尝试使您的指令自包含,而不是直接访问父作用域,然后监视要对更改做出反应的表达式。

    angular.module("myApp").directive("myappmessage", function () {
        //&#9989 : &#10006
        return {
            scope: { 
              message: '='
            },
            link: function postLink(scope, element, attrs) {                            
                scope.$watch('message', function(value) {
                    element.html('<span>' + value + '</span>');
                });
            }
        };
   });

然后按如下方式使用它:

<div data-myappmessage message="validation.messages"></div>