2015-07-10 20 views
14

Sto cercando di gestire diversi comportamenti di ngModel in diversi browser.ngModel - Come gestire il suo diverso comportamento nei diversi browser?

La mia direttiva esegue il completamento automatico di jqueryUI e sul relativo evento select chiama ngModel.$setViewValue(selectedItem.id). Il completamento automatico consente all'utente di selezionare la voce con un clic del mouse o premendo enter sulla tastiera.

Se voce suggerita è:

{ 
    "name": "Apple", 
    "id": "1000" 
} 

mi aspetto dopo averlo selezionato, il valore ngModel verrà selezionato di voce id-1000.

  • In Chrome funziona bene - è imposta $viewValue e $modelValue correttamente ($modelValue=1000).

  • In Firefox è imposta come modello in Chrome ($modelValue=1000), ma quando clicco da qualche altra parte - faccio sfocatura (allora il browser probabilmente spara change evento), modifiche del modello e diventa uguale valore di ingresso visibile ($modelValue='Apple').

  • In IE 11 imposta il modello corretto solo quando seleziono l'elemento con il clic del mouse. Se seleziono premendo enter, il modello diventa valore di ingresso visibile ($modelValue='Apple')

Ecco plunkr: http://plnkr.co/edit/o2Jkgprf8EakGqnpu22Y?p=preview

vorrei raggiungere lo stesso comportamento in tutti i browser. Come affrontare questi problemi?

+0

Penso che sia molto meglio utilizzare due modelli distinti, uno che contiene il valore asincrono risolto (probabilmente privato) e uno che l'utente utilizza per digitare la query (pubblica). Così com'è, quando si digita un nome falso completo (uno che non è nella lista dei frutti), questo sarà il valore del modello. Che probabilmente non è molto utile quando ti aspetti che sia un ID. – Yoshi

+0

@Yoshi, che quello che hai menzionato è solo un miglioramento della validazione - l'utente non sarà in grado di digitare qualcosa di diverso da id, ma non risolve il problema. (O non so come implementarlo correttamente). Con i tuoi suggerimenti sarebbe qualcosa di simile: http://plnkr.co/edit/7nCAEhIXX2wGNR18eRqk. '$ modelValue = null' per i nomi falsi, ma nella funzione parser di firefox è ancora eseguito di nuovo su sfocatura, quindi perdo il mio valore selezionato. – akn

+0

Se sono solo gli eventi, puoi provare a usare ['ngModelOptions.updateOn'] (https://docs.angularjs.org/api/ng/directive/ngModelOptions). – Yoshi

risposta

1

Ok, penso che ce l'ho fatta. La soluzione è basata su Yoshi's comment e utilizza il modello locale per mantenere i dati selezionati.

Quando l'utente seleziona qualcosa, il modello locale è impostato su selezionato Object e $viewValue è impostato sul valore di testo dell'elemento selezionato. Quindi il parser imposta la proprietà id del modello locale come $modelValue.

select: function(event, ui) { 
    if(ui.item && ui.item.data){ 
    model = ui.item.data 
    ngModel.$setViewValue(model.name); 
    scope.$apply(); 
    } 
} 

ngModel.$parsers.push(function(value) { 
    if(_.isObject(model) && value!==model.name){ 
    model = value; 
    return model; 
    } 
    return model.id; 
}); 

La funzione di parser fa anche una cosa importante. Poiché viene eseguito quando l'utente digita qualcosa o sull'evento change (che era il problema in firefox!), Controlla se il valore è uguale al valore di testo del modello locale corrente e, in caso contrario, modifica il modello locale in questo valore. Significa che se la funzione parser viene eseguita da change il valore dell'evento sarà lo stesso del valore di testo, quindi $modelValue non viene modificato, ma se l'utente digita qualcosa il modello viene aggiornato al valore digitato (diventa String).

La funzione di convalida verifica se il modello locale è Object. In caso contrario, significa che il campo non è valido, quindi per impostazione predefinita il suo $modelValue scompare.

Ecco la plunkr: http://plnkr.co/edit/2ZkXFvgLIwDljfJoyeJ1?p=preview

(In funzione di formattazione torno che ciò che viene, così $viewValue è temporaneamente un Object ma poi in $render metodo che io chiamo $setViewValue per impostare $viewValue e $modelValue correttamente, in modo che diventi String Ho sentito che $setViewValue non dovrebbe essere eseguito nel metodo $render, ma non vedo un altro modo per impostare $modelValue corretto quando qualcosa viene da fuori).

2

Questo sembra essere correlato a http://bugs.jqueryui.com/ticket/8878

Come sottolineato nel link qui sopra, l'evento di modifica viene attivato solo in Firefox e non in Chrome. Quindi nel tuo caso, $ setViewValue viene nuovamente attivato quando viene fatto clic all'esterno e il valore del modello è impostato su "Apple".

C'è un cambio di callback per il widget ui jquery del completamento automatico. Per gestire entrambi i casi/browser potrebbe essere necessario impostare di nuovo il valore di visualizzazione in modo esplicito su questa richiamata (e funziona).

http://plnkr.co/edit/GFxhzwieBJTSL8zjSPSZ?p=preview

link: function(scope, elem, attrs, ngModel) { 
     elem.on('change', function(){ 
     // This will not be printed in Chrome and only on firefox 
     console.log('change'); 
    }); 


    select: function(event, ui) { 
     ngModel.$setViewValue(ui.item.data.id); 
     scope.$apply(); 
    }, 
    // To handle firefox browser were change event is triggered 
    // when clicked outside/blur 
    change: function(event, ui) { 
     ngModel.$setViewValue(ui.item.data.id); 
     scope.$apply(); 
    } 
+0

Grazie, non è la soluzione perfetta, ma può essere d'aiuto. Inoltre, non risolve il problema con la chiave 'enter' in IE. – akn

0

ho avuto scontri con la classica del ngModelController e $setViewValue.

Alla fine ho cercato soluzioni alternative. Un approccio che ho trovato che ha funzionato piuttosto bene è stato creare un nuovo elemento come direttiva componente che include il tag di input come elemento transclusionato.

app.directive('fruitAutocomplete', function($http) { 
    return { 
    restrict: 'E', 
    require: 'ngModel', 
    transclude: true, 
    template: '<ng-transclude></ng-transclude>', 
    link: function(scope, elem, attrs, ngModelController) { 
     var $input = elem.find('input'); 

     $input.autocomplete({ 
     ... 
     }); 
    } 
    } 
}) 

Nella HTML:

<fruit-autocomplete name="fruit" ng-model="model.fruit"> 
    <input ng-disabled="inputDisabled" placeholder="input fruit"/> 
</fruit-autocomplete> 

Questo vuole essere un lavorando Plunker

Con questa soluzione proposta è possibile isolare il ngModelController e modale interazione jQueryUI al proprio elemento personalizzato e non lo fa interferire con il tag "normale" <input> e non sei interessato dal bug jQueryUI.

utilizzando il tag <input> come elemento di transclusa si può ancora beneficiare dalla maggior parte delle chicche di ingresso angolare come ng-disabled, placeholder, ecc ...

+0

In questa soluzione, si perdono molte funzionalità predefinite. Per esempio. Non puoi impostare 'segnaposto', non puoi usare' ng-change' o 'ng-disabled'. Potrebbe essere ok quando si desidera utilizzare la direttiva per casi veramente specifici, ma non se si sta costruendo una direttiva riutilizzabile che verrà utilizzata in applicazioni di grandi dimensioni;) – akn

+0

Sfida accettata! Per favore considera la mia risposta aggiornata usando la transizione. – DanEEStar