Angularjs 将模型传递给自定义指令-清除文本输入

Angularjs 将模型传递给自定义指令-清除文本输入,angularjs,angularjs-directive,Angularjs,Angularjs Directive,我试图实现的是相对简单的,但是我已经在这个问题上兜圈子太久了,现在是寻求帮助的时候了 基本上,我创建了一个指令,它由一个文本输入和一个清除它的链接组成 我通过一个正常工作的属性传入id,但我似乎无法确定在单击重置链接时如何传入模型以清除它 以下是我到目前为止的情况: 我认为: <text-input-with-reset input-id="the-relevant-id" input-model="the.relevant.model"/> <text-input-with

我试图实现的是相对简单的,但是我已经在这个问题上兜圈子太久了,现在是寻求帮助的时候了

基本上,我创建了一个指令,它由一个文本输入和一个清除它的链接组成

我通过一个正常工作的属性传入id,但我似乎无法确定在单击重置链接时如何传入模型以清除它

以下是我到目前为止的情况:

我认为:

<text-input-with-reset input-id="the-relevant-id" input-model="the.relevant.model"/>
<text-input-with-reset input-id="the-relevant-id" input-model="the.relevant.model"/>
我的指示:

app.directive('textInputWithReset', function() {

  return {
      restrict: 'AE',
      replace: 'true',
      template: '<div class="text-input-with-reset">' +
                  '<input ng-model="inputModel" id="input-id" type="text" class="form-control">' +
                  '<a href class="btn-reset"><span aria-hidden="true">&times;</span></a>' +
                '</div>',
      link: function(scope, elem, attrs) {
        // set ID of input for clickable labels (works)
        elem.find('input').attr('id', attrs.inputId);

        // Reset model and clear text field (not working)
        elem.find('a').bind('click', function() {
          scope[attrs.inputModel] = '';
        });
      }
  };

});
app.directive('textInputWithReset', function() {

  return {
    restrict: 'AE',
    replace: 'true',
    scope: {
      inputModel: '='
    },        
    template: '<div class="text-input-with-reset">' +
                '<input ng-model="inputModel" id="input-id" type="text" class="form-control">' +
                '<a href ng-click="inputModel = \'\'" class="btn-reset"><span aria-hidden="true">&times;</span></a>' +
              '</div>',
    link: function(scope, elem, attrs) {
      elem.find('input').attr('id', attrs.inputId);
  };

});
很明显,我遗漏了一些基本的东西-非常感谢您提供的任何帮助。

您应该调用scope。$在您的函数中重置inputModel后应用,在该函数中重置值

elem.find('a').bind('click', function() {
      scope.inputModel = '';
      scope.$apply();
    });
请阅读AngularJS中的范围

$apply用于从angular框架外部执行angular中的表达式。例如,来自浏览器DOM事件、setTimeout、XHR或第三方库。因为我们正在调用angular框架,所以我们需要执行异常处理、执行监视的适当范围生命周期

我还将inputModel属性的声明添加到指令的范围中

scope: {
    inputModel: "="
  }

但是如果你能在模板中使用ng click,使用它会更好

好的,我似乎已经通过使用指令范围和在模板中使用ng click修复了它:

我的看法是:

我的指示:

app.directive('textInputWithReset', function() {

  return {
      restrict: 'AE',
      replace: 'true',
      template: '<div class="text-input-with-reset">' +
                  '<input ng-model="inputModel" id="input-id" type="text" class="form-control">' +
                  '<a href class="btn-reset"><span aria-hidden="true">&times;</span></a>' +
                '</div>',
      link: function(scope, elem, attrs) {
        // set ID of input for clickable labels (works)
        elem.find('input').attr('id', attrs.inputId);

        // Reset model and clear text field (not working)
        elem.find('a').bind('click', function() {
          scope[attrs.inputModel] = '';
        });
      }
  };

});
app.directive('textInputWithReset', function() {

  return {
    restrict: 'AE',
    replace: 'true',
    scope: {
      inputModel: '='
    },        
    template: '<div class="text-input-with-reset">' +
                '<input ng-model="inputModel" id="input-id" type="text" class="form-control">' +
                '<a href ng-click="inputModel = \'\'" class="btn-reset"><span aria-hidden="true">&times;</span></a>' +
              '</div>',
    link: function(scope, elem, attrs) {
      elem.find('input').attr('id', attrs.inputId);
  };

});

看起来你已经回答了你的问题,但我将把我的答案留在这里,以备其他人遇到同样的问题时作进一步解释

在当前状态下,您的指令有两个错误:

app.directive('textInputWithReset', function() {

  return {
      restrict: 'AE',
      replace: 'true',
      template: '<div class="text-input-with-reset">' +
                  '<input ng-model="inputModel" id="input-id" type="text" class="form-control">' +
                  '<a href class="btn-reset"><span aria-hidden="true">&times;</span></a>' +
                '</div>',
      link: function(scope, elem, attrs) {
        // set ID of input for clickable labels (works)
        elem.find('input').attr('id', attrs.inputId);

        // Reset model and clear text field (not working)
        elem.find('a').bind('click', function() {
          scope[attrs.inputModel] = '';
        });
      }
  };

});
app.directive('textInputWithReset', function() {

  return {
    restrict: 'AE',
    replace: 'true',
    scope: {
      inputModel: '='
    },        
    template: '<div class="text-input-with-reset">' +
                '<input ng-model="inputModel" id="input-id" type="text" class="form-control">' +
                '<a href ng-click="inputModel = \'\'" class="btn-reset"><span aria-hidden="true">&times;</span></a>' +
              '</div>',
    link: function(scope, elem, attrs) {
      elem.find('input').attr('id', attrs.inputId);
  };

});
单击处理程序将在Angular的摘要循环之外触发。基本上,即使您成功地清除了模型的值,Angular也不会知道。您可以将您的逻辑封装在一个调用中以解决此问题,但在这种情况下,这不是正确的解决方案—请继续阅读

通过作用域[attrs.inputModel]访问作用域的计算结果类似于作用域['the.relevant.model']。显然,模型的名称不是字面上的.relevant.model,因为点通常意味着嵌套,而不是名称的字面部分。您需要以不同的方式引用模型

对于这样的指令,应该使用隔离作用域,请参见和。基本上,您需要修改指令,使其如下所示:

app.directive('textInputWithReset', function() {

  return {
      restrict: 'AE',
      replace: 'true',
      template: [...],

      // define an isolate scope for the directive, passing in these scope variables
      scope: {
          // scope.inputId = input-id attribute on directive
          inputId: '=inputId',
          // scope.inputModel = input-model attribute on directive
          inputModel: '=inputModel'
      },

      link: function(scope, elem, attrs) {
        // set ID of input for clickable labels (works)
        elem.find('input').attr('id', scope.inputId);

        // Reset model and clear text field (not working)
        elem.find('a').bind('click', function() {
          scope.inputModel = '';
        });
      }
  };

});
请注意,当您定义一个隔离作用域时,指令将使用请求的变量获得自己的作用域。这意味着您可以在指令中简单地使用scope.inputId和scope.inputModel,而不是试图以迂回的方式引用它们

这是未经测试的,但它应该可以做很多工作,您将需要使用范围。$apply fix我前面提到过。您可能需要测试inputId绑定,因为您现在可能需要向其传递一个文本字符串,例如,在属性中放入“input id”以指定它是一个文本字符串,而不是输入id,这意味着范围中有一个输入id变量

在你的指令生效后,让我们试着让它在角度上更有效。既然指令中有了隔离作用域,就不需要在link函数中实现自定义逻辑。每当链接函数有.click或.attr时,可能有更好的编写方法

在这种情况下,您可以通过使用更多内置角度逻辑来简化指令,而不是在link函数中手动修改DOM:

<div class="text-input-with-reset">
    <input ng-model="inputModel" id="{{ inputId }}" type="text" class="form-control">
    <a href="#" class="btn-reset" ng-click="reset()"><span aria-hidden="true">&times;</span></a>
</div>

现在,所有链接函数,或者更好的是,指令的控制器需要做的就是在作用域上定义一个重置函数。其他一切都会自动工作

如果要使用scope.apply,请向其传递一个包含逻辑的函数。不要这样空手而归,这会把它变成scope的别名。$digest.Great stuff,谢谢fakerun-我在发布此修复的同时发布了我的修复-唯一的区别是我使用了ng click-in模板,与在我的问题中的指令中使用原始事件处理程序相反。@Jay您的解决方案更好,我一直在写ng click,突然看到您的答案。%我不确定它是如何工作的,因为它似乎会受到臭名昭著的范围问题的困扰。也就是说,该指令创建自己的范围作为实际范围的子范围。读取inputModel应该可以正常工作,因为它不存在于子作用域中,所以会参考原始作用域。但是,如果试图更改我认为的值,则应该向子范围添加一个新值,而不是更新原始范围的值。换句话说,在指令之外,更改值似乎没有任何效果。我遗漏了什么?@AlvinThompson这个问题适用于子作用域,子作用域通常从父作用域继承,本质上是用自己的变量隐藏父作用域的变量。如果这些变量中有一个是基元,那么给它赋值只会改变子对象上的基元
作用域,使父作用域的原语保持不变。这就是为什么在指令绑定中使用引用对象属性而不是简单的原语很重要。只要该指令的使用引用了对象属性,就可以了。是的,您已经用隔离范围解决了它。更多细节请参见我的答案。请参见我对@Fakerun答案的评论。我不是很肯定,但使用隔离范围可能无法避免更基本的范围问题。这很好-非常感谢您的解释: