Varsayılan değerlerden açısal js başlatma modeli


126

Veritabanından yüklenen değerlere sahip bir formunuz olduğunu varsayalım. Ng modelini nasıl başlatırsınız?

Misal:

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

Denetleyicimde $ kapsam.card başlangıçta tanımsız. Böyle bir şey yapmanın bir yolu var mı?

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

Yanıtlar:


136

Bu, yeni Angular uygulamalarında yaygın bir hatadır. Önleyebiliyorsanız, değerlerinizi sunucudaki HTML kodunuza yazmak istemezsiniz. Aslında, sunucunuzun tamamen HTML oluşturmasını engelleyebilirseniz, çok daha iyi.

İdeal olarak, Angular HTML şablonlarınızı göndermek, ardından değerlerinizi JSON'da $ http aracılığıyla aşağı çekmek ve kapsamınıza koymak istiyorsunuz.

Yani mümkünse şunu yapın:

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

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

Değerlerinizi kesinlikle sunucunuzdan HTML'nize dönüştürmeniz ZORUNLUysa, onları global bir değişkene koyabilir ve bunlara $ window ile erişebilirsiniz:

Sayfanızın başlığında şunları yazacaksınız:

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

Ve sonra kontrol cihazınızda şu şekilde elde edersiniz:

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

Umarım bu yardımcı olur.


4
Evet, yardımcı oluyor. Sanırım Angular adamlarının bu kararı vermesine şok oldum.
Calvin Froedge

125
Şok olmayın ... HTML'nin oluşturulması sunuculardan ve tarayıcıya taşınıyor. Bugünlerde JavaScript'te düzinelerce MVC çerçevesi var ve bir sunucunun JSON / XML verilerini JavaScript uygulamalarına barındırması, sunucudaki her bir sayfayı oluşturmaktan çok daha verimli. Sunucunun isabet almasını sağlamak yerine, işin çoğunu müşterinin makinesine dengeliyor. Bant genişliğinden tasarruf sağladığından bahsetmiyorum bile. Her şeyden önce, aynı JSON'u HTTP üzerinden kullanan yerel bir mobil uygulamanız (veya gerçekten herhangi bir şey) olabilir. Gelecek bu.
Ben Lesh

3
@blesh: Harika yanıt. Çok teşekkürler. İleriye giden yolun bu olduğuna katılıyorum ve bu yaklaşımı benimsemeye başladım; bu iddiayı destekleyen herhangi bir bağlantınız var mı? Ayrıca, html oluşturma işleminin sunucudan istemciye taşınmasının, özellikle bir gezinti ağacı gibi çok fazla HTML oluşturabileceğiniz mobil cihazlarda daha yavaş sayfa yükleme sürelerine neden olabileceğinden endişeleniyorum.
GFoley83

19
Bunun bir 'hata' olduğuna katılmıyorum. Bazı durumlarda, istemci tarafında oluşturma en iyi çözümdür. Diğerlerinde, sunucu tarafı oluşturma gitmenin yoludur. Her iki teknikle de açısal kullanabilirsiniz ve istemci tarafı oluşturma, "açısal yol" olarak tutulmamalıdır çünkü öyle değildir. Angular bundan daha esnektir.
hunterloftis

8
@blesh, kesinlikle - SEO'nun önemli olduğu ancak tarayıcılar için alternatif içeriğin maliyetinin gömülü verilerle açısal uygulama maliyetinden daha yüksek olduğu herhangi bir örnek - algılanan hıza veya oluşturma süresine çok yüksek bir değer veren herhangi bir uygulama kullanıcı deneyimi - birçok içerik sitesi ve e-ticaret sitesi bu kategorilerden birine girer. İstemci oluşturmayı deneyen ve daha sonra daha iyi bir kullanıcı deneyimi sunmak için sunucuya geri dönen mühendisler, gerekçelerini de özetlediler: blog.twitter.com/2012/improving-performance-twittercom
hunterloftis 13

236

@Blesh'ın önerdiği şeyi yapmak için uygulamanızı yeniden işleyemiyorsanız (JSON verilerini $ http veya $ resource ile aşağı çekin ve $ kapsam doldurun), bunun yerine ng-init'i kullanabilirsiniz :

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

Ayrıca bkz. AngularJS - Bir ng modeli kullanıldığında, girdi metin kutusundaki değer özelliği yok sayılır.


Açısal yol için +1 (daha az tercih edilen uygulamada). Örnek: jsfiddle.net/9ymB3
John Lehmann

2
Angular with C # web formlarını kullanıyorum ve kullanmanın ng-initarka planda kodlama / postback Örn <input name="phone" data-ng-model="frm.phone" data-ng-init="frm.phone= '<%: Model.Phone %>'" data-ng-pattern="/^[0-9() \-+]+$/" type="tel" required />. Biraz çirkin mi? Evet, ancak hile yapar ve süreçteki bir entegrasyon sorununu çözer.
GFoley83

13
+1, zarif! İnsanlar çabuk ve hızlı davranışı savunuyor, ancak burada SO'daki pek çok kişi "her şeyi istemci tarafında yap" diyor. Milyonlarca http çağrısının hızlı siteler için iyi olduğu gibi. Veri sunucusu tarafını şablonlara yerleştirmek, önbelleğe alma stratejilerini etkinleştirir. Memcached ile statik veya dinamik / kısmi. Twitter'ın yaptığı budur. Bazen faydalıdır, diğer zamanlarda değildir. Bunu vurgulamak istedim. Bunu eklemeliydim, aksini söylediğine değil, @Mark.
oma

1
Aslında bunu geri alıyorum, bu benim için en iyisi, harika: stackoverflow.com/a/20522955/329367
Darren

1
FWIW: Şablon ifadelerinde değişken atamasından hoşlanmıyorum çünkü test edilebilir değil.
Ben Lesh

60

Bu açıkça eksik, ancak AngularJS için kolayca eklenen bir düzeltmedir. Giriş alanından model değerini ayarlamak için hızlı bir yönerge yazmanız yeterlidir.

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

İşte benim versiyonum:

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

Güzel hızlı talimat. Bunu ng-modeline paketlememek için herhangi bir iyi neden var, böylece değer = ve ng-modeli = çoğu insanın beklediği gibi birlikte çalışabilsin?
hunterloftis

Bayağı güzel! Bu kullanışlı.
santiagobasulto

3
Mükemmel cevap! Bunun metin alanlarında da çalışmasını sağlamak için bir "düzeltme" ekledim - gist.github.com/rmontgomery429/6191275
Ryan Montgomery

neden controllerdirektifiniz için tanımladınız ve neden linkyöntemi kullanmadınız ? Angularjs için acemiyim
ebram khalil

1
val = $attrs.ngInitial || $attrs.value || $element.val()belirli öğelerle çalışması için olarak değiştirilmesi gerekiyor .
fjsj

12

IMHO en iyi çözüm @Kevin Stone direktifidir, ancak onu her koşulda (fe select, textarea) çalışacak şekilde yükseltmek zorunda kaldım ve bu kesinlikle çalışıyor:

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

Peki ya seçme?
jwize

7

Özel bir yönerge kullanabilir (metin alanı, seçim, radyo ve onay kutusu desteği ile), bu blog gönderisine göz atın https://glaucocustodio.github.io/2014/10/20/init-ng-model-from-form- alanlar-öznitelikler / .


1
Bu harika bir gönderi. Bu sorudan beri en sevdiğim yanıt, bir girdinin değerini başlangıç ​​değerlerine göre ayarlamaktı.
RPDeshaies

Bu kodu test etmek için bir Plnkr oluşturdum ve harika çalışıyor: plnkr.co/edit/ZTFOAc2ZGIZr6HjsB5gH?p=preview
RPDeshaies

6

Ayrıca HTML kodunuz içinde de kullanabilirsiniz: ng-init="card.description = 12345"

Angular tarafından tavsiye edilmez ve yukarıda belirtildiği gibi yalnızca kontrol cihazınızı kullanmanız gerekir.

Ama işe yarıyor :)


4

Basit bir yaklaşımım var çünkü formlarımda bazı ağır onaylamalarım ve maskelerim var. Bu yüzden, değerimi tekrar almak için jquery kullandım ve "değişiklik" olayını doğrulamalarda tetikledim:

$('#myidelement').val('123');
$('#myidelement').trigger( "change");

3

Diğerlerinin de belirttiği gibi, verileri görünümler üzerine başlatmak iyi bir uygulama değildir. Bununla birlikte, Kontrolörlerdeki verilerin ilklendirilmesi önerilir. (bkz. http://docs.angularjs.org/guide/controller )

Böylece yazabilirsin

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

ve

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

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

Bu şekilde görünümler veri içermez ve denetleyici gerçek değerler yüklenirken değeri başlatır.


3

Kevin Stone'un https://stackoverflow.com/a/17823590/584761 üzerindeki yaklaşımını seviyorsanız , 'input' gibi belirli etiketler için yönergeler yazarak daha kolay bir yaklaşım düşünün.

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

Bu rotaya giderseniz, her etikete ng-başlangıç ​​eklemek konusunda endişelenmenize gerek kalmaz. Modelin değerini otomatik olarak etiketin değer özniteliğine ayarlar. Değer özelliğini ayarlamazsanız, varsayılan olarak boş bir dizge olacaktır.


3

İşte sunucu merkezli bir yaklaşım:

<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>

Bu soruya verilen daha popüler yanıtlarla aynı temel fikir, $ sağla.value dışında varsayılan değerlerinizi içeren bir hizmeti kaydeder.

Yani, sunucuda aşağıdaki gibi bir şeye sahip olabilirsiniz:

{
    description: "Visa-4242"
}

Ve bunu, seçtiğiniz sunucu tarafı teknolojisi aracılığıyla sayfanıza yerleştirin. İşte bir Özet: https://gist.github.com/exclsr/c8c391d16319b2d31a43


1
Anlayabildiğim kadarıyla, ilk $ http talebinde bulunmak bir seçenek değilse, sorunu çözmek için en Angular yöntemdir. Ayrıca, daha sonra $ http ile değiştirmek isterseniz, değiştirmenin çok daha kolay olma avantajına da sahiptir. Olası bir iyileştirme, değişimi daha da kolaylaştırmak için bir sözün yerine getirilmesidir. Global değişkenleri ayarlamaktan veya burada ng-başlangıç ​​kullanmaktan çok utanırdım.
richard

1

Bu, yukarıda bahsedilen fikirlerin daha genel bir versiyonudur ... Basitçe modelde herhangi bir değer olup olmadığını kontrol eder, yoksa değeri modele ayarlar.

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 (kullanım örneği):

<select class="form-control"
        ng-options="v for (i, v) in compressionMethods"
        ng-model="action.parameters.Method"
        default-value="'LZMA2'"></select>

Bu benim için çalıştı, ancak yalnızca $scopeçağrıya geçtikten sonra getter()- umarım bu, bunu deneyen başka biri için işleri çözer!
zesda

0

@Mark Rajcok'un önerdiği şeyi denedim. String değerleri için çalışır (Visa-4242). Lütfen bu kemana bakın .

Kemanla:

Kemanla yapılan aynı şey, ng-repeatherkesin tavsiye edebileceği şekilde yapılabilir . Ancak @Mark Rajcok tarafından verilen cevabı okuduktan sonra, aynısını bir dizi profil içeren bir form için denemek istedim. $cope.profiles = [{}, {}]; denetleyicideki kod. Bu kodu kaldırırsam, hata alıyorum. Ancak normal senaryolarda $scope.profiles = [{},{}]; sunucudan html yazdırdığım veya yankıladığım için yazdıramıyorum. <input name="card[description]" ng-model="card.description" ng-init="card.description='Visa-4242'">Sunucudan JavaScript bölümünü yankılamak zorunda kalmadan, @Mark Rajcok gibi dize değerleri için yukarıdakileri benzer bir şekilde çalıştırmak mümkün olacak mı ?


1
Diziyi başlatmak için ng- init'i
Karen Zilles

0

Ryan Montgomery "düzeltmesine" seçilen öğe için destek eklendi

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

});


0

URL'de init değerine mypage/idsahipseniz, açısal JS'nin denetleyicisinde location.pathnamekimliği bulmak ve istediğiniz modele atamak için kullanabilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.