Model sudut dari model nilai default

126

Katakanlah Anda memiliki formulir yang memuat nilai dari basis data. Bagaimana Anda menginisialisasi model-ng?

Contoh:

<input name="card[description]" ng-model="card.description" value="Visa-4242">

Di controller saya, $ scope.card awalnya tidak terdefinisi. Apakah ada cara selain melakukan sesuatu seperti ini?

$scope.card = {
  description: $('myinput').val()
}
Calvin Froedge
sumber

Jawaban:

136

Ini adalah kesalahan umum dalam aplikasi Angular baru. Anda tidak ingin menulis nilai-nilai Anda ke dalam HTML di server jika Anda dapat menghindarinya. Jika faktanya, jika Anda bisa mendapatkan server Anda membuat HTML seluruhnya, semuanya lebih baik.

Idealnya, Anda ingin mengirimkan template HTML Sudut Anda, lalu tarik nilai-nilai Anda melalui $ http di JSON dan letakkan di lingkup Anda.

Jadi jika memungkinkan, lakukan ini:

app.controller('MyController', function($scope, $http) {
    $http.get('/getCardInfo.php', function(data) {
       $scope.card = data;
    });
});

<input type="text" ng-model="card.description" />

Jika Anda benar-benar HARUS merender nilai-nilai Anda ke dalam HTML dari server Anda, Anda bisa meletakkannya dalam variabel global dan mengaksesnya dengan $ window:

Di tajuk halaman Anda akan menulis:

<head>
   <script>
       window.card = { description: 'foo' };
   </script>
</head>

Dan kemudian di controller Anda, Anda akan mendapatkannya seperti ini:

app.controller('MyController', function($scope, $window) {
   $scope.card = $window.card;
});

Saya harap itu membantu.

Ben Lesh
sumber
4
Ya, itu membantu. Saya kira saya hanya terkejut bahwa orang-orang Angular membuat keputusan ini.
Calvin Froedge
125
Jangan kaget ... rendering HTML berpindah dari server ke browser. Ada puluhan kerangka kerja MVC dalam JavaScript saat ini, dan jauh lebih efisien untuk server hanya untuk meng-host data JSON / XML ke aplikasi JavaScript daripada membuat setiap halaman di server. Ini mengimbangi banyak pekerjaan ke mesin klien daripada meminta server menerima pukulan. Belum lagi menghemat bandwidth. Selain itu, Anda dapat memiliki aplikasi seluler asli (atau apa pun juga) yang menggunakan JSON yang sama melalui HTTP. Inilah masa depan.
Ben Lesh
3
@blesh: Jawaban yang bagus. Terima kasih banyak. Saya setuju bahwa ini adalah jalan ke depan dan sudah mulai mengadopsi pendekatan ini sendiri; apakah Anda memiliki tautan yang mendukung klaim ini? Saya juga khawatir bahwa memindahkan rendering html dari server ke klien dapat mengakibatkan waktu buka halaman yang lebih lambat, terutama di perangkat seluler tempat Anda dapat merender banyak HTML misalnya untuk pohon navigasi.
GFoley83
19
Saya tidak setuju bahwa ini adalah 'kesalahan.' Dalam beberapa kasus, rendering sisi klien adalah solusi terbaik. Di sisi lain, rendering sisi server adalah caranya. Anda dapat menggunakan sudut dengan kedua teknik, dan rendering sisi klien tidak boleh dianggap sebagai "cara sudut," karena tidak. Angular lebih fleksibel dari itu.
hunterloftis
8
@blesh, tentu saja - setiap contoh di mana SEO itu penting tetapi biaya konten alternatif untuk perayap lebih tinggi daripada biaya penerapan sudut dengan data yang disematkan - aplikasi apa pun yang memberikan nilai yang sangat tinggi pada kecepatan yang dirasakan atau waktu yang dibuat untuk pengalaman pengguna - banyak situs konten dan situs e-commerce akan termasuk dalam salah satu kategori ini. Para insinyur di twitter, yang mencoba rendering klien kemudian pindah kembali ke server untuk memberikan pengalaman pengguna yang lebih baik, telah menguraikan alasan mereka juga: blog.twitter.com/2012/improving-performance-twittercom
hunterloftis
236

Jika Anda tidak dapat mengerjakan ulang aplikasi Anda untuk melakukan apa yang disarankan @blesh (tarik data JSON turun dengan $ http atau $ resource dan isi $ scope), Anda bisa menggunakan ng-init sebagai gantinya:

<input name="card[description]" ng-model="card.description" ng-init="card.description='Visa-4242'">

Lihat juga AngularJS - Atribut nilai pada kotak teks input diabaikan ketika ada model-ng yang digunakan?

Mark Rajcok
sumber
+1 untuk sudut jalan (dari praktik yang kurang disukai). Contoh: jsfiddle.net/9ymB3
John Lehmann
2
Saya menggunakan Angular dengan formulir web C # & Saya menemukan bahwa menggunakan ng-initcukup berguna ketika menetapkan nilai dari kode-balik / postback misalnya <input name="phone" data-ng-model="frm.phone" data-ng-init="frm.phone= '<%: Model.Phone %>'" data-ng-pattern="/^[0-9() \-+]+$/" type="tel" required />. Agak jelek? Ya, tetapi apakah trik & mengatasi sakit kepala integrasi dalam proses.
GFoley83
13
+1, elegan! Orang menganjurkan perilaku cepat dan cepat, namun begitu banyak di sini di SO mengatakan "lakukan itu semua sisi klien.", Seperti jutaan panggilan http baik untuk situs tajam. Memasukkan sisi server data ke dalam templat memungkinkan strategi caching. Statis, atau dinamis / parsial dengan Memcached. Inilah yang dilakukan twitter. Terkadang bermanfaat, kadang tidak. Ingin menekankan itu. Baru saja menambahkan itu, bukan berarti Anda mengatakan sebaliknya, @Mark.
oma
1
Sebenarnya saya mengambil kembali, ini bekerja paling baik untuk saya, luar biasa: stackoverflow.com/a/20522955/329367
Darren
1
FWIW: Saya tidak suka penugasan variabel dalam ekspresi template karena tidak dapat diuji.
Ben Lesh
60

Ini jelas kurang, tetapi perbaikan yang mudah ditambahkan untuk AngularJS. Cukup tulis arahan cepat untuk mengatur nilai model dari bidang input.

<input name="card[description]" value="Visa-4242" ng-model="card.description" ng-initial>

Ini versi saya:

var app = angular.module('forms', []);

app.directive('ngInitial', function() {
  return {
    restrict: 'A',
    controller: [
      '$scope', '$element', '$attrs', '$parse', function($scope, $element, $attrs, $parse) {
        var getter, setter, val;
        val = $attrs.ngInitial || $attrs.value;
        getter = $parse($attrs.ngModel);
        setter = getter.assign;
        setter($scope, val);
      }
    ]
  };
});
Kevin Stone
sumber
Arahan cepat yang bagus. Ada alasan bagus untuk tidak mengemas ini menjadi model-ng sehingga nilai = dan model-ng = bisa bekerja sama seperti yang diharapkan kebanyakan orang?
hunterloftis
Cukup bagus! Ini bermanfaat.
santiagobasulto
3
Jawaban bagus! Saya baru saja menambahkan "perbaikan" untuk membuat ini juga berfungsi dengan textareas juga - gist.github.com/rmontgomery429/6191275
Ryan Montgomery
mengapa Anda menetapkan controlleruntuk arahan Anda, dan mengapa tidak menggunakan linkmetode ini ?. Saya pemula untuk angularjs
ebram khalil
1
perlu diubah agar val = $attrs.ngInitial || $attrs.value || $element.val()bisa berfungsi dengan elemen tertentu.
fjsj
12

IMHO solusi terbaik adalah arahan @Kevin Stone, tetapi saya harus memutakhirkannya agar berfungsi di setiap kondisi (pilih, textarea), dan yang ini pasti bekerja:

    angular.module('app').directive('ngInitial', function($parse) {
        return {
            restrict: "A",
            compile: function($element, $attrs) {
                var initialValue = $attrs.value || $element.val();
                return {
                    pre: function($scope, $element, $attrs) {
                        $parse($attrs.ngModel).assign($scope, initialValue);
                    }
                }
            }
        }
    });
jtompl
sumber
Dan bagaimana dengan pilih?
jwize
7

Anda dapat menggunakan arahan khusus (dengan dukungan untuk textarea, pilih, radio dan kotak centang), periksa posting blog ini https://glaucocustodio.github.io/2014/10/20/init-ng-model-from-form- bidang-atribut / .

pengguna1519240
sumber
1
Ini adalah pos yang bagus. Jawaban favorit saya karena pertanyaannya adalah mengatur nilai input sesuai dengan nilai inisial.
RPDeshaies
Saya membuat Plnkr untuk menguji kode ini dan berfungsi dengan baik: plnkr.co/edit/ZTFOAc2ZGIZr6HjsB5gH?p=preview
RPDeshaies
6

Anda juga dapat menggunakan dalam kode HTML Anda: ng-init="card.description = 12345"

Ini tidak direkomendasikan oleh Angular, dan seperti yang disebutkan di atas Anda harus menggunakan controller Anda secara eksklusif.

Tapi itu berhasil :)

koxon
sumber
4

Saya memiliki pendekatan sederhana, karena saya memiliki beberapa validasi dan topeng berat dalam formulir saya. Jadi, saya menggunakan jquery untuk mendapatkan nilai saya lagi dan menjalankan acara "ubah" ke validasi:

$('#myidelement').val('123');
$('#myidelement').trigger( "change");
Guilherme Redmer Machado
sumber
3

Seperti yang ditunjukkan orang lain, bukan praktik yang baik untuk menginisialisasi data pada tampilan. Akan tetapi, menginisialisasi data tentang Pengendali. (lihat http://docs.angularjs.org/guide/controller )

Jadi kamu bisa menulis

<input name="card[description]" ng-model="card.description">

dan

$scope.card = { description: 'Visa-4242' };

$http.get('/getCardInfo.php', function(data) {
   $scope.card = data;
});

Dengan cara ini pandangan tidak mengandung data, dan pengontrol menginisialisasi nilai saat nilai nyata sedang dimuat.

Jkarttunen
sumber
3

Jika Anda menyukai pendekatan Kevin Stone di atas https://stackoverflow.com/a/17823590/584761 pertimbangkan pendekatan yang lebih mudah dengan menulis arahan untuk tag tertentu seperti 'input'.

app.directive('input', function ($parse) {
    return {
        restrict: 'E',
        require: '?ngModel',
        link: function (scope, element, attrs) {
            if (attrs.ngModel) {
                val = attrs.value || element.text();
                $parse(attrs.ngModel).assign(scope, val);
            }
        }
    }; });

Jika Anda memilih rute ini, Anda tidak perlu khawatir tentang menambahkan-inisial ke setiap tag. Secara otomatis menetapkan nilai model ke atribut nilai tag. Jika Anda tidak menetapkan atribut nilai, maka nilai tersebut akan default ke string kosong.

pembuat kode
sumber
3

Berikut adalah pendekatan server-centric:

<html ng-app="project">
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
    <script>
        // Create your module
        var dependencies = [];
        var app = angular.module('project', dependencies);

        // Create a 'defaults' service
        app.value("defaults", /* your server-side JSON here */);

        // Create a controller that uses the service
        app.controller('PageController', function(defaults, $scope) {
            // Populate your model with the service
            $scope.card = defaults;
        });
    </script>

    <body>
        <div ng-controller="PageController">
            <!-- Bind with the standard ng-model approach -->
            <input type="text" ng-model="card.description">
        </div>
    </body>
</html>

Ini adalah ide dasar yang sama dengan jawaban yang lebih populer pada pertanyaan ini, kecuali $ supply.value mendaftarkan layanan yang berisi nilai default Anda.

Jadi, di server, Anda dapat memiliki sesuatu seperti:

{
    description: "Visa-4242"
}

Dan memasukkannya ke halaman Anda melalui teknologi sisi server pilihan Anda. Inilah Intisari: https://gist.github.com/exclsr/c8c391d16319b2d31a43

exclsr
sumber
1
Sejauh yang saya tahu, ini adalah metode yang paling Angular untuk menangani masalah jika membuat permintaan $ http awal bukanlah suatu pilihan. Ini juga memiliki keuntungan menjadi lebih mudah untuk dimodifikasi jika Anda ingin menukar ke $ http nanti. Salah satu peningkatan yang mungkin dilakukan adalah mengembalikan janji alih-alih membuat pergantian itu lebih mudah. Saya akan sangat jauh dari pengaturan global vars atau menggunakan ng-inisial di sini.
richard
1

Yang ini adalah versi yang lebih umum dari ide-ide yang disebutkan di atas ... Ini hanya memeriksa apakah ada nilai dalam model, dan jika tidak, itu menetapkan nilai ke model.

JS:

function defaultValueDirective() {
    return {
        restrict: 'A',
        controller: [
            '$scope', '$attrs', '$parse',
            function ($scope, $attrs, $parse) {
                var getter = $parse($attrs.ngModel);
                var setter = getter.assign;
                var value = getter();
                if (value === undefined || value === null) {
                    var defaultValueGetter = $parse($attrs.defaultValue);
                    setter($scope, defaultValueGetter());
                }
            }
        ]
    }
}

HTML (contoh penggunaan):

<select class="form-control"
        ng-options="v for (i, v) in compressionMethods"
        ng-model="action.parameters.Method"
        default-value="'LZMA2'"></select>
Eitan HS
sumber
Ini bekerja untuk saya, tetapi hanya setelah melewati $scopepanggilan untuk getter()- berharap ini jelas bagi orang lain yang mencoba ini!
zesda
0

Saya mencoba apa yang disarankan oleh @Mark Rajcok. Ini berfungsi untuk nilai-nilai String (Visa-4242). Silakan merujuk biola ini .

Dari biola:

Hal yang sama yang dilakukan dalam biola dapat dilakukan menggunakan ng-repeat, yang semua orang bisa merekomendasikan. Tetapi setelah membaca jawaban yang diberikan oleh @Mark Rajcok, saya hanya ingin mencoba yang sama untuk formulir dengan berbagai profil. Semuanya berjalan dengan baik sampai saya memiliki $ scope.profiles = [{}, {}]; kode dalam pengontrol. Jika saya menghapus kode ini, saya mendapat kesalahan. Tetapi dalam skenario normal saya tidak dapat mencetak $scope.profiles = [{},{}]; karena saya mencetak atau gema html dari server. Apakah mungkin untuk mengeksekusi di atas, dengan cara yang sama seperti @Mark Rajcok lakukan untuk nilai-nilai string seperti <input name="card[description]" ng-model="card.description" ng-init="card.description='Visa-4242'">, tanpa harus menggemakan bagian JavaScript dari server.

Rajkamal Subramanian
sumber
1
Anda dapat menggunakan ng-init untuk menginisialisasi array dan kemudian menggunakan ng-repeat untuk menampilkan garis bentuk: jsfiddle.net/6tP6x/1
Karen Zilles
0

Baru saja menambahkan dukungan untuk elemen pilih ke Ryan Montgomery "fix"

<select class="input-control" ng-model="regCompModel.numberOfEmployeeId" ng-initial>
    <option value="1af38656-a752-4a98-a827-004a0767a52d"> More than 500</option>
    <option value="233a2783-db42-4fdb-b191-0f97d2d9fd43"> Between 250 and 500</option>
    <option value="2bab0669-550c-4555-ae9f-1fdafdb872e5"> Between 100 and 250</option>
    <option value="d471e43b-196c-46e0-9b32-21e24e5469b4"> Between 50 and 100</option>
    <option value="ccdad63f-69be-449f-8b2c-25f844dd19c1"> Between 20 and 50</option>
    <option value="e00637a2-e3e8-4883-9e11-94e58af6e4b7" selected> Less then 20</option>
</select>

app.directive('ngInitial', function () {
return {
    restrict: 'A',
    controller: ['$scope', '$element', '$attrs', '$parse', function ($scope, $element, $attrs, $parse) {
        val = $attrs.sbInitial || $attrs.value || $element.val() || $element.text()
        getter = $parse($attrs.ngModel)
        setter = getter.assign
        setter($scope, val)
    }]
}

});

kolesso
sumber
0

Jika Anda memiliki nilai init di URL seperti mypage/id, maka pada pengontrol JS sudut Anda dapat menggunakan location.pathnameuntuk menemukan id dan menetapkannya ke model yang Anda inginkan.

Neon
sumber