bộ lọc trên mô hình ng trong một đầu vào


124

Tôi có một kiểu nhập văn bản và tôi không muốn cho phép người dùng sử dụng khoảng trắng và mọi thứ được nhập sẽ được chuyển thành chữ thường.

Tôi biết tôi không được phép sử dụng các bộ lọc trên ng-model, vd.

ng-model='tags | lowercase | no_spaces'

Tôi đã xem xét việc tạo chỉ thị của riêng mình nhưng thêm chức năng vào $parsers$formatterskhông cập nhật đầu vào, chỉ các yếu tố khác cóng-model trên đó.

Làm cách nào tôi có thể thay đổi đầu vào mà tôi hiện đang nhập?

Về cơ bản, tôi đang cố gắng tạo tính năng 'thẻ' hoạt động giống như tính năng ở đây trên StackOverflow.


Xem nếu sử dụng $ timeout (..., 0) với ng-change sẽ giúp: stackoverflow.com/questions/12176925/
Kẻ

Câu trả lời:


28

Tôi sẽ đề nghị xem giá trị mô hình và cập nhật nó theo chage: http://plnkr.co/edit/Mb0uRyIIv1eK8nTg3Qng?p=preview

Vấn đề thú vị duy nhất là với khoảng trắng: Trong AngularJS 1.0.3 ng-model trên đầu vào tự động cắt xâu chuỗi, do đó, nó không phát hiện ra mô hình đó đã bị thay đổi nếu bạn thêm khoảng trắng ở cuối hoặc khi bắt đầu (do đó khoảng trắng không bị xóa bởi tôi mã). Nhưng trong 1.1.1, có chỉ thị 'ng-trim' cho phép vô hiệu hóa chức năng này ( cam kết ). Vì vậy, tôi đã quyết định sử dụng 1.1.1 để đạt được chức năng chính xác mà bạn đã mô tả trong câu hỏi của mình.


Đây chính xác là những gì tôi đang tìm kiếm. Hóa ra tôi đã sử dụng angularjs 1.1.1
Andrew WC Brown

@Valentyn, giải pháp của bạn áp dụng cho câu hỏi SO tôi đã tham khảo trong phần bình luận ở trên. Cảm ơn. stackoverflow.com/questions/12176925/
Mark Rajcok

Giải pháp này có thể có tác dụng phụ xấu, xem câu trả lời khác bên dưới, bạn nên sử dụng một chỉ thị cho việc này
pilavdzice

Việc gán lại biến phạm vi từ bên trong $watchbuộc người nghe phải được gọi lại. Trong các trường hợp đơn giản (trong đó bộ lọc của bạn là idempotent), bạn sẽ kết thúc với bộ lọc thực thi hai lần trên mỗi sửa đổi.
hóa thân

204

Tôi tin rằng ý định của các đầu vào AngularJS và ngModeldirecive là đầu vào không hợp lệ sẽ không bao giờ kết thúc trong mô hình . Mô hình phải luôn hợp lệ. Vấn đề với việc có mô hình không hợp lệ là chúng tôi có thể có người theo dõi kích hoạt và thực hiện các hành động (không phù hợp) dựa trên mô hình không hợp lệ.

Như tôi thấy, giải pháp thích hợp ở đây là cắm vào $parsersđường ống và đảm bảo rằng đầu vào không hợp lệ không đưa nó vào mô hình. Tôi không chắc bạn đã cố gắng tiếp cận mọi thứ như thế nào hoặc chính xác điều gì không phù hợp với bạn $parsersnhưng đây là một chỉ thị đơn giản giúp giải quyết vấn đề của bạn (hoặc ít nhất là sự hiểu biết của tôi về vấn đề này):

app.directive('customValidation', function(){
   return {
     require: 'ngModel',
     link: function(scope, element, attrs, modelCtrl) {

       modelCtrl.$parsers.push(function (inputValue) {

         var transformedInput = inputValue.toLowerCase().replace(/ /g, ''); 

         if (transformedInput!=inputValue) {
           modelCtrl.$setViewValue(transformedInput);
           modelCtrl.$render();
         }         

         return transformedInput;         
       });
     }
   };
});

Ngay sau khi lệnh trên được tuyên bố, nó có thể được sử dụng như vậy:

<input ng-model="sth" ng-trim="false" custom-validation>

Như trong giải pháp được đề xuất bởi @Valentyn Shybanov, chúng ta cần sử dụng ng-trimchỉ thị nếu chúng ta muốn không cho phép khoảng trắng ở đầu / cuối của đầu vào.

Ưu điểm của phương pháp này là 2 lần:

  • Giá trị không hợp lệ không được truyền đến mô hình
  • Sử dụng một lệnh, thật dễ dàng để thêm xác thực tùy chỉnh này vào bất kỳ đầu vào nào mà không lặp lại nhiều lần người theo dõi

1
Tôi chắc chắn rằng phần khó khăn với modelCtrl.$setViewValue(transformedInput); modelCtrl.$render();Hữu ích sẽ là liên kết đến tài liệu: docs.angularjs.org/api/ng.directive:ngModel.NgModelContoder Một từ để "bảo vệ" sự hài lòng của tôi là thuộc tính phạm vi có thể được thay đổi không chỉ từ lượt xem và cách của tôi bao gồm điều này. Vì vậy, tôi nghĩ rằng nó phụ thuộc vào một tình huống thực tế làm thế nào phạm vi có thể được sửa đổi.
Valentyn Shybanov

2
'modelCtrl' đề cập đến điều gì trong ví dụ của bạn?
GSto

4
Bạn lấy inputValue từ đâu?
Dofs

2
@GSto modelCtrllà bộ điều khiển theo yêu cầu của chỉ thị. ( require 'ngModel')
Nate-Wilkins

7
Con trỏ nhảy đến cuối trường văn bản mỗi khi bạn nhập một ký tự không hợp lệ, cố gắng viết 'thế giới' và sửa đổi nó thành 'thế giới HeLLo'!
Hafez Divandari

23

Một giải pháp cho vấn đề này có thể là áp dụng các bộ lọc ở phía bộ điều khiển:

$scope.tags = $filter('lowercase')($scope.tags);

Đừng quên khai báo $filterlà phụ thuộc.


4
Nhưng bạn cần một chiếc đồng hồ $ trên nó nếu bạn muốn nó cập nhật đúng cách.
Ông Mikkél

Điều này chỉ được thực hiện một lần. và thêm vào đồng hồ không phải là giải pháp phù hợp bởi vì, ngay cả ban đầu, cho phép mô hình trở nên không hợp lệ - giải pháp chính xác là thêm vào trình phân tích cú pháp $ của mô hình.
icfantv

4
Bạn không cần phải thích câu trả lời của tôi, nhưng điều đó không có nghĩa là nó sai. Kiểm tra cái tôi của bạn trước khi bạn downvote.
icfantv

6

Nếu bạn đang sử dụng trường nhập chỉ đọc, bạn có thể sử dụng ng-value với bộ lọc.

ví dụ:

ng-value="price | number:8"

4

Sử dụng một lệnh bổ sung cho cả bộ sưu tập $ formatters và $ Parsers để đảm bảo rằng việc chuyển đổi được thực hiện theo cả hai hướng.

Xem câu trả lời khác này để biết thêm chi tiết bao gồm một liên kết đến jsfiddle.


3

Tôi đã có một vấn đề tương tự và sử dụng

ng-change="handler(objectInScope)" 

trong trình xử lý của tôi, tôi gọi một phương thức của objectInScope để sửa đổi chính xác (đầu vào thô). Trong bộ điều khiển tôi đã bắt đầu ở đâu đó

$scope.objectInScope = myObject; 

Tôi biết điều này không sử dụng bất kỳ bộ lọc hoặc trình theo dõi ưa thích nào ... nhưng nó đơn giản và hoạt động rất tốt. Mặt trái duy nhất của vấn đề này là objectInScope được gửi trong lệnh gọi tới bộ xử lý ...


1

Nếu bạn đang thực hiện xác thực đầu vào phức tạp, không đồng bộ, có thể đáng để trừu tượng hóa ng-modelmột cấp như một phần của lớp tùy chỉnh với các phương thức xác thực của chính nó.

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

html

<div>

  <label for="a">input a</label>
  <input 
    ng-class="{'is-valid': vm.store.a.isValid == true, 'is-invalid': vm.store.a.isValid == false}"
    ng-keyup="vm.store.a.validate(['isEmpty'])"
    ng-model="vm.store.a.model"
    placeholder="{{vm.store.a.isValid === false ? vm.store.a.warning : ''}}"
    id="a" />

  <label for="b">input b</label>
  <input 
    ng-class="{'is-valid': vm.store.b.isValid == true, 'is-invalid': vm.store.b.isValid == false}"
    ng-keyup="vm.store.b.validate(['isEmpty'])"
    ng-model="vm.store.b.model"
    placeholder="{{vm.store.b.isValid === false ? vm.store.b.warning : ''}}"
    id="b" />

</div>

(function() {

  const _ = window._;

  angular
    .module('app', [])
    .directive('componentLayout', layout)
    .controller('Layout', ['Validator', Layout])
    .factory('Validator', function() { return Validator; });

  /** Layout controller */

  function Layout(Validator) {
    this.store = {
      a: new Validator({title: 'input a'}),
      b: new Validator({title: 'input b'})
    };
  }

  /** layout directive */

  function layout() {
    return {
      restrict: 'EA',
      templateUrl: 'layout.html',
      controller: 'Layout',
      controllerAs: 'vm',
      bindToController: true
    };
  }

  /** Validator factory */  

  function Validator(config) {
    this.model = null;
    this.isValid = null;
    this.title = config.title;
  }

  Validator.prototype.isEmpty = function(checkName) {
    return new Promise((resolve, reject) => {
      if (/^\s+$/.test(this.model) || this.model.length === 0) {
        this.isValid = false;
        this.warning = `${this.title} cannot be empty`;
        reject(_.merge(this, {test: checkName}));
      }
      else {
        this.isValid = true;
        resolve(_.merge(this, {test: checkName}));
      }
    });
  };

  /**
   * @memberof Validator
   * @param {array} checks - array of strings, must match defined Validator class methods
   */

  Validator.prototype.validate = function(checks) {
    Promise
      .all(checks.map(check => this[check](check)))
      .then(res => { console.log('pass', res)  })
      .catch(e => { console.log('fail', e) })
  };

})();

0

Bạn có thể thử cái này

$scope.$watch('tags ',function(){

    $scope.tags = $filter('lowercase')($scope.tags);

});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.