ตัวกรอง ng-model ในอินพุต


124

ฉันมีการป้อนข้อความและไม่ต้องการอนุญาตให้ผู้ใช้ใช้ช่องว่างและทุกอย่างที่พิมพ์จะกลายเป็นตัวพิมพ์เล็ก

ฉันรู้ว่าฉันไม่ได้รับอนุญาตให้ใช้ฟิลเตอร์กับ ng-model เช่น

ng-model='tags | lowercase | no_spaces'

ฉันมองไปที่การสร้างคำสั่งของตัวเอง แต่เพิ่มฟังก์ชันเข้าไป $parsersและ$formattersไม่ได้อัปเดตอินพุตมีเพียงองค์ประกอบอื่น ๆ ที่มีng-modelอยู่

ฉันจะเปลี่ยนข้อมูลที่ฉันกำลังพิมพ์อยู่ได้อย่างไร

โดยพื้นฐานแล้วฉันกำลังพยายามสร้างคุณสมบัติ 'แท็ก' ที่ใช้งานได้เหมือนกับที่นี่ใน StackOverflow


ดูว่าใช้ $ timeout (... , 0) กับ ng-change
help หรือไม่

คำตอบ:


28

ฉันขอแนะนำให้ดูค่าโมเดลและอัปเดตเมื่อ chage: http://plnkr.co/edit/Mb0uRyIIv1eK8nTg3Qng?p=preview

ปัญหาที่น่าสนใจเพียงอย่างเดียวคือการเว้นวรรค: ใน AngularJS 1.0.3 ng-model ที่อินพุตจะตัดสตริงโดยอัตโนมัติดังนั้นจึงไม่พบว่าโมเดลนั้นมีการเปลี่ยนแปลงหากคุณเพิ่มช่องว่างในตอนท้ายหรือเมื่อเริ่มต้น (ดังนั้นฉันจะไม่ลบช่องว่างโดยอัตโนมัติ รหัส). แต่ใน 1.1.1 มีคำสั่ง 'ng-trim' ที่อนุญาตให้ปิดการใช้งานฟังก์ชันนี้ ( กระทำ ) ดังนั้นฉันจึงตัดสินใจใช้ 1.1.1 เพื่อให้ได้ฟังก์ชันที่ตรงตามที่คุณอธิบายไว้ในคำถาม


นี่คือสิ่งที่ฉันกำลังมองหา ปรากฎว่าฉันใช้ angularjs 1.1.1 อยู่แล้ว
Andrew WC Brown

@Valentyn วิธีแก้ปัญหาของคุณใช้กับคำถาม SO ที่ฉันอ้างถึงในความคิดเห็นด้านบน ขอบคุณ stackoverflow.com/questions/12176925/…
Mark Rajcok

การแก้ปัญหานี้อาจมีผลข้างเคียงที่ไม่ดีดูคำตอบอื่น ๆ ด้านล่างคุณควรใช้คำสั่งนี้
pilavdzice

การกำหนดตัวแปรขอบเขตใหม่จากภายใน$watchบังคับให้ผู้ฟังถูกเรียกใช้อีกครั้ง ในกรณีง่ายๆ (ที่ตัวกรองของคุณไม่ได้ใช้งาน) คุณจะพบว่าตัวกรองทำงานสองครั้งในทุกการแก้ไข
BorisOkunskiy

204

ผมเชื่อว่าความตั้งใจของปัจจัยการผลิตและ AngularJS ที่ngModeldirecive คือการป้อนข้อมูลที่ไม่ถูกต้องไม่ควรจะจบลงในรูปแบบ โมเดลควรจะถูกต้องเสมอ ปัญหาในการมีโมเดลที่ไม่ถูกต้องคือเราอาจมีผู้เฝ้าดูที่เริ่มการทำงานและดำเนินการ (ไม่เหมาะสม) โดยอิงจากโมเดลที่ไม่ถูกต้อง

อย่างที่ฉันเห็นวิธีแก้ปัญหาที่เหมาะสมที่นี่คือการเสียบเข้ากับ$parsersท่อและตรวจสอบให้แน่ใจว่าอินพุตที่ไม่ถูกต้องจะไม่ทำให้เข้ากับโมเดล ฉันไม่แน่ใจว่าคุณพยายามเข้าหาสิ่งต่าง ๆ อย่างไรหรืออะไรที่ไม่ได้ผลสำหรับคุณจริงๆ$parsersแต่นี่เป็นคำสั่งง่ายๆที่ช่วยแก้ปัญหาของคุณ (หรืออย่างน้อยก็ความเข้าใจของฉันเกี่ยวกับปัญหา):

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;         
       });
     }
   };
});

ทันทีที่มีการประกาศคำสั่งข้างต้นก็สามารถใช้ได้ดังนี้:

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

เช่นเดียวกับในโซลูชันที่เสนอโดย @Valentyn Shybanov เราจำเป็นต้องใช้ng-trimคำสั่งหากเราต้องการไม่อนุญาตให้มีช่องว่างที่จุดเริ่มต้น / จุดสิ้นสุดของอินพุต

ข้อดีของแนวทางนี้คือ 2 เท่า:

  • ค่าที่ไม่ถูกต้องจะไม่แพร่กระจายไปยังโมเดล
  • การใช้คำสั่งทำให้ง่ายต่อการเพิ่มการตรวจสอบความถูกต้องที่กำหนดเองนี้ลงในอินพุตใด ๆ โดยไม่ต้องทำซ้ำผู้เฝ้าดูซ้ำแล้วซ้ำเล่า

1
ฉันแน่ใจว่าส่วนที่ยุ่งยากอยู่กับmodelCtrl.$setViewValue(transformedInput); modelCtrl.$render();Useful จะเป็นลิงค์ไปยังเอกสาร: docs.angularjs.org/api/ng.directive:ngModel.NgModelController คำเดียวในการ "ป้องกัน" การแก้ปัญหาของฉันคือคุณสมบัติขอบเขตสามารถเปลี่ยนแปลงได้ไม่เพียง แต่จากมุมมองและ วิธีของฉันครอบคลุมสิ่งนี้ ดังนั้นฉันคิดว่ามันขึ้นอยู่กับสถานการณ์จริงว่าจะแก้ไขขอบเขตได้อย่างไร
Valentyn Shybanov

2
'modelCtrl' หมายถึงอะไรในตัวอย่างของคุณ
GS ถึง

4
คุณได้รับ inputValue จากที่ไหน?
Dofs

2
@GSto modelCtrlเป็นตัวควบคุมที่กำหนดโดยคำสั่ง ( require 'ngModel')
Nate-Wilkins

7
เคอร์เซอร์จะกระโดดไปที่ส่วนท้ายของฟิลด์ข้อความทุกครั้งที่คุณพิมพ์อักขระที่ไม่ถูกต้องพยายามเขียน "โลก" และแก้ไขเป็น "โลกของ HeLLo"!
Hafez Divandari

23

วิธีแก้ปัญหานี้คือการใช้ตัวกรองด้านคอนโทรลเลอร์:

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

อย่าลืมประกาศ$filterเป็นเมืองขึ้น


4
แต่คุณต้องมี $ watch หากคุณต้องการให้อัปเดตอย่างถูกต้อง
Mr Mikkél

จะดำเนินการเพียงครั้งเดียว และการเพิ่มลงในนาฬิกาไม่ใช่วิธีแก้ปัญหาที่ถูกต้องเพราะแม้ในตอนแรกจะทำให้แบบจำลองไม่ถูกต้อง - วิธีแก้ปัญหาที่ถูกต้องคือการเพิ่ม $ parsers ของโมเดล
icfantv

4
คุณไม่จำเป็นต้องชอบคำตอบของฉัน แต่ไม่ได้หมายความว่ามันผิด ตรวจสอบอัตตาของคุณก่อนที่คุณจะลงคะแนน
icfantv


4

ใช้คำสั่งที่เพิ่มให้กับทั้งคอลเลกชัน $ formatters และ $ parsers เพื่อให้แน่ใจว่าการแปลงจะดำเนินการในทั้งสองทิศทาง

ดูคำตอบอื่นสำหรับรายละเอียดเพิ่มเติมรวมถึงลิงก์ไปยัง jsfiddle


3

ฉันมีปัญหาที่คล้ายกันและใช้

ng-change="handler(objectInScope)" 

ในตัวจัดการของฉันฉันเรียกวิธีการของ objectInScope เพื่อแก้ไขตัวเองอย่างถูกต้อง (อินพุตหยาบ) ในคอนโทรลเลอร์ฉันได้เริ่มต้นที่ไหนสักแห่ง

$scope.objectInScope = myObject; 

ฉันรู้ว่านี่ไม่ได้ใช้ฟิลเตอร์หรือนักดูแฟนซีใด ๆ ... แต่มันเรียบง่ายและใช้งานได้ดี ข้อเสียอย่างเดียวคือ objectInScope ถูกส่งในการเรียกไปยังตัวจัดการ ...


1

หากคุณกำลังทำการตรวจสอบความถูกต้องของอินพุตแบบ async ที่ซับซ้อนอาจเป็นการคุ้มค่าที่จะทำให้เป็นนามธรรมng-modelขึ้นหนึ่งระดับโดยเป็นส่วนหนึ่งของคลาสที่กำหนดเองด้วยวิธีการตรวจสอบความถูกต้องของมันเอง

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) })
  };

})();

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.