Это обновленная версия нашего предыдущего сообщения в блоге ZingChart и Angular 2.

С тех пор, как мы в последний раз публиковали сообщение в блоге об Angular 2 и ZingChart, в библиотеку были внесены значительные изменения. Следовательно, предоставленные нами демонстрации будут работать для Angular 2 beta 9, но не будут работать в более новых версиях. Следующий пример был написан для Angular 2 RC4.

Что нового в Angular 2 RC4

С тех пор, как мы опубликовали нашу последнюю запись в блоге, Angular 2 перешел от бета-версии 2.0.0 к версии-кандидату 2.0.0 (RC). Это означает, что код, с которым мы работаем, потенциально может стать выпущенной версией Angular 2. Вот что нового с момента нашего последнего сообщения в блоге…

Angular 2 теперь находится в области видимости @angular2.

// 2.0.0 beta 9 -------------------------
import { XXXX } from 'angular2/...'
// 2.0.0 RC4 ----------------------------
import { XXXX } from '@angular/...'

Расположение символа начальной загрузки изменилось

// 2.0.0 beta 9 -------------------------
import { bootstrap } from 'angular2/platform/browser'
// 2.0.0 RC4 ----------------------------
import { bootstrap } from '@angular/platform-browser'

Расположение символа компонента изменилось

// 2.0.0 beta 9 -------------------------
import { Component } from 'angular2/core'
// 2.0.0 RC4 ----------------------------
import { Component } from '@angular/core'

Расположение символа AfterViewInit

/// 2.0.0 beta 9 -------------------------
import { AfterViewInit } from 'angular2/core'
// 2.0.0 RC4 ----------------------------
import { AfterViewInit } from '@angular/core'

#item of items теперь разрешено item of items

/// 2.0.0 beta 9 -------------------------
template: '<div *ngFor="#item of items"></div>'
// 2.0.0 RC4 ----------------------------
template: '<div *ngFor="let item of items"></div>'

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

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

1) Создайте наш файл index.html

Здесь мы загружаем все необходимые файлы, чтобы запустить демоверсию Angular 2.

<!DOCTYPE html>  
<html>  
  <head>
    <title>ZingChart - Angular 2 RC4</title>
    <meta charset="UTF-8">
    <script src="https://npmcdn.com/core-js/client/shim.min.js"></script>
    <script src="https://npmcdn.com/[email protected]?main=browser"></script>
    <script src="https://npmcdn.com/[email protected]"></script>
    <script src="https://npmcdn.com/[email protected]/dist/system.src.js"></script>
    <script src="https://cdn.zingchart.com/zingchart.min.js"></script>
    <script src="systemjs.config.js"></script>
    <script>
      System.import('app').catch(function(err){ console.error(err); });
    </script>
  </head>
  <body>
    <my-app>Loading...</my-app>
  </body>
</html>

В этот элемент ‹my-app›‹/my-app будет внедрено наше приложение. Довольно круто!

2) Создаем наш файл systemjs.config.js

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

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

/**
 * PLUNKER VERSION (based on systemjs.config.js in angular.io)
 * System configuration for Angular 2 samples
 * Adjust as necessary for your application needs.
 */
(function(global) {
  var ngVer = '@2.0.0-rc.4'; // lock in the angular package version; do not let it float to current!
  var routerVer = '@3.0.0-beta.1'; // lock router version
  var formsVer = '@0.2.0'; // lock forms version
  var routerDeprecatedVer = '@2.0.0-rc.2'; // temporarily until we update all the guides
  //map tells the System loader where to look for things
  var  map = {
    'app':                        'app',
    '@angular':                   'https://npmcdn.com/@angular', // sufficient if we didn't pin the version
    '@angular/router':            'https://npmcdn.com/@angular/router' + routerVer,
    '@angular/forms':             'https://npmcdn.com/@angular/forms' + formsVer,
    '@angular/router-deprecated': 'https://npmcdn.com/@angular/router-deprecated' + routerDeprecatedVer,
    'angular2-in-memory-web-api': 'https://npmcdn.com/angular2-in-memory-web-api', // get latest
    'rxjs':                       'https://npmcdn.com/[email protected]',
    'ts':                         'https://npmcdn.com/[email protected]/lib/plugin.js',
    'typescript':                 'https://npmcdn.com/[email protected]/lib/typescript.js',
 };
  //packages tells the System loader how to load when no filename and/or no extension
  var packages = {
    'app':                        { main: 'main.ts',  defaultExtension: 'ts' },
    'rxjs':                       { defaultExtension: 'js' },
  };
  var ngPackageNames = [
    'common',
    'compiler',
    'core',
    'http',
    'platform-browser',
    'platform-browser-dynamic',
    'upgrade',
  ];
  // Add map entries for each angular package
  // only because we're pinning the version with `ngVer`.
  ngPackageNames.forEach(function(pkgName) {
    map['@angular/'+pkgName] = 'https://npmcdn.com/@angular/' + pkgName + ngVer;
  });
  // Add package entries for angular packages
  ngPackageNames.forEach(function(pkgName) {
    // Bundled (~40 requests):
    packages['@angular/'+pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' };
    // Individual files (~300 requests):
    //packages['@angular/'+pkgName] = { main: 'index.js', defaultExtension: 'js' };
  });
  // No umd for router yet
  packages['@angular/router'] = { main: 'index.js', defaultExtension: 'js' };
  // Forms not on rc yet
  packages['@angular/forms'] = { main: 'index.js', defaultExtension: 'js' };
  // Temporarily until we update the guides
  packages['@angular/router-deprecated'] = { main: '/bundles/router-deprecated' + '.umd.js', defaultExtension: 'js' };
  var config = {
    // DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER
    transpiler: 'ts',
    typescriptOptions: {
      tsconfig: true
    },
    meta: {
      'typescript': {
        "exports": "ts"
      }
    },
    map: map,
    packages: packages
  };
  System.config(config);
})(this);

3) Создаем наш файл tsconfig.json

Здесь мы указываем, как мы хотим, чтобы TypeScript компилировал наши файлы .ts. Опять же, мы копируем его прямо из примера быстрого запуска Angular 2.

Он указывает, что мы хотим, чтобы наш TypeScript компилировался в ES5 для совместимости и использовал формат CommonJS для загрузки модуля.

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": true,
    "suppressImplicitAnyIndexErrors": true
  }
}

4) Создаем каталог нашего приложения

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

mkdir app && cd app

5) Создайте файлы модели диаграммы.

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

export class Chart {  
  id: String;
  data: Object;
  height: any;
  width: any;
  constructor (config: Object) {
    this.id = config.id;
    this.data = config.data;
    this.height = config.height || 400;
    this.width = config.width || '100%';
  }
}

Мы используем классы ES6/ES2015 для создания класса Chart со следующими атрибутами: идентификатор, данные, высота и ширина. Мы используем TypeScript для объявления ожидаемых типов.

  • id — это строка, которая относится к узлу DOM, на котором мы будем отображать нашу диаграмму.
  • data — это объект, который настраивает внешний вид нашей диаграммы, какие данные она отображает и т. д.
  • высота и ширина могут быть строками («100%) или числами (300). Таким образом, мы используем тип any для них.

Конструктор вызывается при создании нового экземпляра Chart. При этом мы принимаем объект aconfig и сопоставляем свойства конфигурации с экземпляром. Мы объявляем, что высота должна быть равна 400, если в конфигурации нет атрибута высоты, а ширина должна быть «100%», если в конфигурации нет атрибута ширины. В противном случае по умолчанию используется то, что установлено в конфигурации.

Наш класс Chart экспортируется в начало файла (*export* class Chart). Это позволяет нам использовать его внутри других файлов, ЧТО МЫ СОБИРАЕМСЯ СДЕЛАТЬ! Будьте готовы к этому!

Поздравляю, вы попали! Реквизит для того, чтобы придерживаться его. Теперь мы собираемся перейти к самой крутой части этого урока: Angular 2.

6) Создайте наш компонент ZingChart

Пришло время покопаться в компонентах Angular 2! Давайте создадим файл внутри каталога нашего приложения с именем zingchart.component.ts. Здесь мы создадим фактический компонент ZingChart, который будем использовать в нашем приложении. Довольно круто.

Вот как выглядит код…

import { Component, NgZone, AfterViewInit, OnDestroy } from '@angular/core';  
import { Chart } from './chart.model';
@Component({
  selector: 'zingchart',
  inputs: ['chart'],
  template: '<div id="{{chart.id}}"></div>'
})
export class ZingChart implements AfterViewInit, OnDestroy {  
  chart: Chart
  constructor (private zone: NgZone) { }
  ngAfterViewInit () {
    this.zone.runOutsideAngular(() => zingchart.render(this.chart));
  }
  ngOnDestroy () {
    zingchart.exec(this.chart.id, 'destroy');
  }
}

Теперь мы готовим на газу! У нас здесь очень много всего происходит. Разбираемся шаг за шагом…

Загрузите необходимые элементы Angular 2

import { Component, NgZone, AfterViewInit, OnDestroy } from '@angular/core';

Здесь мы загружаем основную магию Angular 2, которая нам понадобится. Компонент позволяет нам создавать многократно используемые элементы, которые функционируют как обычный HTML, но гораздо более мощные. NgZone используется для работы с мощной библиотекой ZingChart и позволяет эффективно отображать ее внутри Angular 2. AfterViewInit позволяет нам выполнять работу сразу после представление инициализируется. Наконец, OnDestroy позволяет нам выполнять работу непосредственно перед уничтожением представления. Довольно круто.

Загрузите модель диаграммы

import { Chart } from './chart.model`;

Эй, это выглядит знакомо! Помните класс Chart, который мы создали на шаге 5? Тот, что с изящным экспортом? Теперь пользуемся!

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

Создайте компонент

@Component({
  selector: 'zingchart',
  input: ['chart'],
  template: '<div id="{{ chart.id }}"></div>'
})

Помните первую строку файла, где мы импортировали кучу вещей из @angular/core? @Component был одним из них! @Component() — одна из новейших частей Angular 2. Она называется декоратором, а в Angular 2 есть несколько разных встроенных декораторов. Они позволяют нам добавлять метаданные к классу, его членам и аргументам функции. Он должен располагаться непосредственно над объявлением класса.

Так что же происходит в нашем декораторе @Component()?

  • selector позволяет нам указать собственное имя тега HTML. Мы называем наш «zingchart», что позволяет нам позже создавать элементы «zingchart» / zingchart. Довольно прикольно!
  • input позволяет указать, к каким атрибутам родительского элемента мы хотим получить доступ. Я обещаю, что на следующем шаге это станет более ясно.
  • Шаблон позволяет нам объявить, какой HTML мы хотим внутри нашего пользовательского элемента ‹zingchart›‹/zingchart›. У нас все очень просто: это просто ‹div› с идентификатором, который мы указываем в нашей модели. Двойные фигурные скобки ({{ }}) позволяют вставлять данные в наш шаблон. Мы вводим идентификатор диаграммы в шаблон.

Создайте и экспортируйте класс ZingChart

export class ZingChart implements AfterViewInit, OnDestroy {  
  chart: Chart
  constructor (private zone: NgZone) { }
  ngAfterViewInit () {
    this.zone.runOutsideAngular(() => zingchart.render(this.chart));
  }
  ngOnDestroy () {
    zingchart.exec(this.chart.id, 'destroy');
  }
}

Что у нас здесь? Во-первых, мы создаем (и экспортируем) класс ZingChart. Точно так же, как мы создали и экспортировали класс Chart ранее, это позволит нам позже загрузить класс ZingChart в наш код. Мы также заявляем, что наш класс ZingChart реализует интерфейсы OnInit и OnDestroy, которые мы загрузили в начале файла. Реализация — это что-то новое для TypeScript.

Мы объявляем, что наш класс ZingChart имеет атрибут с именем chart и имеет тип Chart (который мы импортировали в начале файла).

Внутри конструктора мы создаем приватную переменную с именем zone типа NgZone (которую мы импортировали в начале файла).

Интерфейсы AfterViewInit и OnDestroy позволяют нам привязаться к жизненному циклу компонента. AfterViewInit позволяет нам создать функцию ngAfterViewInit, которую Angular 2 вызывает автоматически сразу после инициализации представления компонента. OnDestroy позволяет нам создать функцию ngOnDestroy, которую Angular 2 вызывает прямо перед уничтожением представления компонента. Мы полагаемся на эти функции как для рендеринга диаграммы внутри ngAfterViewInit, так и для уничтожения диаграммы внутри ngOnDestroy.

Мы полагаемся на нашу зону внутри ngAfterViewInit для выполнения нашей функции рендеринга за пределами зоны Angular 2. Наш предыдущий пост в блоге содержит довольно краткое объяснение:

ZingChart использует события окна, такие как window.setTimeout и window.setInterval, для обновления визуальных изменений, таких как всплывающие подсказки или выделенное состояние на диаграмме. По умолчанию Zone.js также прослушивает эти события и рассматривает их как часть жизненного цикла приложения — нехорошо! Мы не хотим, чтобы небольшие визуальные изменения, которые предоставляет ZingChart, прослушивались Angular 2. Поэтому нам нужно визуализировать каждую диаграмму за пределами зоны.

Отрисовка диаграммы за пределами зоны позволяет остальной части приложения продолжать работать быстро.

Вау, это было много! В этом плане Angular 2 силен. Вы можете сделать совсем немного без тонны кода. Время двигаться дальше…

7) Создайте наш компонент приложения

Итак, начнем… Внутри каталога приложения создайте файл с именем app.component.ts. Здесь мы создадим элемент ‹my-app›‹/my-app›, который вы видели в файле index.html. Давайте бросим это внутрь…

import { Component } from '@angular/core';
import { ZingChart } from './zingchart.component';  
import { Chart } from './chart.model';
@Component({
  selector: 'my-app',
  template: '<zingchart *ngFor="let chart of charts" [chart]="chart"></zingchart>',
  directives: [ZingChart]
})
export class AppComponent {  
  charts: Chart[];
  constructor () {
    this.charts = [
      {
        id: 'chart-1',
        data: {
          type: 'bar',
          series: [{
            values: [2, 3, 4, 5, 3, 3, 2]
          }]
        },
        height: 300
      }
    ]
  }
}

Что тут происходит? Все начнет выглядеть похоже.

Импорт декоратора компонента

import { Component } from '@angular/core';

Как и в файле zingchart.component.ts, мы загружаем декоратор компонента, так как мы создаем компонент.

Импорт компонента ZingChart

import { ZingChart } from './zingchart.component`;

Помните тот файл zingchart.component.ts, который мы только что создали? Время загрузить его и, наконец, использовать.

Импорт модели диаграммы

import { Chart } from './chart.model';

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

Создайте компонент

@Component({
  selector: 'my-app',
  template: '<zingchart *ngFor="let chart of charts" [chart]="chart"></zingchart>',
  directives: [ZingChart]
})

Как и в нашем компоненте ZingChart, мы создаем новый компонент с HTML-тегом ‹my-app›‹/my-app› (который вы увидите внутри файла index.html). Внутри этого компонента есть тег ‹zingchart›‹/zingchart› (который мы создали в файле thezingchart.component.ts. Мы используем директиву *ngFor для перебора каждого объекта Chart в массиве диаграмм элемента, создавая новый тег ‹zingchart). ›‹/zingchart› для каждого элемента массива Затем мы привязываем объект Chart этой итерации к ключевому слову диаграммы.

Задерживать. Давайте быстро вернемся к декоратору zingchart.component.ts @Component. У него есть атрибут inputs: inputs: [‘chart’]. Эта «диаграмма» внутри массива inputs соответствует [chart]=»chart» в шаблоне app.component.ts, что позволяет нам получить доступ к родительским данным (объект диаграммы app.component.ts) внутри дочернего компонента (zingchart. компонент.ts). Изящный.

Наконец, в директивах мы указываем, какие компоненты нам нужны. Поскольку мы используем zingchart›‹/zingchart› внутри шаблона, нам нужно сообщить нашему компоненту, что нам нужно загрузить этот компонент. Без директив: объявление [ZingChart] мы бы получили один скучный недействительный HTML-тег под названием «zingchart» без ничего внутри вместо великолепной интерактивной диаграммы. Это было бы грустно.

Создайте и экспортируйте класс AppComponent

export class AppComponent {  
  charts: Chart[];
  constructor () {
    this.charts = [
      {
        id: 'chart-1',
        data: {
          type: 'bar',
          series: [{
            values: [2, 3, 4, 5, 3, 3, 2]
          }]
        },
        height: 300
      }
    ]
  }
}

Это становится действительно похожим сейчас. Мы снова создаем и экспортируем класс, на этот раз с именем AppComponent, который включает в себя атрибут, называемый диаграммами, который представляет собой массив диаграмм. Помните бит *ngFor="let chart of charts" из приведенного выше шаблона? Часть диаграмм ссылается на этот массив. Прохладный!

Затем мы создаем конструктор (снова) и устанавливаем массив диаграмм так, чтобы он включал один объект типа Chart. Эта часть представляет собой чистый синтаксис ZingChart. Посетите документы ZingChart, чтобы узнать больше.

Это все для app.component.ts! Не слишком потрепанный. Мы ПОЧТИ закончили. Еще один файл!

8) Создаем основной скрипт

Теперь пришло время связать все это вместе. Внутри каталога приложения создайте последний файл с именем main.ts. Бросьте это внутрь.

import { bootstrap } from '@angular/platform-browser-dynamic';
import { AppComponent } from './app.component';
bootstrap(AppComponent);

Намного меньше, чем обычно! Какое удовольствие! Давайте пробежимся по нему очень быстро…

Импортируйте функцию начальной загрузки

import { bootstrap } from '@angular/platform-browser-dynamic`;

Мы используем это для «загрузки» (или «запуска») нашего приложения.

Импорт компонента приложения

import { AppComponent } from './app.component`;

Помните тот крутой файл app.component.ts, который мы создали ранее? Теперь мы, наконец, используем его! Потрясающий!

Наконец, вызовите bootstrap с помощью AppComponent.

bootstrap(AppComponent);

Функция начальной загрузки запускает приложение с импортированным AppComponent. Вот и все! Наше приложение готово к работе!

9) Запустите веб-сервер и посетите URL!

Просто запустите простой веб-сервер и перейдите по URL-адресу, чтобы увидеть, как окупается ваша тяжелая работа. Если в вашей системе установлен Python, перейдите в основной каталог проекта (приложение выше) и запустите следующее…

python -m SimpleHTTPServer

Это запустит сервер на локальном хосте: 8000. Идите туда сейчас, и вы должны увидеть великолепную гистограмму! Ура!!! Поздравляю, амиго/амига. Ты сделал это.

Резюме

  • Мы рассмотрели, что изменилось по сравнению с нашей предыдущей демонстрацией Angular 2.0.0 beta 9 до Angular 2.0.0 RC4.
  • Мы рассмотрели шаблонный код, чтобы запустить красивое модульное приложение Angular 2.
  • Затем мы создали собственное приложение с компонентом ZingChart для создания красивых интерактивных диаграмм в Angular 2.

Похлопайте себя по спине. Сегодня был хороший день.

Куда пойти отсюда

Наша демонстрация была основана на существующей демонстрации быстрого запуска Angular 2 на сайте Angular 2. Если вы хотите начать дальше, попробуйте пройти Учебник Angular 2: Tour of Heroes.

Следите за нашим блогом. Мы продолжим выпускать новый контент для Angular 2 и продемонстрируем новые способы создания замечательных веб-приложений.

Дайте нам знать, как вы используете ZingChart и Angular 2 для построения графиков! Напишите нам в Twitter @ZingChart или отправьте электронное письмо по адресу [email protected]. Мы всегда рады общению.