【问题标题】:Angular directive for temporary local variables临时局部变量的 Angular 指令
【发布时间】:2016-01-22 18:28:42
【问题描述】:

我在这里要做的是创建一个指令,允许我设置仅适用于我正在呈现的 html 标记内的临时变量。用例是这样的:

<div class="input-group" ng-local="opened = false" ng-blur="opened = false;">
    <input type="text" class="form-control" uib-datepicker-popup="longDate" ng-model="start" is-open="opened" ng-focus="opened = true;" />
    <span class="input-group-btn">
        <button type="button" ng-click="opened = true;" class="fa fa-calendar" ></button>
    </span>
</div>

这里的想法是 ng-local 指令创建一个变量 opened 并将该变量设置为初始值 false。指令中的所有内容都是一个嵌入的模板。这里的好处是我可以在一个页面上拥有多个日期选择器,所有使用相同变量 opened 的人都不必在控制器的范围内放置一堆不同的变量,这些变量仅用作临时变量div 内的内容。但是,由于这将以多种不同的方式使用,我不想为每个用例制定不同的指令。

我的第一次尝试非常顺利。但是,我遇到了一个问题,即没有从日期选择器正确访问父范围变量start。我对 $transclude 功能不是很熟悉,所以我希望有人能指出我正确的方向。这是我目前写的指令:

(function () {
    angular.module('myApp').directive('ngLocal', [function () {
        return {
            restrict: 'A',
            transclude: 'element',
            replace: false,
            scope: {
                ngLocal: '@'
            },
            link: function ngLocalLink(directiveScope, element, attrs, ctrl, $transclude) {
                $transclude(directiveScope, function ngLocalTransclude(clone, scope) {
                    element.empty();
                    element.replaceWith(clone);
                    scope.$eval(directiveScope.ngLocal);
                });
            }
        };
    }]);
})();

提前致谢!

编辑

这是一个plunkr链接

https://plnkr.co/edit/pog2bcxEf8mDEb2vIVjP?p=preview

【问题讨论】:

  • 你能用你已经在运行的东西构建一个 plunker 吗?
  • @IgnacioVillaverde 这有点复杂,但我正在努力
  • 相信会有很大帮助!
  • @IgnacioVillaverde 完成!
  • 谢谢,我正在努力

标签: javascript angularjs angularjs-directive angularjs-ng-transclude


【解决方案1】:

您的指令中不需要 transclude,只需创建 child or isolate scope

样本

angular.module('myApp', ['ngAnimate', 'ui.bootstrap']);




// CONTROLLER
angular.module('myApp').controller('myController', function($scope) {


  $scope.dates = {
      workingDate : new Date(),
      brokenDate1 : new Date(),
      brokenDate2 : new Date(),
      localDate : new Date(),
  }

});



// DIRECTIVE
angular.module('myApp').directive('ngLocal', [
  function() {
    return {
      restrict: 'A',
      replace: false,
      scope: true //directive have own scope
    };
  }
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-animate.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-1.1.0.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">

<div ng-app="myApp">

  <div ng-controller="myController">


    <h4>This one works</h4>
    <div class="input-group">
      <input type="text" class="form-control" uib-datepicker-popup="longDate" is-open="workingOpen" ng-model="dates.workingDate" />
      <span class="input-group-btn">
        <button class="btn btn-secondary" type="button" ng-click="workingOpen = true" >OPEN</button>
      </span>
    </div>

    <br/>
    <br/>
    <br/>



    <h4>This is the problem I'm trying to solve</h4>
    <h4>Both datepickers use "brokenOpen" so they both open whenever either is clicked</h4>
    <div style="width: 40%; display: inline-block;" ng-local>
      <div class="input-group">
        <input type="text" class="form-control" uib-datepicker-popup="longDate" is-open="brokenOpen" ng-model="dates.brokenDate1" />
        <span class="input-group-btn">
          <button class="btn btn-secondary" type="button" ng-click="brokenOpen = true" >OPEN</button>
        </span>
      </div>
    </div>
    <div style="width: 40%;  display: inline-block;" ng-local>
      <div class="input-group">
        <input type="text" class="form-control" uib-datepicker-popup="longDate" is-open="brokenOpen" ng-model="dates.brokenDate2" />
        <span class="input-group-btn">
          <button class="btn btn-secondary" type="button" ng-click="brokenOpen = true" >OPEN</button>
        </span>
      </div>
    </div>



    <br/>
    <br/>
    <br/>

    <h4>This is using my directive</h4>
    <h4>The date does not update correctly to the parent scope</h4>

    <div class="input-group" ng-local="localOpen = false">
      <input type="text" class="form-control" uib-datepicker-popup="longDate" is-open="localOpen" ng-model="dates.localDate" />
      <span class="input-group-btn">
              <button class="btn btn-secondary" type="button" ng-click="localOpen = true;" >OPEN</button>
          </span>
    </div>

    <label>See how the date is not updating: {{dates.localDate}}</label>


  </div>
</div>

【讨论】:

  • 我试过了,它仍然没有更新 $parent 范围内的日期。
  • @mrK,是的,我在你的问题中错过了这个。在这种情况下,您应该使用 dot rule for ng-model
【解决方案2】:

尝试嵌套指令。 outer 指令封装了你想要分享的数据,inner 指令从outer 提供的函数中获取数据。

演示链接: https://plnkr.co/edit/4n6kf40ZMf7lRCad5ofe?p=preview

代码:

angular.module('myapp', [])
  .directive('outer', function () {
    return {
      restrict: 'E',
      transclude: true,
      scope: {
        value: '='
      },
      template: function(element, attrs) {
        return '<div>outer! value = {{value}}<div ng-transclude></div></div>';
      },
      controller: function($scope) {
        this.getValue = function() {
          return $scope.value;
        }
      }
    }
  })
  .directive('inner', function () {
    return {
      restrict: 'E',
      template: function(element, attrs) {
        return '<div>inner! value = {{value}}</div>';
      },
      require: '^outer',
      link: function (scope, element, attrs, parentCtrl) {
        scope.$watch(
          function() {
            return parentCtrl.getValue();
          }, function(oldValue, newValue) {
            scope.value = parentCtrl.getValue();
          }
        );
      }
    }
  });

【讨论】:

    【解决方案3】:

    尝试改用$parent.localDate

    <div class="input-group" ng-local="localOpen = false">
          <input type="text" class="form-control" uib-datepicker-popup="longDate" is-open="localOpen" ng-model="$parent.localDate" />
          <span class="input-group-btn">
              <button class="btn btn-secondary" type="button" ng-click="localOpen = true;" >OPEN</button>
          </span>
    </div>
    

    如果您不想使用 $parent,您可以使用隔离范围,并设置您要使用的变量:

    <div class="input-group" ng-local="localOpen = false" date="localDate">
          <input type="text" class="form-control" uib-datepicker-popup="longDate" is-open="localOpen"  ng-model="date" />
          <span class="input-group-btn">
              <button class="btn btn-secondary" type="button" ng-click="localOpen = true;" >OPEN</button>
          </span>
    </div>
    
    angular.module('myApp').directive('ngLocal', [function () {
    return {
        restrict: 'A',
        transclude: 'element',
        replace: false,
        scope: {
            ngLocal: '@',
            date: '='
        },
        link: function ngLocalLink(directiveScope, element, attrs, ctrl, $transclude) {
            $transclude(directiveScope, function ngLocalTransclude(clone, scope) {
                element.empty();
                element.replaceWith(clone);
                scope.$eval(directiveScope.ngLocal);
            });
        }
    };
    }]);
    

    这是分叉的 plunker:https://plnkr.co/edit/4zrNzbSc5IwqqbE2ISE1?p=preview

    【讨论】:

    • 我希望获得类似于 ng-repeat 的功能,您不必通过嵌入元素中的父范围来解决所有问题。
    • @mrK 看看编辑后的答案,你还有其他选择。
    【解决方案4】:

    我希望获得类似于ng-repeat 的功能,您不必通过嵌入元素中的父范围来解决所有问题。

    ng-repeat 不使用 隔离 范围。它使用继承范围。

    有关指令范围的详细信息,请参阅AngularJS $compile Service API Reference -- scope


    示例

    此自定义指令多次嵌入其内容,每次都创建一个新的继承范围。重复次数由读取repeat属性决定。

    angular.module('myApp').directive('repeat', function () {
        return{
            scope: false,
            transclude: 'element',
            link : function(scope, element, attrs, ctrl, transcludeFn){
                var parent = element.parent();
                var repeatNum = attrs.repeat;
                for(var i = 1;i<= repeatNum;++i){
                    var childScope = scope.$new();
                    childScope.$index = i;
                    transcludeFn(childScope, function (clone) {
                        parent.append(clone);
                    })
                }
            }
        }
    })
    

    DEMO on JSFiddle

    【讨论】:

    • 你能给我举个例子吗?
    • 添加了创建具有继承范围的元素的指令示例。
    猜你喜欢
    • 2018-04-20
    • 1970-01-01
    • 1970-01-01
    • 2014-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-11
    相关资源
    最近更新 更多