简体   繁体   English

Angularjs-我们如何通过动态ng模型名称监视ngRepeat中的动态元素,并在发生任何更改时过滤元素值?

[英]Angularjs - How do we watch dynamic elements from ngRepeat, with dynamic ng-model names, and filter element values on ANY change?

I have banged my head against a cement pillow trying to figure out dynamic content. 我把头撞在水泥枕头上,试图找出动态含量。 Not just in watching or observing dynamic content, but also a dynamic directive I can practically use on any input. 我不仅可以观看或观察动态内容,而且可以在任何输入上实际使用动态指令。 Here is an example case... 这是一个示例案例...

<tbody>
  <tr ng-repeat="(rowIndex, tableRow) in paymentData.lineItems" ng-hide='false' ng-class="{ paymentrow:  markedForPay[rowIndex] == true }">
    <td>Other td's</td>

    <td class="table-row row-gray">
      <input type="text" class="someClass" placeholder='$0.00' ng-model="tableRow.amount" />
    </td>
  </tr>
</tbody>
  • We don't know each input's ng-model name, on load or at any time. 在加载时或任何时候,我们都不知道每个输入的ng模型名称。
  • We don't easily have all the inputs to sum over as the user types in each input, or deletes in each input. 当用户在每个输入中键入或在每个输入中删除时,我们不容易将所有输入进行汇总。

  • We would like to have MANY input fields, created with an ng-repeat, so sum themselves whenever a user types in ANY of the payment amount input td's. 我们希望有许多用ng-repeat创建的输入字段,因此,只要用户键入任何输入的付款金额td,就可以对它们进行汇总。

Something like "Hey table, can you give me each row's value? Also, can you store them in a fashion so that I can know what row they are associated with? Oh, and I will need the most recent values only. 像“嘿表,您能给我每一行的值吗?而且,您可以用一种方式存储它们,以便我知道它们与哪一行相关吗?哦,我将只需要最新的值。

So if the input contains .34 and the user deletes the four, I need that new change immediately, or any change to any payment amount input. 因此,如果输入中包含.34,并且用户删除了这四个,则我需要立即进行新更改,或对任何付款金额进行任何更改。

I am new to Angularjs. 我是Angularjs的新手。 If I just created something that exists in Angularjs already, please let me know with a hug. 如果我刚刚创建的东西已经存在于Angularjs中,请拥抱一下。 (I do understand there is an Angular pre-built filter for currency). (我确实知道有一个针对货币的Angular预置过滤器)。 I do not apologize for writing so much, I prefer for you to have EVERYTHING you need to implement this yourself. 对于这么多的写作,我并不表示歉意,我更希望您拥有自己实现所需的一切。 Or at least improve on the code I just provided. 或至少改善我刚刚提供的代码。

I have banged my head against a cement pillow trying to figure out dynamic content. 我把头撞在水泥枕头上,试图找出动态含量。 Not just in watching or observing dynamic content, but also a dynamic directive I can practically user on any input. 我不仅可以观看或观察动态内容,而且还可以使用动态指令来实际使用任何输入。 Here is an example case... 这是一个示例案例...

<tbody>
  <tr ng-repeat="(rowIndex, tableRow) in paymentData.lineItems" ng-hide='false' ng-class="{ paymentrow:  markedForPay[rowIndex] == true }">

    <td class="table-row row-gray">
      <input type="text" watch-dynamic-input="{ index: {{ rowIndex }}, name: 'accountsTableAmountValues', filter: 'usCurrency'}" class="someClass" placeholder='$0.00' ng-model="tableRow.amount" />
    </td>
  </tr>
</tbody>

I would like to watch any number of inputs on any change. 我希望看到任何变化的任何数量的输入。 In this example, so that I can SUM the value of each row's input under a specific header: or any input by just adding an angularjs element directive: type of 'A'. 在此示例中,这样我就可以在特定的标头下或仅通过添加angularjs元素伪指令(类型为'A')对每行输入的值求和。

I will never know the model name from the start, and each input may be embedded even further in an ngRepeat: which is the case I had, but since it's irrelevant to this example I did not include other nested ngRepeats I had in this table. 我永远不会从一开始就知道模型名称,并且每个输入都可能进一步嵌入到ngRepeat中:这就是我的情况,但是由于与本示例无关,因此我没有在此表中包含其他嵌套的ngRepeats。 In fact, all you get is just the one input for this example. 实际上,您仅获得此示例的一个输入。

At first I created just a dynamic filter for the view value. 首先,我只为视图值创建了一个动态过滤器。 I didin't have dbl curlies {{}} to use this filter type {{ money | 我没有dbl curlies {{}}使用此过滤器类型{{money | usCurrency }} as each input does not use {{}}. usCurrency}},因为每个输入都不使用{{}}。

I didn't want to cram a bunch of logic in the current directive, trying to figure out what each model name is, and then try to bind to it with a $watch function. 我不想在当前指令中塞满一堆逻辑,试图弄清楚每个模型的名称是什么,然后尝试使用$ watch函数将其绑定。 So I cam up with this first... 所以我首先想到了...

A dynamic filter directive for 'probably' any HTML element (only tested and used on form inputs). 一个动态过滤器指令,用于“可能”任何HTML元素(仅在表单输入中经过测试和使用)。

Here is the filter I was going to use just for your reference... 这是我要用作参考的过滤器...

.filter('usCurrency', [function () {
  return function (amount) {
    amount = amount.toString().replace(/\D/g, '');
    if(amount.length <= 0){
      return amount.replace(/.+/, '');
    }else{
      if(amount.length == 1){
        return amount.replace(/^/, '$ ').replace(/(\d{1})$/, '.$1');
      }else{
        return amount.replace(/^/, '$ ').replace(/(\d{2})$/, '.$1');
      }
    }
  };
}])

Here is the HTML again as stated above but only the input element for clarity... 这是如上所述的HTML,但是为了清楚起见,仅输入元素...

<input type="text" filterElement='usCurrency' class="anyClass" placeholder='$0.00' ng-model="tableRow.amount" />

Here is the dynamic directive to ONLY filter ANY input change and add newest value to ngModel, and the actual HTML view element, in this case the actual input cell. 这是动态指令,它仅过滤任何输入更改并将最新值添加到ngModel和实际的HTML视图元素(在本例中为实际的输入单元格)。 As you can see 如你看到的

app.directive('filterElement', ['$filter', function($filter){

  return {
    restrict:'A', // Declares an Attributes Directive.
    require: '?ngModel', // ? gets parent scope if it exists.

    link: function( scope, elem, attrs, ngModel ){
      if( !ngModel ){ return };

      scope.$watch(attrs.ngModel, function(value){
        if( value == undefined ){ return };
        ngModel.$setViewValue($filter( attrs.rsFilterElement )( value )); // Sets ngModelView and ngViewValue
        attrs.$$element[0].value = $filter( attrs.rsFilterElement )( value ); // I would prefer to not have to do this step. Any ideas?
      });
    }
  }
}]);

So now by just adding filterElement='yourfilternamecasesensativeasstring' to any input, you can filter any dynamic input model on any change and set the new values of ngModel and the viewed element. 因此,现在只需将filterElement ='yourfilternamecasesensativeasstring'添加到任何输入,就可以对任何动态输入模型进行任何更改过滤,并设置ngModel和viewed元素的新值。 You could take it a little further and just filter one or the other if your use case dictates changing say only the element value and not the ngModel. 如果您的用例要求更改,则仅过滤元素值而不使用ngModel,则可以更进一步,仅过滤其中一个。 <-- I have not thoroughly tested this statement, but it makes sense that ngModel.value is different than $$element[0].value as the directive requires both to make a change to the ngModel value AND the element value. <-我尚未彻底测试过此语句,但是ngModel.value与$$ element [0] .value不同是有道理的,因为该指令要求同时更改ngModel值和元素值。

However, this did not satisfy another thing I needed to do: which was to sum ALL inputs of the table on ANY or their specific inputs being changed. 但是,这并不能满足我要做的另一件事:将表的所有输入加到ANY上或更改其特定输入。 I needed a controller variable that could be accessed by multiple functions at any time. 我需要一个可以随时通过多个功能访问的控制器变量。 So I chose to do the example below, which builds upon all the above code. 因此,我选择执行以下示例,该示例基于以上所有代码。 I will provide only code with changes. 我将只提供带有更改的代码。

First create an object literal in your controller: 首先在控制器中创建对象文字:

$scope.accountsTableAmountValues = {};

This object will contain all of our HTML elements that contain our 'A' type directive. 该对象将包含所有包含“ A”类型指令的HTML元素。

Here is the HTML using the directive as well as a few items to pass in and utilize. 这是使用指令的HTML以及一些要传递和利用的项目。

<input type="text" watch-dynamic-input="{index: {{ rowIndex }}, name: 'accountsTableAmountValues', filter: 'usCurrency'}" ng-change='selected_to_pay(rowIndex)' class="table-text-field-white payment-field-font-color" placeholder='$0.00' ng-model="tableRow.amount" />

First we introduce the directive to dynamically created input(s) by adding 首先,我们通过添加将指令引入动态创建的输入

watch-dynamic-input="index: ng-repeat index number, name: this is the variable name you created in the controller, without the leading $scope, and as string, filter: this can either be a filter name as a string and case sensitive, or just NULL if you don't want to use a filter here.

We add the index of the ng-repeat first by creating {{ rowIndex }}. 我们首先通过创建{{rowIndex}}添加ng-repeat的索引。 If we used just rowIndex without curly braces, then the first index number would be created for each input. 如果仅使用不带花括号的rowIndex,则将为每个输入创建第一个索引号。 By using curly's here, rowIndex becomes each instance of ng-repeat and ads each row's index. 通过在此处使用curl,rowIndex成为ng-repeat的每个实例,并在每一行的索引上添加广告。

Then we just add in the string name of the object we created in our controller, in this case our object, if you recall was '$scope.accountsTableAmountValues = {};' 然后,我们只需添加在控制器中创建的对象的字符串名称,在本例中为对象,如果您还记得是'$ scope.accountsTableAmountValues = {};' We just need the key as a string, which becomes ...name: 'accountsTableAmountValues'. 我们只需要键作为字符串,它就变成... name:'accountsTableAmountValues'。

Now we can also filter since we are watching each input, instead of adding the first directive I showcased above: 'filterElement'. 现在,由于我们正在监视每个输入,因此我们也可以进行过滤,而不是添加上面展示的第一个指令:“ filterElement”。 We just add the filter name at the end ...filter: 'usCurrency' or just add null if you don't want to include a filter. 我们只是在过滤器的末尾添加过滤器名称... filter:'usCurrency',或者如果您不想包括过滤器,则只需添加null。

So this : 所以这 :

watch-dynamic-input="{index: {{ rowIndex }}, name: 'accountsTableAmountValues', filter: 'usCurrency'}"

uses this : 使用这个:

app.directive('watchDynamicInput', ['$filter', function($filter){

  return {
    restrict:'A', // Declares an Attributes Directive.
    require: '?ngModel', // ? makes looking for controller on parent element.

    link: function( scope, elem, attrs, ngModel ){
      if( !ngModel ){ return }; // If there is no ngModel, then forget everthing else, and just return.  This may be redundant and better off in the $watch function. Haven't tested yet.

      scope.$watch(attrs.ngModel, function(value){
      // Above, let's watch THIS ROW's ng-model="tableRow.amount"; whatever that value might be.  We don't care now because it's all dynamic, upon page render will be undefined at first, then whatever the user types or deletes.  Who cares!!!  

      if( value == undefined ){ return };  // if you are not yet defined, then return.  I'll catch you on the next input change and check again.  It's OK, we are best friends.  

      // We make a local for improved readability, and faster code.
      // Also, the below code transforms our passed in string of: 
      // "{index: {{ rowIndex }}, name: 'accountsTableAmountValues', filter: 'usCurrency'}"
      // into an actual object literal thus becomes...
      // {index: {{ rowIndex }}, name: 'accountsTableAmountValues', filter: 'usCurrency'}

      var passed_object = scope.$eval(attrs['watchDynamicInput'])

      // and now this works passed_object.index or passed_object.name

      // We allow dynamic filters, or we don't-a as a NULL can be passed into HTML || a filter name
      value = passed_object.filter ? $filter(passed_object.filter)(value) : value;

      // We set the THIS model value to new value (filtered or not, whatever: we're easy like that)
      ngModel.$setViewValue(value);

      // We have to set the element value as well or the filter amount will not display in input
      // I think I am strong arming this.  I just want the ACTUAL element value to match the ngModel.
      attrs.$$element[0].value = value; 

      // Now we just add the new value to our object literal we created in the controller.
      // It will always create a key if nothing is in the object, or it will write over the current value.  If you require ALL inputs old and new you could rewrite and use an array.

      scope[passed_object.name][passed_object.index] = value;

      // This object would now look something like {0: 'row0inputvalue', 1: 'row1inputvalue' etc...}
    });
  }
}

}]); }]);

Now I just watch my object, in this case $scope.accountsTableAmountValues, in any directive I want. 现在,我只看我想要的任何指令中的对象,在本例中为$ scope.accountsTableAmountValues。 In this case I have added the example below of what I do with this object. 在这种情况下,我在下面添加了该对象的示例。

scope.$watch('accountsTableAmountValues', function(amounts){
// You can do what you want now.  I iterate of amounts and sum the values
// and then transfer the result to my $scope.grandTotal in my controller.
// Now I have a way of ALWAYS summing the total on ANY input change to payment amount column inputs
// that were created without knowing the ng-model name or how many there may be.  

}, true); // The true setting here checks the new version to the old version. I need this for my example, but you may not.  

If you have additions please let me know. 如果您有其他添加,请告诉我。 Or improvements. 或改进。 We are always learning my friend. 我们一直在学习我的朋友。

To display the sum of all td values, set the ng-model of the total sum element to a function 要显示所有td值的总和,请将总和元素的ng-model设置为一个函数

<span id="totalSum" ng-model="calculateSum()"></span>

that iterates over the paymentData.lineItems array to sum up all of the td amounts. 遍历paymentData.lineItems数组以汇总所有td金额。

$scope.calculateSum = function()
{
   var sum = 0;
   for(var i=0; i<$scope.paymentData.lineItems.length; i++)
   {
      sum += $scope.paymentData.lineItems[i];
   }

   return sum;
}

To find each row's value, you just need to know the rowIndex. 要查找每一行的值,您只需要知道rowIndex。 Then use $scope.paymentData.lineItems[rowIndex].amount 然后使用$scope.paymentData.lineItems[rowIndex].amount

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM