Bagaimana cara melakukan pemfilteran dua arah di AngularJS?

124

Salah satu hal menarik yang dapat dilakukan AngularJS adalah menerapkan filter ke ekspresi penyatuan data tertentu, yang merupakan cara mudah untuk menerapkan, misalnya, mata uang khusus budaya atau format tanggal properti model. Ini juga bagus untuk memiliki properti yang dihitung pada ruang lingkup. Masalahnya adalah bahwa tidak satu pun dari fitur ini bekerja dengan skenario penyatuan data dua arah - hanya penyatuan data satu arah dari ruang lingkup ke tampilan. Ini tampaknya merupakan kelalaian yang mencolok di perpustakaan yang sangat bagus - atau apakah saya melewatkan sesuatu?

Di KnockoutJS , saya bisa membuat properti hitung baca / tulis, yang memungkinkan saya menentukan sepasang fungsi, yang dipanggil untuk mendapatkan nilai properti, dan yang dipanggil saat properti disetel. Hal ini memungkinkan saya untuk mengimplementasikan, misalnya, masukan yang sadar budaya - membiarkan pengguna mengetik "$ 1.24" dan menguraikannya menjadi pelampung di ViewModel, dan memiliki perubahan dalam ViewModel yang tercermin dalam masukan.

Hal terdekat yang bisa saya temukan mirip dengan ini adalah penggunaan $scope.$watch(propertyName, functionOrNGExpression);This memungkinkan saya untuk memiliki fungsi yang dipanggil ketika properti dalam $scopeperubahan. Tapi ini tidak menyelesaikan, misalnya, masalah masukan sadar budaya. Perhatikan masalah ketika saya mencoba mengubah $watchedproperti di dalam $watchmetode itu sendiri:

$scope.$watch("property", function (newValue, oldValue) {
    $scope.outputMessage = "oldValue: " + oldValue + " newValue: " + newValue;
    $scope.property = Globalize.parseFloat(newValue);
});

( http://jsfiddle.net/gyZH8/2/ )

Elemen masukan menjadi sangat bingung saat pengguna mulai mengetik. Saya memperbaikinya dengan membagi properti menjadi dua properti, satu untuk nilai yang tidak diuraikan dan satu untuk nilai yang diuraikan:

$scope.visibleProperty= 0.0;
$scope.hiddenProperty = 0.0;
$scope.$watch("visibleProperty", function (newValue, oldValue) {
    $scope.outputMessage = "oldValue: " + oldValue + " newValue: " + newValue;
    $scope.hiddenProperty = Globalize.parseFloat(newValue);
});

( http://jsfiddle.net/XkPNv/1/ )

Ini adalah peningkatan dari versi pertama, tetapi sedikit lebih bertele-tele, dan perhatikan bahwa masih ada masalah parsedValueproperti perubahan lingkup (ketik sesuatu di masukan kedua, yang mengubah parsedValuesecara langsung. Perhatikan bahwa masukan teratas tidak memperbarui). Ini mungkin terjadi dari tindakan pengontrol atau dari memuat data dari layanan data.

Apakah ada cara yang lebih mudah untuk mengimplementasikan skenario ini menggunakan AngularJS? Apakah saya kehilangan beberapa fungsionalitas dalam dokumentasi?

Jeremy Bell
sumber

Jawaban:

231

Ternyata ada solusi yang sangat bagus untuk ini, tetapi tidak didokumentasikan dengan baik.

Nilai model pemformatan untuk tampilan dapat ditangani oleh |operator dan sudut formatter. Ternyata ngModel itu tidak hanya memiliki daftar pemformat tetapi juga daftar parser.

1. Gunakan ng-modeluntuk membuat data binding dua arah

<input type="text" ng-model="foo.bar"></input>

2. Buat direktif di modul angular Anda yang akan diterapkan ke elemen yang sama dan bergantung pada ngModel pengontrol

module.directive('lowercase', function() {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function(scope, element, attr, ngModel) {
            ...
        }
    };
});

3. Di dalam linkmetode, tambahkan konverter ubahsuaian Anda kengModel pengontrol

function fromUser(text) {
    return (text || '').toUpperCase();
}

function toUser(text) {
    return (text || '').toLowerCase();
}
ngModel.$parsers.push(fromUser);
ngModel.$formatters.push(toUser);

4. Tambahkan direktif baru Anda ke elemen yang sama yang sudah memiliki ngModel

<input type="text" lowercase ng-model="foo.bar"></input>

Ini a contoh kerja yang mengubah teks menjadi huruf kecil di inputdan kembali ke huruf besar dalam model

The API Dokumentasi untuk Controller Model juga memiliki penjelasan singkat dan gambaran metode lain yang tersedia.

phaas
sumber
APAKAH ada alasan Anda menggunakan "ngModel" sebagai nama untuk parametet keempat dalam fungsi penautan? Bukankah itu hanya pengontrol generik untuk direktif yang pada dasarnya tidak ada hubungannya dengan atribut ngModel? (Masih belajar sudut di sini jadi saya bisa salah total.)
Drew Miller
7
Karena "require: 'ngModel'", parameter ke-4 dari fungsi penautan akan menjadi pengontrol direktif ngModel - yaitu, pengontrol foo.bar, yang merupakan turunan dari ngModelController . Anda dapat memberi nama parameter ke-4 apa pun yang Anda inginkan. (Saya akan ngModelCtrl
menamainya
8
Teknik ini didokumentasikan di docs.angularjs.org/guide/forms , di bagian Validasi Kustom.
Nikhil Dabas
1
@Mark Rajcok di biola yang disediakan, sambil mengklik Muat Data - semua huruf kecil, saya berharap nilai model akan ada dalam SEMUA HURUF BESAR, tetapi nilai model kecil. Bisakah Anda tolong. jelaskan mengapa, dan bagaimana membuat model selalu DALAM HURUF BESAR
Rajkamal Subramanian
1
@rajkamal, karena loadData2 () memodifikasi $scopesecara langsung, model akan disetel ke ... hingga pengguna berinteraksi dengan kotak teks. Pada saat itu, pengurai apa pun kemudian dapat memengaruhi nilai model. Selain parser, Anda dapat menambahkan $ watch ke pengontrol Anda untuk mengubah nilai model.
Mark Rajcok