临时局部变量的Angular指令

时间:2016-01-22 18:28:42

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

我在这里要做的是创建一个指令,允许我设置仅适用于我正在渲染的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内的内容。但是,由于这将以多种不同的方式使用,我不想为每个用例制定不同的指令。

我对此的第一次尝试非常顺利。但是,我遇到了一个问题,即从datepicker无法正确访问父作用域变量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

4 个答案:

答案 0 :(得分:0)

请尝试使用$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

答案 1 :(得分:0)

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

样品

&#13;
&#13;
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
    };
  }
]);
&#13;
<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>
&#13;
&#13;
&#13;

答案 2 :(得分:0)

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

链接到演示: 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 :(得分:0)

  

我希望获得与ng-repeat类似的功能,您不必通过transcluded元素中的父作用域来解决所有问题。

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