memfilter ng-model di input

124

Saya memiliki input teks dan saya tidak ingin mengizinkan pengguna menggunakan spasi, dan semua yang diketik akan diubah menjadi huruf kecil.

Saya tahu saya tidak diizinkan menggunakan filter pada ng-model misalnya.

ng-model='tags | lowercase | no_spaces'

Saya melihat membuat direktif saya sendiri tetapi menambahkan fungsi ke $parsersdan $formatterstidak memperbarui input, hanya elemen lain yang ada ng-modeldi dalamnya.

Bagaimana cara mengubah masukan yang saat ini saya ketik?

Pada dasarnya saya mencoba membuat fitur 'tag' yang berfungsi seperti yang ada di StackOverflow.

Andrew WC Brown
sumber
Lihat apakah menggunakan $ timeout (..., 0) dengan bantuan ng-change: stackoverflow.com/questions/12176925/…
Mark Rajcok

Jawaban:

28

Saya akan menyarankan untuk melihat nilai model dan memperbaruinya setelah chage: http://plnkr.co/edit/Mb0uRyIIv1eK8nTg3Qng?p=preview

Satu-satunya masalah yang menarik adalah dengan spasi: Dalam AngularJS 1.0.3 ng-model pada input secara otomatis memotong string, jadi itu tidak mendeteksi bahwa model diubah jika Anda menambahkan spasi di akhir atau di awal (jadi spasi tidak secara otomatis dihapus oleh saya. kode). Tetapi di 1.1.1 ada direktif 'ng-trim' yang memungkinkan untuk menonaktifkan fungsionalitas ini ( komit ). Jadi saya telah memutuskan untuk menggunakan 1.1.1 untuk mencapai fungsionalitas persis seperti yang Anda jelaskan dalam pertanyaan Anda.

Valentyn Shybanov
sumber
Inilah yang saya cari. Ternyata saya sudah menggunakan angularjs 1.1.1
Andrew WC Brown
@Valentyn, solusi Anda diterapkan pada pertanyaan SO yang saya referensikan dalam komentar di atas. Terima kasih. stackoverflow.com/questions/12176925/…
Mark Rajcok
solusi ini dapat memiliki efek samping yang buruk, lihat jawaban lain di bawah ini, Anda harus menggunakan petunjuk untuk ini
pilavdzice
Menetapkan ulang variabel lingkup dari dalam $watchmemaksa pendengar dipanggil lagi. Dalam kasus sederhana (di mana filter Anda idempoten), Anda akan berakhir dengan filter yang dijalankan dua kali pada setiap modifikasi.
inkarnasi pada
204

Saya percaya bahwa maksud dari masukan AngularJS dan ngModelarahannya adalah bahwa masukan yang tidak valid tidak boleh berakhir di model . Model harus selalu valid. Masalah dengan model yang tidak valid adalah bahwa kami mungkin memiliki pengamat yang menembak dan mengambil tindakan (tidak pantas) berdasarkan model yang tidak valid.

Seperti yang saya lihat, solusi yang tepat di sini adalah menyambungkan ke $parserspipa dan memastikan bahwa masukan yang tidak valid tidak masuk ke dalam model. Saya tidak yakin bagaimana Anda mencoba mendekati sesuatu atau apa yang sebenarnya tidak berhasil untuk Anda, $parserstetapi berikut adalah arahan sederhana yang menyelesaikan masalah Anda (atau setidaknya pemahaman saya tentang masalah tersebut):

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

Segera setelah direktif di atas dideklarasikan, dapat digunakan seperti ini:

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

Seperti dalam solusi yang diusulkan oleh @Valentyn Shybanov, kita perlu menggunakan ng-trimarahan jika kita ingin melarang spasi di awal / akhir input.

Keuntungan dari pendekatan ini adalah 2 kali lipat:

  • Nilai yang tidak valid tidak disebarkan ke model
  • Menggunakan arahan, mudah untuk menambahkan validasi kustom ini ke masukan apa pun tanpa menduplikasi pengamat berulang kali
pkozlowski.opensource
sumber
1
Saya yakin bahwa bagian rumit dengan modelCtrl.$setViewValue(transformedInput); modelCtrl.$render();Useful akan ditautkan ke dokumentasi: docs.angularjs.org/api/ng.directive:ngModel.NgModelController Satu kata untuk "melindungi" solisi saya adalah bahwa properti cakupan dapat diubah tidak hanya dari tampilan dan cara saya menutupi ini. Jadi saya pikir itu tergantung pada situasi aktual bagaimana ruang lingkup dapat dimodifikasi.
Valentyn Shybanov
2
apa yang dimaksud dengan 'modelCtrl' dalam contoh Anda?
GSto
4
Dari mana Anda mendapatkan inputValue?
Dofs
2
@GSto modelCtrladalah pengontrol yang dibutuhkan oleh direktif. ( require 'ngModel')
Nate-Wilkins
7
Kursor melompat ke akhir bidang teks setiap kali Anda mengetik karakter yang tidak valid, coba tulis 'dunia' dan ubah menjadi 'dunia HeLLo'!
Hafez Divandari
23

Solusi untuk masalah ini adalah dengan menerapkan filter pada sisi pengontrol:

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

Jangan lupa untuk mendeklarasikan $filtersebagai ketergantungan.

Pierre-Yves Le Dévéhat
sumber
4
Tetapi Anda memerlukan jam tangan $ di atasnya jika Anda ingin memperbarui dengan benar.
Tuan Mikkél
ini hanya dilakukan sekali. dan menambahkan ke jam tangan bukanlah solusi yang tepat karena, bahkan pada awalnya, memungkinkan model menjadi tidak valid - solusi yang tepat adalah menambahkan $ parsers model.
icfantv
4
Anda tidak harus menyukai jawaban saya, tetapi itu tidak berarti itu salah. Periksa ego Anda sebelum Anda memberi suara negatif.
icfantv
6

Jika Anda menggunakan bidang input hanya baca, Anda dapat menggunakan nilai-ng dengan filter.

sebagai contoh:

ng-value="price | number:8"
Edward D. Wilson
sumber
4

Gunakan arahan yang menambahkan koleksi $ formatters dan $ parsers untuk memastikan bahwa transformasi dilakukan di kedua arah.

Lihat jawaban lain ini untuk lebih jelasnya termasuk tautan ke jsfiddle.

Scott Munro
sumber
3

Saya memiliki masalah yang sama dan terbiasa

ng-change="handler(objectInScope)" 

di handler saya, saya memanggil metode objectInScope untuk memodifikasi dirinya sendiri dengan benar (input kasar). Dalam kontroler saya telah memulai suatu tempat itu

$scope.objectInScope = myObject; 

Saya tahu ini tidak menggunakan filter atau pengamat mewah ... tapi sederhana dan bekerja dengan baik. Satu-satunya kekurangan ini adalah objectInScope dikirim dalam panggilan ke handler ...

wojjas
sumber
1

Jika Anda melakukan validasi input asinkron yang kompleks, mungkin ada baiknya untuk mengabstraksi ng-modelsatu level sebagai bagian dari kelas kustom dengan metode validasinya sendiri.

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>

kode

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

})();
Daniel Lizik
sumber
0

Anda bisa mencobanya

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

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

});
Nikhil Mahirrao
sumber