Компонентите са достъпни в Angular от самото начало; много хора обаче все още се оказват да използват компоненти неправилно. В работата си съм виждал хора, които изобщо не ги използват, създават компоненти вместо директиви за атрибути и други. Това са въпроси, които младши и старши Ъглови разработчици са склонни да правят, включително и аз. И така, тази публикация е за хора като мен, когато учех Angular, тъй като нито официалната документация, нито неофициалната документация за компонентите обясняват със случаи на употреба как и кога да се използват компонентите.
В тази статия ще посоча правилните и неправилни начини за използване на ъглови компоненти, с примери. Тази публикация трябва да ви даде ясна представа за:
Преди да можем да преминем към правилното използване на Angular компоненти, искам накратко да засегна темата за компонентите като цяло. Най-общо казано, всяко приложение Angular има поне един компонент по подразбиране - root component
Оттам нататък зависи от нас как да проектираме нашето приложение. Обикновено бихте създали компонент за отделни страници и след това всяка страница ще съдържа списък с отделни компоненти. Като основно правило компонентът трябва да отговаря на следните критерии:
Нека си представим сценарий, при който имаме приложение, което има две страници: Предстоящи задачи и Изпълнени задачи . В Предстоящи задачи страница, можем да видим предстоящите задачи, да ги маркираме като „готови“ и накрая да добавим нови задачи. По същия начин в Изпълнени задачи страница, можем да видим изпълнените задачи и да ги маркираме като „отменени“ И накрая, имаме навигационни връзки, които ни позволяват да навигираме между страниците. Като се има предвид това, можем да разделим следната страница на три раздела: корен компонент, страници, компоненти за многократна употреба.
Въз основа на екранната снимка по-горе можем ясно да видим, че структурата на приложението ще изглежда по следния начин:
── myTasksApplication ├── components │ ├── header-menu.component.ts │ ├── header-menu.component.html │ ├── task-list.component.ts │ └── task-list.component.html ├── pages │ ├── upcoming-tasks.component.ts │ ├── upcoming-tasks.component.html │ ├── completed-tasks.component.ts │ └── completed-tasks.component.html ├── app.component.ts └── app.component.html
Така че нека свържем файловете на компонентите с действителните елементи на каркаса по-горе:
header-menu.component
и task-list.component
са компоненти за многократна употреба, които се показват със зелена граница на екранната снимка на каркаса;upcoming-tasks.component
и completed-tasks.component
са страници, които се показват с жълта граница в екрана на екрана по-горе; иapp.component
е основният компонент, който се показва с червена граница на екранната снимка на каркаса.Така че, като се каза, можем да посочим отделна логика и дизайн за всеки компонент. Въз основа на жичните рамки по-горе, имаме две страници, които използват повторно един компонент— task-list.component
. Ще възникне въпросът - как да посочим какъв тип данни трябва да показваме на конкретна страница? За щастие не трябва да се притесняваме за това, защото когато създавате компонент, можете също да посочите Вход и Изход променливи.
Входните променливи се използват в компонентите за предаване на някои данни от родителския компонент. В горния пример можем да имаме два входни параметъра за task-list.component
- tasks
и listType
. Съответно, tasks
би бил списък от низове, които ще показват всеки низ в отделен ред, докато listType
би било или предстоящи или завършен , което би посочило дали е отметнато квадратчето. По-долу можете да намерите малък кодов фрагмент за това как може да изглежда действителният компонент.
// task-list.component.ts import { Component, Input } from '@angular/core'; @Component({ selector: 'app-task-list', templateUrl: 'task-list.component.html' }) export class TaskListComponent { @Input() tasks: string[] = []; // List of tasks which should be displayed. @Input() listType: 'upcoming' | 'completed' = 'upcoming'; // Type of the task list. constructor() { } }
Подобно на входните променливи, изходните променливи също могат да се използват за предаване на известна информация между компонентите, но този път на родителския компонент. Например за task-list.component
бихме могли да имаме изходна променлива itemChecked
. Той би информирал родителския компонент, ако даден елемент е проверен или не. Изходните променливи трябва да бъдат излъчватели на събития . По-долу можете да намерите малък кодов фрагмент за това как компонентът може да изглежда с изходна променлива.
// task-list.component.ts import { Component, Input, Output } from '@angular/core'; @Component({ selector: 'app-task-list', templateUrl: 'task-list.component.html' }) export class TaskListComponent { @Input() tasks: string[] = []; // List of tasks which should be displayed. @Input() listType: 'upcoming' | 'completed' = 'upcoming'; // Type of the task list. @Output() itemChecked: EventEmitter = new EventEmitter(); @Output() tasksChange: EventEmitter = new EventEmitter(); constructor() { } /** * Is called when an item from the list is checked. * @param selected---Value which indicates if the item is selected or deselected. */ onItemCheck(selected: boolean) { this.itemChecked.emit(selected); } /** * Is called when task list is changed. * @param changedTasks---Changed task list value, which should be sent to the parent component. */ onTasksChanged(changedTasks: string[]) { this.taskChange.emit(changedTasks); } }
Възможно task-list.component.ts
съдържание след добавяне на изходни променливиНека да разгледаме как да използваме този компонент в родителския компонент и как да правим различни типове променливи обвързвания. В Angular има два начина за обвързване на входните променливи - еднопосочно обвързване, което означава, че свойството трябва да бъде увито в квадратни скоби []
и двупосочно обвързване, което означава, че свойството трябва да бъде увито в квадрат и кръгли скоби [()]
. Погледнете примера по-долу и вижте различните начини, по които могат да се предават данни между компонентите.
Възможно upcoming-tasks.component.html
съдържаниеНека да разгледаме всеки параметър:
tasks
параметър се предава с помощта на двупосочно свързване. Това означава, че в случай че параметърът задачи се промени в дъщерния компонент, родителският компонент ще отрази тези промени в upcomingTasks
променлива. За да позволите двупосочно свързване на данни, трябва да създадете изходен параметър, който следва шаблона „[inputParameterName] Change“, в този случай tasksChange
listType
параметър се предава с помощта на еднопосочно свързване. Това означава, че той може да бъде променен в дъщерния компонент, но няма да бъде отразен в родителския компонент. Имайте предвид, че можех да присвоя стойността 'upcoming'
към параметър в компонента и го предаде вместо това - няма да има значение.itemChecked
параметър е функция на слушател и той ще бъде извикан всеки път onItemCheck се изпълнява на task-list.component
. Ако даден елемент е маркиран, $event
ще съдържа стойността true
, но ако не е отметнато, тя ще съдържа стойността false
.Както можете да видите, като цяло, Angular предоставя чудесен начин за предаване и споделяне на информация между множество компоненти, така че не трябва да се страхувате да ги използвате. Просто се уверете, че ги използвате разумно и не ги прекалявайте.
Както вече споменахме, не трябва да се страхувате да използвате ъглови компоненти, но определено трябва да ги използвате разумно.
Така че, когато трябва ли създаване на ъглови компоненти?
task-list.component
. Ние им се обаждаме компоненти за многократна употреба .Тези три правила ми помагат да установя дали трябва да създам нов компонент и те автоматично ми дават ясна визия за ролята на компонента. В идеалния случай, когато създавате компонент, вече трябва да знаете каква ще бъде ролята му в приложението.
Тъй като вече разгледахме използването на компоненти за многократна употреба , нека да разгледаме използването на компоненти на кодовата организация . Нека си представим, че имаме регистрационен формуляр и в долната част на формуляра имаме кутия с Правила и условия . Обикновено legalese обикновено е много голям, заемайки много място - в този случай в HTML шаблон. Така че, нека разгледаме този пример и след това да видим как бихме могли да го променим.
В началото имаме един компонент - registration.component
- който съдържа всичко, включително формуляра за регистрация, както и самите условия.
Registration
Username
Password
Text with very long terms and conditions. Registrate
Начално състояние преди разделянерегистрация.компонентв множество компонентиШаблонът сега изглежда малък, но представете си дали бихме заменили „Текст с много дълги условия“ с действителен бит текст, който има повече от 1000 думи - това би направило редактирането на файла трудно и неудобно. Имаме бързо решение за това - бихме могли да измислим нов компонент terms-and-conditions.component
, който да съдържа всичко, свързано с условията. Така че нека да разгледаме HTML файла за terms-and-conditions.component
.
Text with very long terms and conditions.
Новосъздадениусловия и условия.компонентHTML шаблонИ сега можем да коригираме registration.component
и използвайте terms-and-conditions.component
в него.
Registration
Username
Password
Registrate
Актуализиранорегистрация.component.tsшаблон с компонент за организация на кодаЧестито! Току-що намалихме размера на registration.component
от стотици редове и улесни четенето на кода. В горния пример направихме промени в шаблона на компонента, но същият принцип може да бъде приложен към логиката на компонента.
И накрая, за компоненти за оптимизация , Силно препоръчвам да преминете през този пост , тъй като ще ви предостави цялата информация, необходима за разбиране на откриването на промяната, и към кои конкретни случаи можете да я приложите. Няма да го използвате често, но може да има някои случаи и ако можете да пропуснете редовни проверки на множество компоненти, когато не е необходимо, това е печеливша за ефективността.
Като се има предвид това, не винаги трябва да създаваме отделни компоненти, така че нека да разгледаме кога трябва да избягвате създаването на отделен компонент.
Има три основни точки, въз основа на които предлагам да създам отделен Angular компонент, но има случаи, в които бих избягвал да създавам и отделен компонент.
Отново, нека да разгледаме точките, които ще ви позволят лесно да разберете кога не трябва създайте отделен компонент.
Сега, нека да разгледаме отблизо и да разгледаме и двата случая в примери. Нека си представим, че искаме да имаме бутон, който регистрира съобщението при щракване. Това може да се обърка и да се създаде отделен компонент за тази специфична функционалност, който да съдържа конкретен бутон и действия. Нека първо проверим неправилния подход:
// log-button.component.ts import { Component, Input, Output } from '@angular/core'; @Component({ selector: 'app-log-button', templateUrl: 'log-button.component.html' }) export class LogButtonComponent { @Input() name: string; // Name of the button. @Output() buttonClicked: EventEmitter = new EventEmitter(); constructor() { } /** * Is called when button is clicked. * @param clicked - Value which indicates if the button was clicked. */ onButtonClick(clicked: boolean) { console.log('I just clicked a button on this website'); this.buttonClicked.emit(clicked); } }
Логика на неправилен компонент log-button.component.ts
И съответно, този компонент е придружен от следния HTML изглед.
{{ name }}
Шаблон за неправилен компонент log-button.component.html
Както можете да видите, горният пример ще работи и бихте могли да използвате горния компонент във всичките си изгледи. Тя би направила това, което искате, технически. Но правилното решение тук би било използването на директиви. Това ще ви позволи не само да намалите количеството код, който трябва да напишете, но и да добавите възможността да приложите тази функционалност към какъвто елемент искате, а не само към бутоните.
import { Directive } from '@angular/core'; @Directive({ selector: '[logButton]', hostListeners: { 'click': 'onButtonClick()', }, }) class LogButton { constructor() {} /** * Fired when element is clicked. */ onButtonClick() { console.log('I just clicked a button on this website'); } }
logButton
директива, която може да бъде присвоена на всеки елементСега, когато създадохме нашата директива, можем просто да я използваме в нашето приложение и да я присвоим на всеки елемент, който искаме. Например, нека използваме повторно нашите registration.component
.
Registration
Username
Password
Registrate
logButton
директива, използвана върху бутона за регистрационни формуляриСега можем да разгледаме втория случай, в който не трябва да създаваме отделни компоненти, а това е обратното на компоненти за оптимизация на кода . Ако новосъздаденият компонент прави вашия код по-сложен и по-голям, няма нужда да го създавате. Да вземем за пример нашите registration.component
. Такъв случай би бил създаването на отделен компонент за етикет и поле за въвеждане с тон входни параметри. Нека да разгледаме тази лоша практика.
// form-input-with-label.component.ts import { Component, Input} from '@angular/core'; @Component({ selector: 'app-form-input-with-label', templateUrl: 'form-input-with-label.component.html' }) export class FormInputWithLabelComponent { @Input() name: string; // Name of the field @Input() id: string; // Id of the field @Input() label: string; // Label of the field @Input() type: 'text' | 'password'; // Type of the field @Input() model: any; // Model of the field constructor() { } }
Логика на form-input-with-label.component
И това може да е мнението за този компонент.
{{ label }}
Изглед на form-input-with-label.component
Разбира се, количеството на кода ще бъде намалено в registration.component
, но прави ли общата логика на кода по-лесна за разбиране и четене? Мисля, че ясно виждаме, че това прави кода ненужно по-сложен от преди.
За да обобщим: Не се страхувайте да използвате компоненти; просто се уверете, че имате ясна визия за това, което искате да постигнете. Изброените по-горе сценарии са най-често срещаните и ги считам за най-важните и най-често срещаните; обаче вашият сценарий може да е уникален и от вас зависи да вземете информирано решение. Надявам се, че сте научили достатъчно, за да вземете добро решение.
Ако искате да научите повече за стратегиите за откриване на промени на Angular и стратегията OnPush, препоръчвам да прочетете Откриване на ъглови промени и стратегията OnPush . Тя е тясно свързана с компонентите и, както вече споменах в публикацията, може значително да подобри ефективността на приложението.
Тъй като компонентите са само част от директивите, които Angular предоставя, би било чудесно да се запознаете и с тях атрибутни директиви и структурни директиви . Разбирането на всички директиви най-вероятно ще улесни програмиста изобщо да пише по-добър код.
Има директиви за компоненти, директиви за атрибути и структурни директиви.
Селекторът е начин за извикване на компонент в шаблон. Това е уникален идентификатор за компонент.
Компонентът е директива с шаблон, която позволява изграждане на блокове на потребителски интерфейс в ъглово приложение. Компонентите винаги ще имат шаблон, селектор и може да имат или да нямат отделен стил. Компонентите се декларират с помощта на @Component.
Ъгловите шаблони дефинират потребителския интерфейс на компонент.
Директивите за атрибутите променят начина, по който DOM елементът се появява или държи. Атрибутите нямат шаблон и се декларират с помощта на @Directive.
Angular е платформа, която позволява на потребителите лесно да създават уеб приложения. Базира се на TypeScript.
TypeScript е скриптов език, който произхожда от JavaScript и е супер набор от JavaScript.