Компоненты Angular: советы и рекомендации

Компоненты Angular: советы и рекомендации

От автора: одним из моих последних заданий было создать настраиваемые раскрывающиеся Angular компоненты, которые поддерживают как одиночный, так и множественный выбор. Как всегда, я поделюсь с вами некоторыми советами в надежде, что вы узнаете новые вещи.

Начнем с подготовки основы с двумя ключевыми компонентами — нам нужны компоненты dato-select и dato-option.

@Component({
  selector: 'dato-option',
  template: `
 <div class="dato-option" [ngClass]="...">
 <ng-content></ng-content>
 </div>
  `,
})
export class DatoOptionComponent implements OnInit {
} 
@Component({
  selector: 'dato-select',
  template: `<ng-content></ng-content>`
})
export class DatoSelectComponent {
  @ContentChildren(DatoOptionComponent) options: QueryList<DatoOptionComponent>;
 
  ngAfterContentInit() {
  }
} 

Совет 1

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

Один из способов добиться этого — добавить type input() и в соответствии с его значением показать и скрыть разметку.

@Component({
  selector: 'dato-option',
  template: `
 <div class="dato-option" [ngClass]="...">
 <input type="checkbox" *ngIf="isMulti">
 <ng-content></ng-content>
 </div>
  `,
})
export class DatoOptionComponent {
  @Input() isMulti = false;
} 

Да, это сработает, но недостатком этого подхода является то, что мы добавили еще одну проверку для каждого параметра в Angular, поэтому в раскрывающемся меню с 10 000 опциями мы добавили еще 10 000 проверок, и это только один ngIf; обычно он будет сопровождаться ngClass, ngStyle и т. д.

Практический курс по созданию веб-приложения на Angular4

Станьте профессиональным веб-разработчиком, создавая востребованные веб-приложения на Angular4.

Узнать подробнее

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

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

export function getOptionTemplate(isMulti = false) {
  return `
 <div class="dato-option">
 ${isMulti ? '<input type="checkbox">' : ''}
 <ng-content></ng-content>
 </div>
  `
}

@Component({
  selector: 'dato-option',
  template: getOptionTemplate(),
})
export class DatoOptionComponent {
} 
@Component({
  selector: 'dato-option-multi',
  template: getOptionTemplate(true)
})
export class DatoOptionMultiComponent extends DatoOptionComponent {
} 

Совет 2

Предыдущее изменение вызвало новую проблему. Предположим, я хочу использовать раскрывающееся multi-select с multi-select:

@Component({
  selector: 'dato-select',
  template: `<ng-content></ng-content>`
})
export class DatoSelectComponent {
  @ContentChildren(DatoOptionComponent) options: QueryList<DatoOptionComponent>;
} 
<dato-select>
  <dato-option-multi *ngFor="let option of options">{{option}}</dato-option-multi>
</dato-select> 

Это не сработает, так как DatoOptionComponent ContentChildren ищет DatoOptionComponent. Если нам нужна ссылка на option-multi компоненты, нам нужно добавить другой запрос ContentChildren.

@ContentChildren(DatoOptionComponent) options: QueryList<DatoOptionComponent>;
@ContentChildren(DatoOptionMultiComponent) optionsMulti: QueryList<DatoOptionMultiComponent>; 

Но теперь мы должны поддерживать оба. К счастью, мы можем решить это более элегантно с помощью Angular DI.

@Component({
  selector: 'dato-option-multi',
  template: getOptionTemplate(true),
  providers: [{provide: DatoOptionComponent, useExisting: DatoOptionMultiComponent}]
})
export class DatoOptionMultiComponent extends DatoOptionComponent {
} 

Мы можем сказать Angular — когда вам нужен DatoOptionComponent, используйте DatoOptionMultiComponent.

Совет 3

Я еще не был доволен. Я хотел использовать тот же селектор — dato-option — для одиночных и нескольких выпадающих компонентов. Поэтому я изменил селектор с multi-option, чтобы быть тем же.

Практический курс по созданию веб-приложения на Angular4

Станьте профессиональным веб-разработчиком, создавая востребованные веб-приложения на Angular4.

Узнать подробнее
@Component({
  selector: 'dato-option',
  ...
})
export class DatoOptionMultiComponent extends DatoOptionComponent {
} 

Но теперь у меня проблема. Angular не допускает дублирования селекторов и выдает ошибку:

Более одного компонента соответствует этому элементу. Убедитесь, что селектор только одного компонента может соответствовать данному элементу.

Способ, которым я решил это, заключается в том, что Angular поддерживает :not CSS-селектор. (наряду с другими полезными селекторами)

@Component({
  selector: 'dato-option:not([multi])',
  ...
})
export class DatoOptionComponent implements OnInit {
} 
@Component({
  selector: 'dato-option[multi]',
  ...
})
export class DatoOptionMultiComponent extends DatoOptionComponent {
} 

Теперь мы можем придерживаться одного и того же селектора и добавлять атрибут, когда нам нужна multi-option. Это кажется более уместным, чем введение совершенно нового селектора.

Совет 4

В раскрывающемся списке есть окно поиска, которое по умолчанию выполняет внутренний поиск. Нам также нужна опция для поиска на стороне сервера.

Один из способов добиться этого — создать как input() и output():

@Component({
  selector: 'dato-select',
  template: `<ng-content></ng-content>`
})
export class DatoSelectComponent {
  @ContentChildren(DatoOptionComponent) options: QueryList<DatoOptionComponent>;
  @Input() internalSearch = true;
  @Output() search = new EventEmitter();
} 

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

@Component({
  selector: 'dato-select',
  template: `<ng-content></ng-content>`
})
export class DatoSelectComponent {
  @ContentChildren(DatoOptionComponent) options: QueryList<DatoOptionComponent>;
  @Output() search = new EventEmitter();
 
  private internalSearch;
 
  ngOnInit() {
 this.internalSearch = this.search.observers.length === 0;
  }
} 

Каждый эмитент событий ведет список своих наблюдателей. Если у нас есть хотя бы один наблюдатель, это означает, что кто-то подписался.

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

Автор: Netanel Basal

Источник: https://netbasal.com/

Редакция: Команда webformyself.

Практический курс по созданию веб-приложения на Angular4

Станьте профессиональным веб-разработчиком, создавая востребованные веб-приложения на Angular4.

Узнать подробнее

Angular 4 с Нуля до Профи

Angular 4 - полное руководство для современной веб-разработки

Научиться

Метки:

Похожие статьи:

Комментарии Вконтакте:

Комментарии Facebook:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Я не робот.

Spam Protection by WP-SpamFree