Вступление


Эта статья основана на документации Angular 2 и представляет собой перевод двух статей — Вступление и Редактор героя.


Используется Angular 2 release is beta.12.


Тур героев: обзор


Наш великий план — построить приложение для кадрового агентства героев. Даже героям нужна работа!


Конечно, в этом учебнике будет рассмотрены только основные понятия. То, что мы создаем будем иметь много функций, которые мы ожидаем найти в полномасштабных, ориентированных на данные приложениях: получение и отображение списка героев, редактирование информации о выделенном герое, навигация между различными представления данных о героях.


Тур героев покрывают основы Angular2. Мы будем использовать встроенные директивы для того чтобы показать/скрыть элементы и отобразить списки данных героев. Мы создадим компонент для отображения данных героя и другой компонент для отображения множества героев. Мы будем использовать одностороннюю привязку данных для тех данных, которые предназначены только для чтения. Мы добавим редактируемые поля для обновления модели используя двустороннее связывание данных. Мы будем связывать методы компонентов с пользовательскими событиями, такими как нажатия клавиш и щелчки мыши. Мы научимся, выбирать героя из главного списка и редактировать этого героя в представлении деталей. Мы будем форматировать данные, используя каналы. Мы создадим общую службу, чтобы собрать наших героев. И мы будем использовать маршрутизацию для навигации между различными видами и их компонентами.


Мы узнаем Angular2 достаточно для того, чтобы начать работу и получить уверенность, что Angular2 может все, что нам необходимо. Многое мы будем рассматривать поверхностно, но будут присутствовать ссылки для углубленного изучения.


Запустить пример приложения.


Конец игры


Вот наглядное представление о том, к чему мы придем в этом туре. Начнем с вида "Обзорная панель" (Dashboard) и наших самых героических героев:


heroes-dashboard


Над панелью находятся две ссылки: Dashboard и Heroes. Мы могли бы перемещаться между представлениями героев, нажимая на эти ссылки. Но мы нажмем на героя с именем "Magenta", и маршрутизатор отобразит нам вид "Детали героя", где мы можем изменить имя героя.


hero-details


Нажатие на кнопку "Назад" вернет нас к виду "Dashboard". Ссылки вверху могут привести нас к одному из основных видов. Нажмем на "Heroes" и приложение перейдет к списку героев.


heroes-list


Если мы щелкнем другого героя и внизу появится мини-детальная отразит наш выбор. Нажатие на кнопку "View Details" отобразит уже знакомый вид, где мы можем редактировать детали нашего выбранного героя. Следующая диаграмма отражает все возможные варианты навигации.


nav-diagram


Вот наше приложение в действии:


toh-anim


Далее


Мы будем создавать этот тур героев вместе, шаг за шагом. Мы будем обосновывать каждый шаг на основе тех требований, что мы встречали в бесчисленных приложениях. Всему есть причина. И мы встретим многие из основ Angular2 на этом пути.


Давайте начнем!


Часть 1 — Редактор героя


Давным-давно


Каждая история начинается где-нибудь. Наша история начинается там, где заканчивается QuickStart.


Создайте папку с именем angular2-tour-of-heroes и выполните шаги из QuickStart, которые обеспечивают необходимые условия, структуру папок и файлов для нашего тура героев.


Но вместо этого вы можете скачать готовые исходные файлы QuickStart.

У нас должна быть следующая структура:


angular2-tour-of-heroes
    app
        app.component.ts
        main.ts
    node_modules ...
    typings ...
    index.html
    package.json
    styles.css
    tsconfig.json
    typings.json

Поддержка преобразования кода и выполнения приложения


Мы хотим запустить компилятор TypeScript, чтобы при этом он отслеживал изменения в файлах и сразу осуществлял компиляцию, и запустить наш web-сервер. Мы сделаем это, набрав:


npm start

Эта команда запустит компилятор в режиме наблюдения, запустит сервер, запустит приложение в браузере и будет отслеживать изменения, пока мы будем создавать тур героев.


Покажем нашего героя


Мы хотим отобразить данные героя в нашем приложении.
Давайте добавим два свойства к нашему AppComponent, свойство title для имени приложения и свойства hero для героя с именем "Windstorm".


app.component.ts (AppComponent class)


export class AppComponent {
  public title = 'Tour of Heroes';
  public hero = 'Windstorm';
}

Теперь мы обновляем шаблон в аннотации @Component, добавив связывание данных для этих новых свойств.


template: '<h1>{{title}}</h1><h2>{{hero}} details!</h2>'

Браузер должен обновить страницу и отобразить наше название и героя.


Двойные фигурные скобки говорят нашему приложению, что нужно прочитать название и свойства героя из компонента и отобразить их. Это "интерполяционная" форма односторонней привязки данных.


Подробнее об интерполяции в главе Отображение данных

Объект героя


В данный момент наш герой — это просто имя. Наш герой нуждается в большем количестве свойств. Давайте преобразуем героя из строки в класс.


Создайте класс героя со свойствами id и name. Сейчас разместим это в верхней части файла app.component.ts, чуть ниже оператора импорта.
app.component.ts (Hero class)


export class Hero {
  id: number;
  name: string;
}

Теперь, когда у нас есть класс героя, давайте отрефакторим свойство hero вашего компонента, чтобы он имел тип Hero. Затем инициализируем его с id 1 и name "Windstorm".
app.component.ts (Hero property)


public hero: Hero = {
  id: 1,
  name: 'Windstorm'
};

Так как мы изменили героя из строки в объект, мы обновляем связывание в шаблоне, чтобы ссылаться на свойство героя name.


template: '<h1>{{title}}</h1><h2>{{hero.name}} details!</h2>'

Браузер обновляется и продолжает отображать имя нашего героя.


Добавим еще HTML


Отображение имени это хорошо, но мы хотим увидеть все свойства нашего героя. Мы добавим один <div> для нашего свойства id и другой <div> для свойства name героя.


template: '<h1>{{title}}</h1><h2>{{hero.name}} details!</h2><div><label>id: </label>{{hero.id}}</div><div><label>name: </label>{{hero.name}}</div>'

Ой-ой, наша строка шаблона становится длинной. Нужно позаботиться о том, чтобы избежать риска сделать опечатку в шаблоне.
app.component.ts (AppComponent's template)


template:`
      <h1>{{title}}</h1>
      <h2>{{hero.name}} details!</h2>
      <div><label>id: </label>{{hero.id}}</div>
      <div><label>name: </label>{{hero.name}}</div>
      `

Редактирование нашего героя


Мы хотим иметь возможность редактировать имя героя в текстовом поле.
Изменим имя героя — с <label> на <label> и <input>, как показано ниже:
app.component.ts (input element)


template:`
  <h1>{{title}}</h1>
  <h2>{{hero.name}} details!</h2>
  <div><label>id: </label>{{hero.id}}</div>
  <div>
    <label>name: </label>
    <input value="{{hero.name}}" placeholder="name">
  </div>
  `

Мы видим в браузере, что имя героя появляется в текстовом поле <input>. Но что-то здесь не так. Когда мы меняем имя, мы замечаем, что наше изменение не отражается в <h2>. Мы не получим желаемого поведения, используя одностороннее связывание к <input>.


Двустороннее связывание


Мы намерены вывести имя героя в <input>, изменить его и увидеть эти изменения везде, где они связаны с именем героя. Короче говоря, мы хотим, чтобы связывание данных было двухсторонним.
Давайте обновим шаблон для использования встроенной директивы ngModel для двухстороннего связывания.


Узнать больше о ngModel можно в главах Формы и Синтаксис шаблонов.

Замените <input> на следующий HTML


<input [(ngModel)]="hero.name" placeholder="name">

Браузер обновляется. Мы снова видим нашего героя. Мы можем изменить имя героя и увидеть, что изменения сразу же отражаются в <h2>.


Путь, по которому мы прошли


Давайте подведем итоги того, что мы сделали.


  • Наш Тур героев использует двойные фигурные скобки интерполяции (форма односторонней привязки данных), чтобы отобразить название приложения и свойства объекта Hero.
  • Мы написали шаблон из нескольких строк, используя строки шаблонов ES2015, чтобы сделать наш шаблон более читабельным.
  • Мы можем одновременно отобразить и изменить имя героя добавлением двухстороннего связывания данных к элементу <input>, используя встроенную в ngModel директиву.
  • Директива ngModel также передает изменения любой другой связке hero.name.

Вот полное содержимое app.component.ts, на текущий момент:
app.component.ts


    import {Component} from 'angular2/core';

    export class Hero {
      id: number;
      name: string;
    }

    @Component({
      selector: 'my-app',
      template:`
        <h1>{{title}}</h1>
        <h2>{{hero.name}} details!</h2>
        <div><label>id: </label>{{hero.id}}</div>
        <div>
          <label>name: </label>
          <input [(ngModel)]="hero.name" placeholder="name">
        </div>
        `
    })
    export class AppComponent {
      public title = 'Tour of Heroes';
      public hero: Hero = {
        id: 1,
        name: 'Windstorm'
      };
    }

Предстоящий путь


Наш Тур героев отображает только одного героя, в то время как мы хотим отображать список героев. Мы также хотим позволить пользователю выбрать героя и отобразить его данные. Мы узнаем больше о том, как получать списки, привязать их к шаблону, и позволить пользователю делать в нем выбор в следующей главе.


Результат первой части:


Запустить приложение, часть 1

Комментарии (11)


  1. DeeplessHole
    08.04.2016 02:04
    +4

    Для понимания этих гайдов нужен самый минимальный уровень английского. Стоило ли переписывать то же самое(тем более, что различных гайдов разного уровня по Angular 2 под TypeScript пруд пруди)?

    А вот структурированной подробной информации по Angular 2 под JavaScript особо и нет, с удовольствием почитал бы такую статью, пусть и на английском.

    P.S. Перевел обзорную часть по архитектуре Angular 2, переделав примеры под ES5: Angular 2 in JavaScript. Также реализовал поэтапно туториал «Tour of Heroes» на ES5: JavaScript реализация Tour of Heroes. Надеюсь, кому-то поможет :)


    1. Andzhik
      08.04.2016 19:22

      Спасибо за отличные переводы на русский и ES5. Только вот в сервисе героев (HeroService) из «Angular 2 in JavaScript» используются промисы. Это же вроде ES6. Чем это можно заменить? Нужна поддержка IE10.


      1. bromzh
        08.04.2016 20:02

        Чем это можно заменить?

        Observable. Собственно, в ангуляре же Http сервис теперь возвращает не промисы, а Observable из RxJS. А для es6 промисов можно полифил подключить.


      1. DeeplessHole
        08.04.2016 21:36

        Рад, что Вам понравилось. Момент с Promise: на самом деле он не нужен в этом туториале, он скорее для того, чтобы показать, что сервисы также используются для получения удаленных данных. Так что почти честно :)

        Чем заменить? Поискать библиотеки, либо реализовать самому. По идее, вещь не сильно сложная.

        З.Ы. Только приступаю к изучению JavaScript, прошу сильно не пинать.


      1. Large
        09.04.2016 00:13

        Можно просто подключить полифил bluebird с реализацией промисов на ES5


  1. stanr
    08.04.2016 10:55

    Полу-машинный перевод без учета лингвистических особенностей обоих языков… При переводе тех. статей — бессмысленный и беспощадный.


  1. navar
    10.04.2016 08:52

    Возьмем из статьи класс Hero. Допустим, мы хотим иметь возможность сохранять героя в базе данных на сервере. Добавим герою метод save(), который для сохранения героя использует Angular-сервис Http. Каким образом должен резолвится этот (Http) сервис в классе Hero, чтобы была возможность использовать следующий подход?


    var hero = new Hero();
    hero.name = 'Windstorm';
    hero.save();


    1. bromzh
      10.04.2016 15:44

      Добавим герою метод save()

      Такие вещи лучше вынести в отдельный сервис. Тут Hero — это DTO, у него нет поведения.


      Каким образом должен резолвится этот (Http) сервис в классе Hero

      Что подразумевается под этим? Какой коллбек в then засунуть? Ну если уж и идти по такому сомнительному пути, то нужно просто обновить поля Hero новыми значениями.
      Да и нынче методы Http не возвращают промисы, их заменили Observable-объекты.


      1. navar
        10.04.2016 16:38

        Извините, прикрепилось отдельной веткой ниже.


  1. navar
    10.04.2016 16:37

    Такие вещи лучше вынести в отдельный сервис.

    Имеете в виду сделать сервис типа HeroSaver? Писать что-то вроде:


    var hero_saver = injector.get(HeroSaver);
    var hero = new Hero();
    hero.name = 'Windstorm';
    hero_saver.save(hero);

    Тут Hero — это DTO, у него нет поведения.

    Да, но, допустим нужно реализовать поведение, например, как в Django.


    hero = Hero(name="Windstorm")
    hero.save()

    Что подразумевается под этим? Какой коллбек в then засунуть?

    Каким образом правильно было бы получить сервис Http в объекте класса Hero, чтобы реализовать функционал save? Как пример реализации:


    class Hero {
      http:Http;
      constructor(injector:Injector) {
        this.http = injector.get(Http);
      }
      save () {
        this.http.post.....
      }
    }
    ....
    var hero = new Hero(injector);
    hero.name = 'Windstorm';
    hero_saver.save(hero);


    1. bromzh
      10.04.2016 18:39

      Правильнее — делать через внедрение зависимостей, никаких injector.get. В джанге внедрения нет, поэтому там по-другому.