如何在页面加载完成后再次执行AngularJS指令

时间:2017-06-09 16:25:40

标签: angularjs validation dynamic angularjs-directive

请在此处查看此代码示例:

http://plnkr.co/edit/zQ049VsfrprutNYkZm3y?p=preview

此代码示例使用指令check-if-required添加ng-required属性。页面加载完成后,test_me的输入元素将根据需要进行设置。

现在假设稍后,必填字段列表已更改,如何重新运行该指令以更改已成为必需字段的ng-required属性。

塔雷克。

更新2:

经过大量的研究和试验,我意识到这个要求不切实际,不应该接近。我试图解决的问题是由于在运行时添加了验证规则,因此在IE11中加载HTML的速度很慢。

See related post here in attempt to solve the problem

Also, check the final work here

我试图修改HTML属性/指令以添加ng-reuqired等验证,然后使用$compile,因为AngularJS需要进行动态验证。

在阅读了此处添加的解决方案和评论后,我认为有更好的选择。

而不是通过添加&ng-ng-required'来修改元素HTML。指令,然后编译,我可以跳过HTML并使用相关HTML元素的ngModel.NgModelController,然后访问$validators以使用代码执行验证。如果您阅读code here,您会看到我已经为变量ngModel.NgModelController中的每个元素访问了elmModel。我认为这个变量将提供$validators的访问权限,可用于向元素添加验证。由于规则现在可以在validationList变量中使用,我将通过查找此列表并在运行中应用可用的验证来编写一个执行验证的函数。

这将是未来短跑的改进。

如果你需要这样的解决方案,你可以继续关注this post,因为我计划实施新提议的方法。

此帖可暂时关闭。

塔雷克

UPDATE1:

当我回复评论时,解决方案在我的脑海中浮现......所以,谢谢大家强迫我去考虑解决方案。

简单地说,我将在指令isFieldRequired()中创建范围函数check-if-required。此函数将接受元素ID,如果找到元素ID,则为必填字段列表listRequiredFields,否则返回true,否则返回false。此时,我可以访问元素ID,因此这里没有问题。最终,HTML将如下所示:

<input id="some_id" type="text" ng-required="isFieldRequired('some_id')">

最后,$compile服务将在修改后的HTML元素上执行。

以下是指令check-if-required的高级结构:

app.directive('checkIfRequired', ['$compile', '$timeout', '$interpolate', function ($compile, $timeout, $interpolate) {
    return {
        //JIRA: NE-2682 - trying to solve issue of interference between datepicker popup box and `$compile` service.
        priority: 100,
        terminal: true,
        require: '?^form',
        link: function (scope, el, attrs, ngForm) {
            scope.isFieldRequired = function (prmFieldName) {
                var isFound;
                isFound = scope.listRequiredFields.indexOf(prmFieldName) !== -1;
                return isFound;
            }
            var children;
            //Use timeout to give chance for inner block elements to finish rendering
            $timeout(function() {
                children = $(":input", el);
                angular.forEach(children, function(child, key) {
                    var childID = (child?child.id:"") || "";
                    if (childID.indexOf("{{") >= 0) {
                        childID = $interpolate(childID)(scope);
                    }
                    if(child && childID) {
                        angular.element(child).attr("ng-required", "isFieldRequired('" + childID + "')");
                    }
                });
                $compile(el, null, 100)(scope);
            }, 100)
        }
    };
}]);

要使用check-if-required将其添加到任何父元素,如下所示:

<div ng-form="masterForm" check-if-required>
    <label>First Name:</label><input id="firsName" type="text"><br>
    <label>Last Name:</label><input id="lastName" type="text"><br>
    <label>Age:</label><input id="age" type="number"><br>
    <label>Address:</label><input id="address" type="text"><br>
</div>

当我成功实施上述内容时,我会将其添加为解决方案。

上述解决方案的唯一缺点是即使不相关,也会为所有字段添加ng-required指令。这就是为什么我在寻找一种方法来重新应用指令check-if-required

塔雷克

2 个答案:

答案 0 :(得分:1)

首先,据我所知,你无缘无故地使用JQuery操作DOM。有时候有理由在Angular中使用JQuery,但更常见的是它表明您没有正确使用Angular。

关于手头的问题,我们并不完全清楚为什么你首先使用id。 ng-required是一个指令,可以将范围变量作为其参数,并在该范围变量更改时更新。要改变是否需要,只需将几个变量放在范围内并根据需要进行更改。

我修改了你的Plunkr以添加一个范围变量来控制字段是否是必需的。

&#13;
&#13;
(function(angular) {
  'use strict';
var app=angular.module('ui.bootstrap.demo', ['ngAnimate', 'ui.bootstrap']);
app.controller('DatepickerPopupDemoCtrl', ['$scope', function ($scope) {
  $scope.today = function() {
    $scope.dt = new Date();
    $scope.dt2 = new Date();
  };
  
  // I recommend bundling variables that just control ui to keep your scope tidy
  $scope.ui = {
    datepicker: {
      required: true
    },
    datepicker2: {
      required: false
    },
    format: {
      required: true
    }
  }
  
  $scope.today();
  $scope.clear = function() {
    $scope.dt = null;
    $scope.dt2 = null;
  };

  $scope.inlineOptions = {
    customClass: getDayClass,
    minDate: new Date(),
    showWeeks: true
  };

  $scope.dateOptions = {
    dateDisabled: disabled,
    formatYear: 'yy',
    maxDate: new Date(2020, 5, 22),
    minDate: new Date(),
    startingDay: 1
  };

  // Disable weekend selection
  function disabled(data) {
    var date = data.date,
      mode = data.mode;
    return mode === 'day' && (date.getDay() === 0 || date.getDay() === 6);
  }

  $scope.toggleMin = function() {
    $scope.inlineOptions.minDate = $scope.inlineOptions.minDate ? null : new Date();
    $scope.dateOptions.minDate = $scope.inlineOptions.minDate;
  };

  $scope.toggleMin();

  $scope.open1 = function() {
    $scope.popup1.opened = true;
  };

  $scope.open2 = function() {
    $scope.popup2.opened = true;
  };

  $scope.setDate = function(year, month, day) {
    $scope.dt = new Date(year, month, day);
    $scope.dt2 = new Date(year, month, day);
  };

  $scope.formats = ['dd-MMMM-yyyy', 'yyyy/MM/dd', 'dd.MM.yyyy', 'shortDate'];
  $scope.format = $scope.formats[0];
  $scope.altInputFormats = ['M!/d!/yyyy'];

  $scope.popup1 = {
    opened: false
  };

  $scope.popup2 = {
    opened: false
  };

  var tomorrow = new Date();
  tomorrow.setDate(tomorrow.getDate() + 1);
  var afterTomorrow = new Date();
  afterTomorrow.setDate(tomorrow.getDate() + 1);
  $scope.events = [
    {
      date: tomorrow,
      status: 'full'
    },
    {
      date: afterTomorrow,
      status: 'partially'
    }
  ];

  function getDayClass(data) {
    var date = data.date,
      mode = data.mode;
    if (mode === 'day') {
      var dayToCheck = new Date(date).setHours(0,0,0,0);

      for (var i = 0; i < $scope.events.length; i++) {
        var currentDay = new Date($scope.events[i].date).setHours(0,0,0,0);

        if (dayToCheck === currentDay) {
          return $scope.events[i].status;
        }
      }
    }

    return '';
  }
}])

})(window.angular);
&#13;
<!DOCTYPE html>
<html ng-app="ui.bootstrap.demo">

  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-animate.js"></script>
    <script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-1.3.3.js"></script>
    <script src="example.js"></script>
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
  </head>

  <body ng-controller="DatepickerPopupDemoCtrl">
    <style>
  .full button span {
    background-color: limegreen;
    border-radius: 32px;
    color: black;
  }
  .partially button span {
    background-color: orange;
    border-radius: 32px;
    color: black;
  }
  input.ng-invalid-required {
      background-color:yellow;
  }
    </style>
    <fieldset check-if-required>
      <div >
        <pre>Selected date is:           <em>{{dt | date:'fullDate' }}</em>
        </pre>
        <h4>Popup</h4>
        <div class="row">
          <div class="col-md-6">
            <p class="input-group">
              <input
                type="text"
                class="form-control"
                uib-datepicker-popup="{{format}}"
                ng-model="dt" is-open="popup1.opened"
                datepicker-options="dateOptions"
                close-text="Close"
                alt-input-formats="altInputFormats"
                ng-required="ui.datepicker.required"
              />
              <span class="input-group-btn">
                <button type="button" class="btn btn-default" ng-click="open1()">
                  <i class="glyphicon glyphicon-calendar"></i>
                </button>
              </span>
            </p>
          </div>
          <div class="col-md-6">
            <h4>Using $compile with Datepicker Popup</h4>
            <p class="input-group">
              <input
                id="test_me"
                type="text"
                class="form-control"
                uib-datepicker-popup=""
                ng-model="dt2"
                is-open="popup2.opened"
                datepicker-options="dateOptions"
                close-text="Close"
                ng-required="ui.datepicker2.required"
              />
              <span class="input-group-btn">
                <button type="button" class="btn btn-default" ng-click="open2()">
                  <i class="glyphicon glyphicon-calendar"></i>
                </button>
              </span>
            </p>
          </div>
        </div>
        <div class="row">
          <div class="col-md-6">
            <label>Format:<span class="muted-text">(manual alternate<em>{{altInputFormats[0]}}</em>
)</span>
            </label>
            <select
              class="form-control"
              ng-model="format"
              ng-options="f for f in formats"
              ng-required="ui.format.required"
            >
              <option></option>
            </select>
          </div>
        </div>
        <div class="row">
          <div class="col-md-6">
            <label>Test ng-init after using terminal = true or flase:
            </label>
            <input class="form-control" ng-model="testTerminal" ng-init="testTerminal = 'It is working'">
            <span>Test terminal=true using interpolation: {{testTerminal}} </span><br>
            <span>Comment out the line 'terminal: true' to check the result</span>
          </div>
        </div>
        <hr />
        <button type="button" class="btn btn-sm btn-info" ng-click="today()">Today</button>
        <button type="button" class="btn btn-sm btn-default" ng-click="setDate(2009, 7, 24)">2009-08-24</button>
        <button type="button" class="btn btn-sm btn-danger" ng-click="clear()">Clear</button>
        <button type="button" class="btn btn-sm btn-default" ng-click="toggleMin()" uib-tooltip="After today restriction">Min date</button>
      </div>
    </fieldset>
  </body>

</html>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

根据需要创建所需的输入字段,然后使用directive重新编译$compile。这是工作plunker

在plunker中,我添加了最后一个按钮,以输入id为make_require的输入字段。当有人点击该按钮ng-required属性时,会为该输入添加值true

  <button type="button" class="btn btn-sm btn-info" ng-click="today()">Today</button>
    <button type="button" class="btn btn-sm btn-default" ng-click="setDate(2009, 7, 24)">2009-08-24</button>
    <button type="button" class="btn btn-sm btn-danger" ng-click="clear()">Clear</button>
    <button type="button" class="btn btn-sm btn-default" ng-click="toggleMin()" uib-tooltip="After today restriction">Min date</button>
    <button type="button" class="btn btn-sm btn-primary" ng-click="makeRequired()">Make input Required</button>

在控制器中添加了这个js代码

  $scope.makeRequired = function() {
    $("#make_require").attr('ng-required', 'true');
    $compile(angular.element($("fieldset")))($scope)  //compile the directive again
  }