2015-07-10 7 views
14

Próbuję uporać się z innym zachowaniem ngModel w różnych przeglądarkach.ngModel - Jak radzić sobie z jego różnymi zachowaniami w różnych przeglądarkach?

Moja dyrektywa otacza proces autouzupełniania JQueryUI i wywołuje select zdarzenie, które wywołuje ngModel.$setViewValue(selectedItem.id). Autouzupełnianie pozwala użytkownikowi wybrać pozycję jednym kliknięciem myszy lub naciskając enter na klawiaturze.

Jeśli pozycja jest sugerowane:

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

się spodziewać po wybraniu go, wartość ngModel zostanie wybrana pozycja na id - 1000.

  • W Chrome działa OK - ustawia $viewValue i $modelValue poprawnie ($modelValue=1000).

  • W Firefoksie ustawia modelu jak w Chrome ($modelValue=1000), ale po kliknięciu gdzieś indziej - zrobić rozmazania (wówczas przeglądarka prawdopodobnie odpala change zdarzenie), zmiany modelu i staje się tak samo jak widocznej wartości wejściowej ($modelValue='Apple').

  • W IE 11 ustawia model tylko wtedy, gdy zaznaczam element za pomocą kliknięcia myszką. Gdybym wybierz ją, naciskając enter, model staje się widoczna wartość wejściowa ($modelValue='Apple')

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

chciałbym osiągnąć to samo zachowanie w każdej przeglądarce. Jak sobie z tym poradzić?

+0

Wydaje mi się, że zdecydowanie lepiej jest używać dwóch różnych modeli, z których jedna ma rozpoznaną wartość asynchroniczną (prawdopodobnie prywatną) i taką, której używa użytkownik do wpisania zapytania (publiczne). W obecnej formie, gdy wpiszesz kompletną fałszywą nazwę (taką, której nie ma na liście owoców), będzie to wartość modelu. Co prawdopodobnie nie jest pomocne, gdy spodziewasz się, że będzie to dowód osobisty. – Yoshi

+0

@Yoshi, To, o czym wspomniałeś, jest tylko usprawnieniem sprawdzania poprawności - użytkownik nie będzie mógł wpisać czegoś innego niż id, ale to nie rozwiąże problemu. (Albo nie wiem jak to właściwie zaimplementować). Dzięki twoim podpowiedziom będzie to coś takiego: http://plnkr.co/edit/7nCAEhIXX2wGNR18eRqk. '$ modelValue = null' dla fałszywych nazw, ale w funkcji parseera firefox nadal jest uruchamiany ponownie przy rozmyciu, więc tracę wybraną wartość. – akn

+0

Jeśli są to tylko zdarzenia, możesz spróbować użyć ['ngModelOptions.updateOn'] (https://docs.angularjs.org/api/ng/directive/ngModelOptions). – Yoshi

Odpowiedz

1

Ok, myślę, że się udało. Rozwiązanie jest oparte na Yoshi's comment i wykorzystuje lokalny model do przechowywania wybranych danych.

Gdy użytkownik wybierze coś, model lokalny jest ustawiony na wybrany Object, a $viewValue jest ustawiony na wartość tekstową wybranego elementu. Następnie parser ustawia właściwość lokalnego modelu jako $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; 
}); 

Funkcja parsera to także jedna ważna rzecz. Ponieważ jest uruchamiany, gdy użytkownik wpisuje coś lub w zdarzeniu change (to był problem w firefoxie!), Sprawdza, czy wartość jest taka sama, jak aktualna wartość tekstowa lokalnego modelu, a jeśli nie, zmienia model lokalny na tę wartość. Oznacza to, że jeśli funkcja analizatora składni jest uruchamiana przez change, wartość zdarzenia będzie taka sama jak wartość tekstowa, więc $modelValue nie zostanie zmieniona, ale jeśli typ użytkownika zostanie zaktualizowany jakiś model do wpisanej wartości (stanie się to String).

Funkcja sprawdzania poprawności sprawdza, czy model lokalny to Object. Jeśli nie, oznacza to, że pole jest nieprawidłowe, więc domyślnie znika jego $modelValue.

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

(funkcja formatowania wrócę że to, co przychodzi, więc $viewValue jest tymczasowo Object ale potem w $render metodzie nazywam $setViewValue ustawić $viewValue i $modelValue prawidłowo, więc staje String Słyszałem, że $setViewValue nie należy uruchamiać w metodzie $render, ale nie widzę innego sposobu na ustawienie poprawnej $modelValue, gdy coś pochodzi z zewnątrz).

2

To wydaje się być związane z http://bugs.jqueryui.com/ticket/8878

Jak wskazano w powyższym linku, wydarzenie zmiana jest wyzwalany tylko w Firefox, a nie w Chrome. Tak więc w twoim przypadku $ setViewValue jest ponownie wyzwalane po kliknięciu na zewnątrz, a wartość modelu jest ustawiona na "Apple".

Istnieje wywołanie zwrotne zmiany dla widgetu jQuery autouzupełniania. Aby obsłużyć oba przypadki/przeglądarki, konieczne może być jawne ustawienie wartości widoku podczas tego połączenia zwrotnego (i to działa).

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

Dzięki, nie jest to idealne rozwiązanie, ale może pomóc. Ponadto nie rozwiązuje problemu z kluczem 'enter' w IE. – akn

0

miałem podobne walczy z ngModelController i $setViewValue.

Ostatecznie szukałem alternatywnych rozwiązań. Jednym ze znalezionych rozwiązań, które sprawdziły się całkiem dobrze, było utworzenie nowego elementu jako dyrektywy komponentu, która zawiera znacznik wejściowy jako element translatujący.

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({ 
     ... 
     }); 
    } 
    } 
}) 

W HTML:

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

Tu jest praca Plunker

Z tego proponowanego rozwiązania można wyizolować ngModelController i jQueryUI modalne grę do własnej niestandardowego elementu i nie ingerować w "normalny" znacznik <input> i nie przejmujesz się błędem jQueryUI.

Za pomocą znacznika <input> jako dołączany elementu nadal można korzystać z większości kątowego smakołyków wejściowych jak ng-disabled, placeholder, itp ...

+0

W tym rozwiązaniu utracisz wiele domyślnych funkcji. Na przykład. Nie możesz ustawić 'placeholder', nie możesz użyć' ng-change' lub 'ng-disabled'. To może być ok, jeśli chcesz użyć swojej dyrektywy dla naprawdę konkretnych przypadków, ale nie wtedy, gdy tworzysz dyrektywę wielokrotnego użytku, która będzie używana w dużej aplikacji;) – akn

+0

Wyzwanie zaakceptowane! Prosimy o rozważenie mojej zaktualizowanej odpowiedzi za pomocą funkcji transclusion. – DanEEStar

Powiązane problemy