Добро пожаловать в первую публикацию Angular View, серию статей о том, как фреймворк Angular обрабатывает представление в нашем структурированном приложении MVC (Model View Controller). В этом месяце мы собираемся подробно рассказать о том, как представление Angular работает в веб-приложениях. Давайте прямо сейчас!

При создании приложения Angular вы «объявляете» в своем NgModule все компоненты (классы и представления), которые может использовать ваше приложение.

@NgModule({
     declarations: [
          AppComponent
     ],
     imports: [
          BrowserModule,
          FormsModule,
          HttpModule
     ],
     providers: [],
     bootstrap: [AppComponent]
})
export class AppModule { }

NgModule предоставляет определение вашего приложения или только его часть. Сервисы, модули и другие предметы первой необходимости, которые требуются нашему приложению Angular для создания и обработки кода, который мы пишем, проходят через наш NgModule. В приведенном выше фрагменте вы можете увидеть компонент, изначально созданный Angular CLI, AppComponent, был немедленно загружен в наше приложение с объявлением (где мы определяем все наши представления), а также в начальной загрузке (где мы определяем наше начальное представление ). Однако есть три модуля, которые также импортируются в наше приложение; BrowserModule, FormsModule и HttpModule. Последние два из трех предоставляют разработчикам Angular некоторые невероятно важные функции для нашего приложения, но BrowserModule действительно является звездой (по крайней мере, для этого сообщения в блоге).

В Angular есть очень четкая абстракция от представления по сравнению с другими распространенными фреймворками JavaScript (в первую очередь React). Ниже приведен пример из Руководства по React представления, созданного в классе компонентов.

class ShoppingList extends React.Component {
     render() {
          return (
               <div className="shopping-list">
                    <h1>Shopping List for {this.props.name}</h1>
                    <ul>
                         <li>Instagram</li>
                         <li>WhatsApp</li>
                         <li>Oculus</li>
                    </ul>
               </div>
          );
     }
}

Если бы вы взяли образец фрагмента из учебника React и реализовали его в Angular, вероятно, потребовалось бы еще несколько файлов, но структура приложения все равно была бы очень организованной.

HTML-файл будет содержать что-то очень похожее на возврат функции рендеринга React.

<div class="shopping-list">
     <h1>Shopping List for {{props.name}}</h1>
     <ul>
          <li>Instagram</li>
          <li>WhatsApp</li>
          <li>Oculus</li>
     </ul>
</div>

Все представление управляется в отдельном HTML-файле, который связан с нашим компонентом через декоратор компонентов. Даже если вы не очень хорошо знакомы с декоратором компонентов, вы, вероятно, видели его в действии, не осознавая этого.

Декораторы - это особенность ES7, которая была завершена в июне 2016 года, но была реализована в TypeScript задолго до окончательной спецификации, чтобы разработчики Angular могли ею воспользоваться. Декораторы позволяют нам предоставлять метаданные для нашего класса. Метаданные описывают функциональность и другие подробности о нашем классе. В Angular наш декоратор компонентов позволяет нам связывать, какой стиль мы хотим для нашего компонента, каким должен быть селектор HTML и, что наиболее важно, каков шаблон для нашего компонента! Вот пример во фрагменте ниже:

@Component({
     selector: ‘app-shopping-list’,
     templateUrl: ‘./shopping-list.component.html’,
     styleUrls: [‘./shopping-list.component.css’]
})
export class ShoppingListComponent {}

Так как это работает? Как это вернуться к нашему BrowserModule? Не волнуйтесь, мы уже приближаемся.

Если вы потратите время на то, чтобы покопаться в своем BrowserModule, вы увидите, что он использует множество сервисов, которые манипулируют нашей DOM и создают элементы. Основным важным классом является класс DefaultDomRenderer2, от которого на самом деле происходят несколько других классов, который в основном используется в нашей службе DomRendererFactory2.

Здесь происходят две разные вещи. Во-первых, наш класс DefaultDomRenderer2 имеет несколько функций, которые напрямую управляют DOM для создания и управления нашим представлением. Этот класс не является эксклюзивным для наших компонентов Angular; помните, что наши пользовательские элементы, скорее всего, будут вызывать стандартные компоненты HTML (например, ваши теги H1, теги P, DIV и т. д.), а createElement () будет вызывать собственные API-интерфейсы DOM для генерации стандартных компонентов HTML с помощью document. createElement () function или ваши собственные с помощью document.createElementNS () function .

createElement(name: string, namespace?: string): any {
     if (namespace) {
          return document.createElementNS(NAMESPACE_URIS[namespace], name);
     }
     return document.createElement(name);
}

Фактически, если вы изучили манипуляции с DOM без JQuery, вы, вероятно, увидите множество функций, которые управляют узлом / компонентом DOM, а также позволяют добавлять дочерние элементы и нацеливаться на родительский узел. Это чистый, чистый JavaScript, настолько абстрагированный, что вам даже не нужно об этом думать или беспокоиться!

appendChild(parent: any, newChild: any): void { parent.appendChild(newChild); }
parentNode(node: any): any { return node.parentNode; }
addClass(el: any, name: string): void { el.classList.add(name); }
removeClass(el: any, name: string): void { el.classList.remove(name); }

Несколько других классов и служб расширяют DefaultDomRenderer2, но в основном он используется для службы DomRendererFactory2. Если вы не знакомы с тем, что такое фабрика, фабрики используются в объектно-ориентированном программировании (ООП) для создания и / или возврата новых объектов. В этом случае наши компоненты представления создаются в конструкторе нашей фабрики для нашего использования.

В результате наш BrowserModule может брать весь код, который мы пишем, и создавать представления, которые отображаются в браузере. Он использует чистый JavaScript для создания и управления нашим представлением через DomRendererFactory2 и DefaultDomRenderer2, но он также позволяет нам связывать наши события с нашими представлениями. Этим занимаются различные службы, которые также используют чистый JS для добавления прослушивателей событий к нашим веб-элементам. Знайте, что есть и другие важные элементы для нашей привязки данных и прослушивателей событий, которые могут быть не зависящими от браузера поведением, которые обрабатываются общей и базовой библиотеками Angular.

addEventListener(element: HTMLElement, eventName: string, handler: Function): Function {
     element.addEventListener(eventName, handler as any, false);
     return () => element.removeEventListener(eventName, handler as any, false);
}

tl;dr

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

Это все прекрасно, но некоторым из вас может быть интересно, зачем мы все это делаем. Зачем использовать абстрагированный модуль для генерации наших представлений или даже вообще абстрагировать наши представления от наших контроллеров / классов? Что ж, нам не нужно просто создавать наши приложения для Интернета. Есть много других платформ, на которых мы могли бы запустить наш код Angular, и в следующем месяце мы обсудим, как эта модель позволяет нам писать собственные приложения с Angular!