AngularJS从内部指令更改属性模型

时间:2015-04-20 23:12:52

标签: angularjs angularjs-directive

我已经设置了如下指令,即控制Flyout面板的打开和关闭,我希望弹出窗口的状态(无论是打开/关闭)是否可由父作用域公开访问,我看到一些为此使用服务,但似乎冗长使用服务,我的问题是我想知道是否有一种优雅的方法来设置附加到关闭事件属性的变量?或者我必须访问父范围?小提琴http://jsfiddle.net/sjmcpherso/EbDRR/

<div class="page">
<button ng-click="openFlyout()">Bhttp://jsfiddle.net/sjmcpherso/EbDRR/#baseutton {{fly.flyoutOpen}}</button>
<flyout foopen={{fly.flyoutOpen}}>
    <button ng-click="close()">X</button>
</flyout>
</div>



angular.module('myApp', [])
.controller('MyController', function($scope) {
        $scope.fly = {flyoutOpen:false};
        $scope.openFlyout = function(){
            $scope.fly.flyoutOpen = !$scope.fly.flyoutOpen;
        }
    }).directive('flyout', function() {

    return {
        restrict: 'AE',
        link: function(scope, el, attr) {

            close.bind('click', function() {
               //Set fly.flyoutOpen via attr.foopen to false
            });
            attr.$observe('foopen', function(value) {        
                console.log(typeof value);
                if (value == "true") {                      
                    el.css('right', '0');
                    console.log("left:"+value);
                } else {                                   
                    el.css('right', '-150px');
                    console.log("right:"+value);
                }       
            });
        }
    };
});

4 个答案:

答案 0 :(得分:0)

在我看来,改变指令中的父范围不是角度方式。 虽然我也遇到过无法找到其他解决方案的情况,但我认为我们应该尽可能避免这种情况。 在您的情况下,我相信您可以避免更改指令中的父作用域。

因此,我建议你......

  1. 删除指令中的close.bind('click'...

  2. 添加控制器的close功能,您可以在其中更改范围值。(正如您所知,这样就不会破坏角度原则。)

  3. jsfiddle is here.

    我希望这会对你有所帮助。

答案 1 :(得分:0)

很简单。 您可以使用可用于双向绑定的指令范围。

<http://jsfiddle.net/EbDRR/14/>

我添加了&#34; openclose&#34;你的代码中的属性。

答案 2 :(得分:0)

看看这两个例子;对不起,他们都是单页... ... 我试图保持代码自包含,以便您可以在一个地方看到所有的部分。通常,构建过程会保持代码和模板以及页面分离;不是,在这里。

好消息是这些页面应该在没有服务器的情况下工作(因为唯一的外部文件是有角度的),因此您可以将每个页面保存为.html页面,并从驱动器中打开它。


父母与子女之间的共享配置

<!doctype html>


<html ng-app="myApp" ng-controller="MyController">
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.min.js"></script>
    <style>
      page-flyout { display: block; }
      page-flyout.expanded { background-color : grey; }
    </style>
  </head>
  <body>
    <main ng-controller="page-controller as page">
      <button ng-click="page.toggle()"
        >{{ page.flyout.expanded ? "Hide":"Show" }} Flyout</button>
      <page-flyout
        view="page.flyout"
        class="ng-class: {'expanded':page.flyout.expanded,'contracted':!page.flyout.expanded }"
        ></page-flyout>
    </main>
    <script type="text/ng-template" id="components/page-flyout/page-flyout.html">
      <h4>I am {{ view.expanded ? "OPEN" : "CLOSED" }}</h4>
      <button ng-click="flyout.close()" ng-hide="!view.expanded">&times;</button>
    </script>
    <script >
    var app = initApp();
    initAngular(app);


    function initAngular (MyApp) {
      angular.module("myApp", ["pageFlyout"]);
      angular.module("pageFlyout", []);

      angular.module("myApp")
        .controller("MyController", [MyApp.MyController])
        .controller("page-controller", [MyApp.PageController]);

      angular.module("pageFlyout")
      .controller("page-flyout-controller", ["$scope", MyApp.PageFlyoutController])
      .directive("pageFlyout", [function () {
        return {
          scope: { view: "=" },
          restrict: "AE",
          replace: false,
          controller: "page-flyout-controller",
          controllerAs: "flyout",
          templateUrl: "components/page-flyout/page-flyout.html"
        };
      }]);
    };

    function initApp () {
      var MyApp = {
        MyController: function MyController () {

        },

        PageController: function PageController () {
          var page = extend(this, { toggle: toggle, flyout: { expanded: false } });
          function toggle () {
            var currentState = page.flyout.expanded;
            page.flyout.expanded = !currentState;
          }
        },

        PageFlyoutController: function PageFlyoutController ($scope) {
          var flyout = extend(this, { close: close });
          function close () { $scope.view.expanded = false; }
        }
      };
      function extend () {
        var args = [].slice.call(arguments);
        return angular.extend.apply(angular, args);
      }

      return MyApp;
    }
    </script>
  </body>
</html>

我正在使用“PageController”作为外部控制器;此外部元素具有toggle方法和flyout对象 flyout对象具有expanded属性,仅此而已 这是将在父级和子级之间共享的配置/状态对象。父母对孩子一无所知,孩子对父母一无所知...... 但两者都需要了解这一个对象的结构;基本上,父母必须同意给孩子一个满足孩子需要的配置对象,如果它将使用孩子的服务。

在我的子范围中,我将此对象称为view,它在我的指令声明<page-flyout view="page.flyout">中提供。

弹出窗口有一个控制器,使用close方法设置view.expanded = false; 因为父和子都共享这个配置对象,并且因为UI事件触发了摘要检查,所以这很有效,并且每个人都很高兴......

尝试一下,看看会发生什么:flyout html在一个完全不同的宇宙中,然而按钮仍然按照它应该做的,并且弹出窗口范围内外的所有文本都保持最新。 ..

...除了这不是那么干净。

分享某种可读状态是一回事,但是两个人直接写信给它并从中读取,可能会有点多。


父控件状态,子定义事件,父实现事件处理程序

<!doctype html>


<html ng-app="myApp" ng-controller="MyController">
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.min.js"></script>
    <style>
      page-flyout { display: block; }
      page-flyout.expanded { background-color : grey; }
    </style>
  </head>
  <body>
    <main ng-controller="page-controller as page">
      <button ng-click="page.toggle()"
        >{{ page.flyout.expanded ? "Hide":"Show" }} Flyout</button>
      <page-flyout
        onclose="page.close()"
        class="ng-class: {'expanded':page.flyout.expanded,'contracted':!page.flyout.expanded }"
        ></page-flyout>
    </main>
    <script type="text/ng-template" id="components/page-flyout/page-flyout.html">
      <h4>I am {{ view.expanded ? "OPEN" : "CLOSED" }}</h4>
      <button ng-click="flyout.close()">&times;</button>
    </script>
    <script >
    var app = initApp();
    initAngular(app);


    function initAngular (MyApp) {
      angular.module("myApp", ["pageFlyout"]);
      angular.module("pageFlyout", []);

      angular.module("myApp")
        .controller("MyController", [MyApp.MyController])
        .controller("page-controller", [MyApp.PageController]);

      angular.module("pageFlyout")
      .controller("page-flyout-controller", ["$scope", MyApp.PageFlyoutController])
      .directive("pageFlyout", [function () {
        return {
          scope: { onclose: "&" },
          restrict: "AE",
          replace: false,
          controller: "page-flyout-controller",
          controllerAs: "flyout",
          templateUrl: "components/page-flyout/page-flyout.html"
        };
      }]);
    };

    function initApp () {
      var MyApp = {
        MyController: function MyController () {

        },

        PageController: function PageController () {
          var page = extend(this, {
            toggle: toggle,
            close: close,
            flyout: { expanded: false }
          });

          function toggle () {
            var currentState = page.flyout.expanded;
            page.flyout.expanded = !currentState;
          }
          function close () {
            page.flyout.expanded = false;
          }
        },

        PageFlyoutController: function PageFlyoutController ($scope) {
          var flyout = extend(this, { close: close });
          function close () { $scope.onclose(); }
        }
      };
      function extend () {
        var args = [].slice.call(arguments);
        return angular.extend.apply(angular, args);
      }

      return MyApp;
    }
    </script>
  </body>
</html>
我会救你一些狩猎;几乎每条线都是100%相同的 重要的是在指令中:

return {
  scope: { onclose: "&" }
}

...&符号表示属性$scope.onclose是一个方法,它是在指令声明中定义的。

果然,当您查看page-flyout的声明时,您会注意到<page-flyout onclose="page.close()">

现在$ scope.onclose不等于page.close();,因为它等于function () { page.close(); }
在父母的范围内,它可以是一个简单的表达式:

<page-flyout onclose="page.flyout.expanded = false">
$scope.onclose = function () { page.flyout.expanded = false; };

您甚至可以将命名参数传递回父表达式/方法(此处不必要;在教程中阅读它,但基本上您调用$scope.method( paramsObj ),其中对象的键与参数名称相同实施。非常方便。

所以在我的情况下,我的弹出按钮调用flyout.close(),这是我控制器上的一个方法。我的控制器方法调用$scope.onclose();(我的视图模型之间的分隔,以及将所有内容联系在一起的特定于角度的粘合剂),$scope.onclose然后调用父项中的page.close(),其设置{{1} }}

尼斯。整齐。干净。

孩子对父母一无所知,但是,我创建了父母可以订阅的事件;在HTML中,不能少。

但是,

一个重大问题 如果您加载此页面,您会注意到弹出按钮的文本永远不会改变,并且弹出按钮的关闭按钮永远不会隐藏,具体取决于弹出按钮是“打开”还是“关闭”。 原因很简单 孩子对父母或父母的数据一无所知。

这很好,但在这种情况下,这意味着尽管能够自己关闭就好了,但它永远不会知道它是否首先打开(如果我在父范围中使用page.flyout.expanded = false;,那将解决问题,如果你不想让它半开)。

当然,解决方案是给孩子一个共同的对象,代表一个弹出的状态......
我们以前去过那里...... ......除了这次,它是一个只读对象(没有保证,只是通过练习),你正在使用事件来说话,并使用属性来说话。

真正的解决方案包括同时执行这两项操作:在子视图中同时使用子级的只读属性,以及创建父级可以挂接的事件,以更新其模型(以及它给孩子的内容在下一个$ digest上。)

然后,除了孩子定义的合同和父母保留的合同之外,没有人知道关于另一个的任何事情。

答案 3 :(得分:0)

我认为你想要实现的目标如下。

  1. 将功能封装在指令中。
  2. 不与父控制器共享范围值。
  3. 不使用服务。
  4. 我的想法是这样的。

    1. 准备作为更新弹出式面板元素的包装函数。(此函数仅完全封装在指令中。
    2. 设置包装器并在指令代码中单独使用
    3. 在实例化包装器后,指令通过oninit函数将其发送给父控制器。
    4. 收到包装后,控制器可以使用而不分享范围
    5. jsfiddle is here.