Создание hacker news с angular 2 cli, rxjs и webpack, часть 1

Разработка /
Разработка: Создание hacker news с angular 2 cli, rxjs и webpack
Если вы когда-либо создавали приложения с Angular 2, то знаете, что первоначальная настройка проекта отнимает немало времени. К счастью, команда Angular создала Angular CLI — утилиту командной строки, облегчающую эту задачу.

В этой статье мы построим клиента Hacker News, используя Angular CLI, RxJS Observables и Webpack как загрузчик модулей.

Готовое приложение.
Исходный код.

Разработка: Создание hacker news с angular 2 cli, rxjs и webpack
Мы постепенно, шаг за шагом, пройдём весь процесс построения приложения и я постараюсь подробно объяснять важные моменты по ходу дела, а также сделанные мной ошибки и способы их решения.

Вот краткий список того, что нам предстоит сделать:

  1. Мы начнём с построения каркаса приложения, главной страницы Hacker News
  2. Затем подключим Observable Data Service для асинхронной загрузки данных
  3. Добавим роутинг с Angular Component Router для построения навигации между страницами и видами топиков
  4. И, наконец, добавим роуты чтобы пользователь мог перейти к комментариям к топику и в профили пользователей.

Приступим

Убедитесь, что у вас установлен Node и npm, затем установите CLI в терминале:

npm install -g @angular/cli

Создадим и запустим приложение:

ng new angular2-hn
cd angular2-hn
ng serve

Откройте браузер по адресу localhost:4200/

Разработка: Создание hacker news с angular 2 cli, rxjs и webpack, часть 1
Круто, да?

Настроим Sass как препроцессор CSS:

ng set defaults.styleExt scss

Создадим первый компонент HeaderComponent

ng generate component Header

Будет создана папка header, содержащая такие файлы:

  • header.component.scss
  • header.component.html
  • header.component.ts
  • header.component.spec.ts
Посмотрим на файл app.module.ts и увидим, что наш компонент уже задекларирован:

// app.module.ts

// ...
import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component';

@NgModule({
  declarations: [
    AppComponent,
    HeaderComponent
  ],
//...

А взглянув в файл header.component.ts, вы увидите, что селектор компонентов — app-header. Добавим его в наш корневой компонент, app.component.ts.

<!-- app.component.html -->

<app-header></app-header>

Запустим приложение. Компонент header загрузился нормально:

Разработка: Создание hacker news с angular 2 cli, rxjs и webpack, часть 1
Супер. Теперь добавим кое-какую разметку и стили.

<!-- app.component.html -->

<div id="wrapper">
  <app-header></app-header>
</div>

Стили из файла app.component.scss можете скачать здесь. Перейдём к header.

<!-- header.component.html -->

<header id="header">
  <a class="home-link" href="/">
    <img class="logo" src="https://i.imgur.com/J303pQ4.png" alt="Разработка: Создание hacker news с angular 2 cli, rxjs и webpack, часть 1" />
  </a>
  <div class="header-text">
    <div class="left">
      <h1 class="name">
        <a href="/">Angular 2 HN</a>
      </h1>
      <span class="header-nav">
        <a href="">new</a>
        <span class="divider">
          |
        </span>
        <a href="">show</a>
        <span class="divider">
          |
        </span>
        <a href="">ask</a>
        <span class="divider">
          |
        </span>
        <a href="">jobs</a>
      </span>
    </div>
    <div class="info">
      Built with <a href="https://cli.angular.io/" target="_blank">Angular CLI</a>
    </div>
  </div>
</header>

Стили этого компонента можно скачать здесь. Запустим приложение:

Разработка: Создание hacker news с angular 2 cli, rxjs и webpack, часть 1

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

Разработка: Создание hacker news с angular 2 cli, rxjs и webpack, часть 1
Но у нас с края появился непредвиденный отступ. Это из-за того, что элемент body имеет отступ по-умолчанию (через margin):

Разработка: Создание hacker news с angular 2 cli, rxjs и webpack, часть 1
Но если открыть app.component.scss, там указано правило margin: 0 для экранов меньше 768px:

$mobile-only: "only screen and (max-width : 768px)";

body {
  margin-bottom: 0;

  @media #{$mobile-only} {
    margin: 0;
  }
}

Так почему же оно так работает? Это из-за способа, которым Angular инкапсулирует CSS-стили компонента. Не будем вдаваться в детали, но есть три способа, которые может использовать Angular для этого:

  • None: Angular ничего не предпринимает — ни инкапсуляции, ни Shadow DOM, просто обычная загрузка стилей.
  • Emulated: Angular эмулирует поведение Shadow DOM. Это способ по-умолчанию.
  • Native: Angular использует нативный Shadow DOM браузера (только в браузерах, имеющих соответствующую поддержку).

В корневом компоненте мы добавляем стили элементу body, но оно не работает, потому что мы не указали Angular не применять никаких действий к представлению компонента:

// app.component.ts

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

@Component({
  selector: 'app-root',
  encapsulation: ViewEncapsulation.None,
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})

export class AppComponent {
}

Перезапустим приложение и посмотрим на него. Теперь стили применились как и должно.

Разработка: Создание hacker news с angular 2 cli, rxjs и webpack, часть 1

Несколько компонентов

Добавим ещё пару компонентов, Stories и Footer. Stories представляют топики в Hacker News, и мы начнём с каркаса, добавив в него упорядоченный список.

ng g component Stories

// stories.component.ts

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

@Component({
  selector: 'app-stories',
  templateUrl: './stories.component.html',
  styleUrls: ['./stories.component.scss']
})

export class StoriesComponent implements OnInit {
  items: number[];

  constructor() {
    this.items = Array(30);
  }

  ngOnInit() {
  }
}

<!-- stories.component.html -->

<div class="main-content">
  <ol>
    <li *ngFor="let item of items; let i = index" class="post">
      Story #{{i}}
    </li>
  </ol>
  <div class="nav">
    <a href="" class="prev">
      ‹ Prev
    </a>
    <a href="" class="more">
      More ›
    </a>
  </div>
</div>

Стили для Stories можно взять здесь. Подвал страницы очень простой (стили для него скачайте тут).

ng g component Footer

<!-- footer.component.html -->

<div id="footer">
    <p>Show this project some ❤ on
      <a href="https://github.com/housseindjirdeh/angular2-hn" target="_blank">
        GitHub
      </a>
    </p>
</div>

Обновим корневой компонент, чтобы увидеть добавленные компоненты:

<!-- app.component.html -->

<div id="wrapper">
  <app-header></app-header>
  <app-stories></app-stories>
  <app-footer></app-footer>
</div>

Вот так теперь выглядит приложение:

Разработка: Создание hacker news с angular 2 cli, rxjs и webpack, часть 1
Так как каждый топик, или элемент, будет иметь собственные атрибуты, имеет смысл создать отдельный компонент для этого.

ng g component Item

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

<!-- stories.component.html -->

<div class="main-content">
  <ol>
    <li *ngFor="let item of items; let i = index" class="post">
      <item class="item-block" itemID="{{ i + 1 }}"></item>
    </li>
  </ol>
  <div class="nav">
    <a href="" class="prev">
      ‹ Prev
    </a>
    <a href="" class="more">
      More ›
    </a>
  </div>
</div>

// item.component.ts

import { Component, Input, OnInit } from '@angular/core';

@Component({
  selector: 'item',
  templateUrl: './item.component.html',
  styleUrls: ['./item.component.scss']
})
export class ItemComponent implements OnInit {
  @Input() itemID: number;

  constructor() { }

  ngOnInit() {
  }

}

<!-- item.component.html -->

<p>Story #{{itemID}}<p>

Перезапустите приложение, всё должно работать так же, а это значит, что параметр позиции передаётся успешно с @Input.

Итак, у нас получился отличный каркас главной страницы. Здесь исходный код примера на текущем этапе.

Продолжение.
0 комментариев
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.