Modello angolare js init ng dai valori predefiniti


126

Supponi di avere un modulo con valori caricati dal database. Come si inizializza ng-model?

Esempio:

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

Nel mio controller, $ scope.card inizialmente non è definito. C'è un modo oltre a fare qualcosa del genere?

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

Risposte:


136

Questo è un errore comune nelle nuove applicazioni angolari. Non vuoi scrivere i tuoi valori nel tuo HTML sul server se puoi evitarlo. In effetti, se riesci ad evitare che il tuo server esegua il rendering HTML, tanto meglio.

Idealmente, si desidera inviare i modelli HTML angolari, quindi ridurre i valori tramite $ http in JSON e inserirli nel proprio ambito.

Quindi, se possibile, fai questo:

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

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

Se DEVI assolutamente rendere i tuoi valori nel tuo HTML dal tuo server, potresti metterli in una variabile globale e accedervi con $ window:

Nell'intestazione della tua pagina scriveresti:

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

E poi nel controller lo otterresti così:

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

Spero che aiuti.


4
Sì, aiuta. Immagino di essere scioccato dal fatto che i ragazzi angolari abbiano preso questa decisione.
Calvin Froedge,

125
Non essere scioccato ... Il rendering dell'HTML si sta spostando dai server al browser. Al giorno d'oggi ci sono dozzine di framework MVC in JavaScript, ed è molto più efficiente per un server ospitare i dati JSON / XML su app JavaScript piuttosto che renderizzare ogni singola pagina sul server. Compensa gran parte del lavoro sulla macchina del client piuttosto che far sì che il server subisca il colpo. Per non parlare del risparmio sulla larghezza di banda. Per finire, potresti avere un'app mobile nativa (o qualcosa del genere) che consuma lo stesso JSON su HTTP. Questo è il futuro
Ben Lesh,

3
@blesh: ottima risposta. Molte grazie. Sono d'accordo che questa è la strada da seguire e ho iniziato ad adottare questo approccio da solo; hai dei link che supportano questa affermazione? Sono anche preoccupato che lo spostamento del rendering dell'html dal server al client potrebbe comportare tempi di caricamento della pagina più lenti, soprattutto nei dispositivi mobili in cui è possibile eseguire il rendering di molto HTML, ad esempio per un albero di navigazione.
GFoley83

19
Non sono d'accordo sul fatto che si tratti di un "errore". In alcuni casi, il rendering sul lato client è la soluzione migliore. In altri, il rendering sul lato server è la strada da percorrere. È possibile utilizzare l'angolare con entrambe le tecniche e il rendering sul lato client non deve essere considerato come il "modo angolare" perché non lo è. Angolare è più flessibile di così.
hunterloftis

8
@blesh, certamente - qualsiasi istanza in cui la SEO è importante ma il costo di contenuti alternativi per i crawler è superiore al costo dell'implementazione angolare con i dati incorporati - qualsiasi applicazione che attribuisce un valore molto elevato alla velocità percepita o al tempo di rendering per il esperienza dell'utente: molti siti di contenuti e siti di e-commerce rientrerebbero in una di queste categorie. Gli ingegneri di Twitter, che hanno provato il rendering dei client, sono tornati sul server per offrire una migliore esperienza utente, hanno delineato anche il loro ragionamento: blog.twitter.com/2012/improving-performance-twittercom
hunterloftis

236

Se non riesci a rielaborare la tua app per fare ciò che suggerisce @blesh (abbassa i dati JSON con $ http o $ risorse e popola $ scope), puoi invece usare ng-init :

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

Vedi anche AngularJS - L'attributo del valore in una casella di testo di input viene ignorato quando viene utilizzato un modello ng?


+1 per la via angolare (della pratica meno preferita). Esempio: jsfiddle.net/9ymB3
John Lehmann,

2
Sto usando Angular con i moduli web C # e trovo che usare ng-initsia abbastanza utile quando si impostano valori da code-behind / postback ad es <input name="phone" data-ng-model="frm.phone" data-ng-init="frm.phone= '<%: Model.Phone %>'" data-ng-pattern="/^[0-9() \-+]+$/" type="tel" required />. Un po 'brutto? Sì, ma fa il trucco e risolve un mal di testa di integrazione nel processo.
GFoley83,

13
+1, elegante! Le persone sostengono un comportamento scattante e rapido, eppure molti qui in SO dicono "fai tutto sul lato client", come milioni di chiamate http sono buone per i siti scattanti. Il seeding del lato server di dati in modelli consente strategie di memorizzazione nella cache. Statico o dinamico / parziale con Memcached. Questo è ciò che fa Twitter. A volte è utile, altre volte no. Volevo sottolineare questo. Devo solo aggiungere questo, non che tu abbia detto diversamente, @Mark.
oma,

1
In realtà lo riprendo, questo funziona meglio per me, fantastico: stackoverflow.com/a/20522955/329367
Darren

1
FWIW: Non mi piace l'assegnazione delle variabili nelle espressioni dei template perché non è verificabile.
Ben Lesh,

60

Questa è ovviamente una soluzione carente, ma facilmente aggiunta per AngularJS. Basta scrivere una direttiva rapida per impostare il valore del modello dal campo di input.

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

Ecco la mia versione:

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

Bella direttiva rapida. Qualche buona ragione per non impacchettarlo in ng-model in modo che value = e ng-model = possano lavorare insieme come la maggior parte delle persone si aspetterebbe?
hunterloftis

Molto carino! Questo è utile
santiagobasulto,

3
Bella risposta! Ho appena aggiunto una "correzione" per far funzionare anche questo con textareas - gist.github.com/rmontgomery429/6191275
Ryan Montgomery

perché hai definito la controllertua direttiva e perché non hai usato il linkmetodo? Sono un novizio di angularjs
ebram khalil,

1
è necessario cambiarlo val = $attrs.ngInitial || $attrs.value || $element.val()per farlo funzionare con elementi selezionati.
fjsj,

12

IMHO la soluzione migliore è la direttiva @Kevin Stone, ma ho dovuto aggiornarla per funzionare in tutte le condizioni (fe select, textarea), e questa funziona sicuramente:

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

E che dire di selezionare?
jwize,

7

Puoi usare una direttiva personalizzata (con supporto per textarea, selezionare, radio e checkbox), dai un'occhiata a questo post sul blog https://glaucocustodio.github.io/2014/10/20/init-ng-model-from-form- attributi-campi / .


1
Questo è un grande post. La mia risposta preferita dal momento che la domanda era di impostare il valore di un input in base ai valori iniziali.
RPDeshaies,

Ho creato un Plnkr per testare questo codice e funziona benissimo: plnkr.co/edit/ZTFOAc2ZGIZr6HjsB5gH?p=preview
RPDeshaies

6

Puoi anche usare nel tuo codice HTML: ng-init="card.description = 12345"

Non è raccomandato da Angular e, come detto sopra, è necessario utilizzare esclusivamente il controller.

Ma funziona :)


4

Ho un approccio semplice, perché ho alcune convalide e maschere pesanti nelle mie forme. Quindi, ho usato jquery per ottenere di nuovo il mio valore e attivare l'evento "modifica" in convalide:

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

3

Come altri hanno sottolineato, non è buona norma inizializzare i dati sulle viste. Si consiglia, tuttavia, di inizializzare i dati sui controller. (vedi http://docs.angularjs.org/guide/controller )

Quindi puoi scrivere

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

e

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

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

In questo modo le viste non contengono dati e il controller inizializza il valore mentre vengono caricati i valori reali.


3

Se ti piace l'approccio di Kevin Stone sopra https://stackoverflow.com/a/17823590/584761 considera un approccio più semplice scrivendo direttive per tag specifici come "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);
            }
        }
    }; });

Se segui questa strada non dovrai preoccuparti di aggiungere ng-initial a ogni tag. Imposta automaticamente il valore del modello sull'attributo value del tag. Se non si imposta l'attributo value, il valore predefinito sarà una stringa vuota.


3

Ecco un approccio incentrato sul server:

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

È la stessa idea di base delle risposte più popolari su questa domanda, ad eccezione di $ fornire.value registra un servizio che contiene i valori predefiniti.

Quindi, sul server, potresti avere qualcosa del tipo:

{
    description: "Visa-4242"
}

E inseriscilo nella tua pagina tramite la tecnologia lato server di tua scelta. Ecco un riassunto: https://gist.github.com/exclsr/c8c391d16319b2d31a43


1
Per quanto ne so, questo è il metodo più angolare per gestire il problema se fare una richiesta iniziale $ http non è un'opzione. Ha anche il vantaggio di essere molto più semplice da modificare se si desidera passare a $ http in seguito. Un possibile miglioramento è quello di restituire una promessa invece di rendere il passaggio ancora più semplice. Mi allontanerei molto dall'impostazione di var globali o dall'uso di ng-iniziale qui.
richard,

1

Questa è una versione più generica delle idee sopra menzionate ... Controlla semplicemente se c'è qualche valore nel modello e, in caso contrario, imposta il valore sul modello.

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 (esempio di utilizzo):

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

Questo ha funzionato per me, ma solo dopo essere passato $scopealla chiamata a getter()- spero che questo chiarisca le cose per chiunque lo stia provando!
zesda,

0

Ho provato ciò che suggeriva @Mark Rajcok. Funziona con valori String (Visa-4242). Si prega di fare riferimento a questo violino .

Dal violino:

La stessa cosa che si può fare nel violino può essere fatta usando ng-repeat, cosa che tutti potrebbero consigliare. Ma dopo aver letto la risposta data da @Mark Rajcok, volevo solo provare lo stesso per un modulo con una serie di profili. Le cose funzionano bene finché non ho $ scope.profiles = [{}, {}]; codice nel controller. Se rimuovo questo codice, sto ottenendo errori. Ma in scenari normali non posso stampare $scope.profiles = [{},{}]; mentre stampo o echo html dal server. Sarà possibile eseguire quanto sopra, in modo simile a quanto fatto da @Mark Rajcok per i valori stringa come <input name="card[description]" ng-model="card.description" ng-init="card.description='Visa-4242'">, senza dover ripetere la parte JavaScript dal server.


1
È possibile utilizzare ng-init per inizializzare l'array e quindi utilizzare ng-repeat per generare le righe del modulo: jsfiddle.net/6tP6x/1
Karen Zilles

0

Aggiunto il supporto per l'elemento selezionato alla "correzione" di Ryan Montgomery

<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

Se hai il valore init nell'URL come mypage/id, quindi nel controller dell'angolare JS puoi usare location.pathnameper trovare l'id e assegnarlo al modello che desideri.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.