Angular JS – Składnia „controller as”.

Otrzymałem kilka pytań o stosowaną przeze mnie, w ostatnim projekcie, składnie. Chodziło o sformułowanie controller as w widoku zamiast używania $scope.

Zaznaczam, że nie jestem guru angulara, więc nie przedstawię dogłębnej wiedzy na temat działania frameworka. Napiszę tylko dlaczego ja stosuje pewne techniki a innych nie 🙂 .

Angular JS - Składnia "controller as"

Tak naprawdę to pytania o brak $scope nie zdziwiły mnie tak bardzo. We wszystkich dostępnych w Polsce książkach i tutorialach używana jest właśnie ta składania. Sam długi czas nie wiedziałem, że istnieje alternatywa. A tu okazuje się, że możliwość używania controller as developerzy mają już od wersji 1.2 Angulara. Ponad to, z tego co wiem, jest ona używana dość powszechnie.

Ktoś w tym miejscu mógłby powiedzieć, „ale jakie to ma znaczenie i tak wchodzi już Angular 2”. I w sumie ma rację. Należy jednak pamiętać, że w sieci jest wciąż bardzo dużo kodu stworzonego w „jednyce”. Prawda jest taka, że kod ten nie zniknie czy nam się to podoba czy nie 🙂 . Dlatego wciąż warto mieć pojęcie o niuansach frameworka.

Wracając do sedna sprawy, gdy tworzymy kontroler „standardowym” podejściem’, wygląda on mniej więcej tak:

Dokładnie tak jak uczą w książkach. Najpierw należy wstrzyknąć do kontrolera $scope, a następnie przypisać do niego potrzebne dane. Widok obsługiwane przez taki kontroler mógłby wyglądać tak:

Kolejny idealny, książkowy przykład 🙂 . Tak naprawdę nie ma w nim nic złego, ale wiele osób nie zdaje sobie sprawy, że można to zrobić inaczej, używając składni „controller as”. Aby móc to zrobić najpierw trzeba trochę zmienić zapis kontrolera:

Różnica jest subtelna, przede wszystkim, nie trzeba wstrzykiwać $scope. Zamiast tego wewnątrz kontrolera definiuję zmienną, do której przypisuję wartość this. Następnie, wszelkie potrzebne w aplikacji dane przypisuję do tej zmiennej. Zaznaczę tu, że nazwa tej zmiennej nie ma żadnego znaczenia. Na początku przyjęło się, że zmienna ta powinna umownie nazywać się vm, od view model. Nie jest to jednak wymagane. Ba, nawet nie zaleca się tego, o czym wspomnę za chwilę. Prawda jest taka, że zmienna w tym miejscu jest tylko po to aby zwiększyć czytelność kodu. Dane można by przypisywać bezpośrednio do this, czyli kontekstu kontrolera. Dobrą praktyka jednak jest deklarowanie tej zmiennej.

Ok, skoro zmienił się kontroler, to musi zmienić się też widok:

Pierwsza i najważniejsza różnica, to to, że w dyrektywie ng-controller jako wartość trzeba wpisać nie tylko nazwę kontrolera ale też wyrażenie ‚as nasza_nazwa‚. Następnie wewnątrz widoku, aby odwołać się do danych z kontrolera, trzeba zrobić to przez wyrażenie nasza_nazwa.dane_z_kontrolera. Dokładnie tak jak pobiera się pola z obiektu. I tu znów, próbowano wprowadzić taki standard aby nasza_nazwa była zawsze równa vm. To nie jest najlepsza praktyka. Nazwa tej zmiennej może być dowolna, nie musi też być równa aliasowi this z wnętrza kontrolera.

Tak to właśnie wygląda. Prawda jest taka, że oba te sposoby zapisu są poprawne. Powinno używać się tej wersji, która wydaje się być bardziej rozsądna i wygodna. Ważne aby być konsekwentnym. Ja wole wyrażenie controller as, ponieważ ma dla mnie więcej sensu i pomijam (tylko z pozoru, w bebechach dalej to jest) zmienną scope, co sprawia że dla mnie kod wydaje się być mniej zaciemniony.

I na tym mógłbym skończyć, gdyby nie jedna drobna rzecz, o której po prostu muszę napisać. Zanim w Angularze pojawiła się notacja controller as, developerzy wymyślili taką zasadę, że wewnątrz dyrektywy ng-model musi być kropka. Jeśli o tym nie słyszałeś to teraz słuchaj, bo to ważne 🙂 . Nie każdy koniecznie wiedział dlaczego, ale wszyscy się do tego stosowali. Dane w scope chowane były w obiektach tak, żeby dostanie się do nich w modelu wymagało kropki. Już tłumaczę skąd to się wzięło.

Angular pozwala na to aby kontrolery zagnieżdżać. Na przykład, aplikacja posiada jeden duży widok obsługiwany przez główny kontroler, wewnątrz którego znajdują się jakieś „okienka”, które obsługują specyficzne dla nich kontrolery. Oto przykład:

Główny kontroler posiada w swoim scope zmienną mainData. Jest ona powiązana z kontrolką znajdującą się w widoku. Developer chce aby wewnętrzny widok posiadał dwie kontrolki, jedna z nich ma być również powiązana ze zmienną mainData głównego scope’a (może tak zrobić w końcu wciąż znajduje się ‚pod kontrolą’ głównego kontrolera). Druga kontrolka odwołuje się do danych z wewnętrznego kontrolera. Idea jest taka, że zmiana w głównej kontrolce na być widoczna w pierwszej kontrolce wewnętrznej i na odwrót. Super. Gdy odpalamy aplikację, wpisanie danych do głównej kontrolki przynosi zamierzony efekt. Ta sama treść pojawia się w wewnętrznej kontrolce. Ale gdy tylko coś zostanie wpisane do wewnętrznej kontrolki, program się kaszani. Zewnętrzna kontrolka, nie zmienia już swojej treści, a gdy wpiszemy do niej jakąś wartość, wewnętrzna kontrolka przestaje reagować. Co się dzieje?

Odpowiedź, chociaż jest prosta, spędziła sen z powiek wielu devom. Otóż scope to obiekt, który jest dziedziczony protoypowo. Scope wewnętrznego Kontrolera dziedziczy dane z zewnętrznego. Gdy dane w zewnętrznym kontrolerze zmienią się, kontroler wewnętrzny znajduje je po łańcuchu prototypu. Natomiast gdy zmienimy dane w wewnętrznym kontrolerze, w jego scope powstanie ich wewnętrzna wersja i dane się „rozprzęgną”. Rozwiązaniem do tego było tworzenie danych wewnątrz obiektów, w ten sposób połączenia były referencyjne i nic się nie psuło. Stąd właśnie wzięła się zasada, że wartość dyrektywy ng-model musi zawierać kropkę.

I teraz czas na zwrot akcji. Oto jak ta sama sytuacja wygląda przy użyciu controller as:

Czyż ten kod nie jest czytelniejszy, od razu widać co skąd się bierze. I nie trzeba się martwić o to że scopey się „rozprzęgną”. Dlatego dla mnie, ta składnia wygrywa. Tak jak pisałem, nie jestem angularowym guru, więc mogę się mylić 🙂 jeżeli uważasz, że nie mam racji, daj znać w komentarzach, chętnie zapoznam się z Twoją opinią 🙂

I to wszystko na dziś. Odsłoniłem trochę tajemnej wiedzy angularowej i mam nadzieję, że ten wpis komuś pomoże w jego projekcie 🙂 .

Na koniec, jak zawsze, zachęcam do polubienia mojej strony na facebooku. Zawsze na bieżąco zamieszczam tam informacje o nowościach, więc warto polubić aby nie przegapić żadnego nowego wpisu.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *