Главная » Статьи » Компоненты Angular: советы и рекомендации

Компоненты 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 и т. д.

В моем случае производительность очень важна, поэтому я решил разделить 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, чтобы быть тем же.

@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.