#2 ControllerCzęść NR.2O kontrolerach w Angular JS trzeba wiedzieć kilka rzeczy.

Kontroler ma za zadanie kontrolować przebieg informacji na stronie.

Zapisać je w scenariuszu, w którym edytujemy informacje.

Aby użyć kontrolera, najpierw trzeba do strony dodać nową dyrektywę “ng-controller”.

ng-controller to atrybut, który umieszczamy w kodzie HTML. Podobnie jak atrybut ng-app, który omówiliśmy wcześniej, ng-controller jest kolejną dyrektywą.

<body>
    <div ng-app=""> 
        <div ng-controller="MainController">

        </div>
    </div>
</body>

Gdy używamy ng-controller musimy sprecyzować jego nazwę. W kodzie powyżej dyrektywa ng-app uruchomi Angulara i zacznie skanować elementy wewnątrz niego.

Co to oznacza? Oznacza to, że ng-controller może być tylko użyty wewnątrz elementu, który posiada ng-app.

Musimy nazywać kontrollery ponieważ w dużej aplikacji będziemy mieć ich więcej niż jeden.

Teraz powstaje pytanie skąd Angular wie, gdzie ten element żyje?. Odpowiedź jest następująca:

Musimy stworzyć funkcję Angular, która będzie wywoływać ten kontroler.

Funkcje tę można przypisać do zmiennej o podobnej nazwie.

<script>
    var MainController = function ($scope) {

        $scope.mytext = "Angular JS Rulez";

    }
</script>

Jak widać na powyższym kodzie, kontrolery to tak naprawdę funkcje. Funkcje te nigdy nie są uruchamiane przez nas. Angular zrobi to za nas wtedy, gdy ten kontroler będzie potrzebny do zarządzania danym obszarem HTML.

Gdy Angular tworzy nasz kontroler wysyła parametr do naszej funkcji. Parametr ten nazywa się “$scope”.

Pracując z Angularem spotkasz dużo zmiennych zaczynających się od znaku dollara.

Podobnie jak prefiks “ng”, prefiks dollara jest znakiem, że komponent, z którym pracujesz jest komponentem otrzymanym przez Angular.

Co możemy zrobić z $scope?

Możemy przypisać model do obiektu $scope.

$scope nie jest modelem, ale rzeczy przypisane do $scope nim będą. Na chwilę obecną przypisaliśmy jedną rzecz do $scope o nazwie “mytext”.

Teraz wewnątrz HTML możemy to wykorzystać i połączyć z wybranym elementem.

<body>
    <div ng-app> 
        <div ng-controller="MainController">
            <p>{{mytext}}</p>
        </div>
    </div>
</body>

By zobaczyć kontrolery w akcji. Wróć do Plunker i uzupełnij kod.

Angularefsds

Prawda, że było to proste. Przejdźmy więc dalej.

Model Kontroler i Widok

Widzieliśmy już kontroler w akcji. Widzieliśmy, że główna odpowiedzialność kontrolera polega na ustawieniu modelu do obiektu “$scope”. Co się odbywa w jego funkcji JavaScript.

Angular przekazuje “$scope” do funkcji, która nim manipuluje.

image

Zauważ, że kontroler nigdy bezpośrednio nie manipuluje stroną HTML. Kod HTML możemy nazwać widokiem.

Kontroler manipuluje tylko zmienną “$scope” poprzez dodanie modelu.

W samym widoku obecnie korzystam tylko z prostego wyrażenia, żeby wyświetlić to, co przekazałem.

image

W następnych wpisach pokażę jak w funkcja kontrolera odpowiada na zdarzenia użytkownika, takie jak kliknięcie.

Robi się to za pomocą innych dyrektyw. Jak widzisz filozofią Angulara jest elastyczna manipulacji HTML, żeby nie użyć słowa odwróconej zależności. To nie widok decyduje, co ma się stać, tylko kontroler.

Kontroler manipuluje “$scope”, który przekazuje model. Widok natomiast martwi się dyrektywami.

Jak widzisz jest to zupełni inny świat niż jQuery. Tutaj nie manipulujemy elementami HTML bezpośrednio. jQuery wyszukuje elementy po klasie lub po innych atrybutach i na tej podstawie odbywa się manipulacja.

Angular jest lepszym rozwiązaniem, gdyż nasza zależność od widoku czyli kodu HTML nie jest tak duża.

Co możemy zrobić z kontrolerami?

Co możemy zrobić z kontrolerami?

W większych aplikacjach możemy mieć ich dużo więcej. Nie ma niczego dziwnego w posiadaniu wielu kontrolerów, nawet na tej samej stronie.

<body>
    <div ng-app> 
        <div ng-controller="MainController">
            <p>{{mytext}}</p>
        </div>
        <h1>Test Angular</h1>
        <div ng-controller="PersonController">
            <div>Imie: {{name}}</div>
            <div>Nazwisko: {{ surname }}</div>
        </div>
        <div ng-controller="SkillsController">
            <div>Programowanie w C#: {{ programing.csharp }}</div>
            <div>
                Przemawianie w mowach humorystycznych:
                {{ speech.humorous }}
            </div>
        </div>
        <div ng-controller="PhotoController">
            <img src="{{facephoto.Source}}"
                 alt="{{facephoto.description}}" />
        </div>
    </div>
</body>

Każdy kontroler powinien być odpowiedzialny tylko za jedną funkcję. Powyżej znajduje się kod z trzema kontrolerami.

Jest możliwe używanie właściwości bardziej zaszytych w obiekcie, jak “programing.scharp”.

Jest także możliwe ustawienie atrybutów HTML na podstawie informacji zawartych w modelach. Mogę na przykład ustawić ścieżkę do obrazka i jego opis.

Oznacza to, że nie jesteśmy oczywiście zmuszeni tylko do wyświetlania zawartości tekstowych w modelach.

Ten przykład nie jest jednak doskonały. Napiszmy więc kontroler do powyższego widoku i zobaczymy, czy wyświetli on dane, tak jak chcemy.

Wróćmy więc do Plunker. Do pliku “script.js” dodajmy następujący kod:

Zadeklarujmy funkcję “PersonController.

var PersonController = function ($scope) {
    $scope.name = "Cezary"
    $scope.surname = "Walenciuk"
}

Warto wspomnieć o błędzie, który każdy może popełnić. Obiekt $scope zawiera dużo specjalnych parametrów.

image

Przenigdy nie powinniśmy go nadpisywać. Składnia poniższa jest przydatna i bardzo skraca kod, co pokażę później. Jednak w tym wypadku nie możemy jej zastosować bo ona nadpisuje cały obiekt.

var PersonController = function ($scope) {
    $scope = {
        name : "Cezary",
        surname : "Walenciuk"
    };
}

Pozbawiając go tych właściwości, obiekt nie będzie później powiązywał się z elementami HTML.

image

Zastosujmy więc prawidłową składnię.

var PersonController = function ($scope) {
    $scope.name = "Cezary"
    $scope.surname = "Walenciuk"
}

SkillsController składa się z dwóch bardziej złożonych obiektów. Możemy więc zastosować inicjalizator JavaScript.

var SkillsController = function ($scope) {
    var programing = {
        csharp: "Very Good"
    }

    var speaches = {
        humorous : "Average"
    }

    $scope.programing = programing;
    $scope.speaches = speaches;
}

“PhotoController” zawiera dwie właściwości. Jedna określa ścieżkę do zdjęcia, druga jego opis.

var PhotoController = function ($scope) {
    var facephoto = {
        Source : "http://www.cezarywalenciuk.pl/my/CezaryWalenciuk.png",
        description: "My face"
    };

    $scope.facephoto = facephoto;
}

Cały kod javascript powinien wyglądać tak:

var MainController = function ($scope) {

    $scope.mytext = "Hello";

}

var PersonController = function ($scope) {
    $scope.name = "Cezary"
    $scope.surname = "Walenciuk"
}

var SkillsController = function ($scope) {
    var programing = {
        csharp: "Very Good"
    }

    var speaches = {
        humorous : "Average"
    }

    $scope.programing = programing;
    $scope.speech = speaches;
}

var PhotoController = function ($scope) {
    var facephoto = {
        Source : "http://www.cezarywalenciuk.pl/my/CezaryWalenciuk.png",
        description: "My face"
    };

    $scope.facephoto = facephoto;
}

Jeśli jednak uruchomisz ten kod w plunker lub w Visual Studio zauważysz pewien problem.

plunker agularJS'

Zanim kod JavaScript z Angular się wykona przeglądarka zdąży wysłać zapytanie o zdjęcie o nazwie {{facet.Source}}.

Uruchamiając kod w konsoli można sprawdzić, że rzeczywiście tak jest.

image

Na szczęście ten problem można rozwiązać stosując dyrektywę “ng-src”.

<div ng-controller="PhotoController">
    <img ng-src="{{facephoto.Source}}"
            alt="{{facephoto.description}}" />
</div>

Co się jednak stanie, gdy obiekt nie posiada danej właściwości, a HTML określimy taką właściwość.

Otóż nic się nie wydarzy.

Nie pojawi się żaden błąd.

Mechanizm działa tak ponieważ Angular oczekuje, że ten uzupełniony obiekt może się pojawić później.

Przykładowo wysłaliśmy zapytanie AJAX do usługi typu REST i oczekujemy na zwrot wypełnionego obiektu, którego obecnie nie mamy.

O wywołaniach http w Angular w następnym wpisie.