#angular #typescript #angular-changedetection
Вопрос:
Я изо всех сил пытаюсь найти решение, которое работает и не выдает NG0100
ошибку. https://angular.io/errors/NG0100
В моем компоненте приложения есть логика для создания дочерних компонентов при нажатии на элемент заголовка. Когда этот дочерний компонент создается, он запускает серию вызовов данных, загрузка которых занимает некоторое время. В течение этого времени я показываю панель загрузки. В настоящее время пользователь может снова и снова нажимать на элемент заголовка в HTML-файле приложения, в результате чего выбранный дочерний компонент создается и уничтожается снова и снова. Это также приводит к тому, что несколько вызовов данных выполняются снова и снова.
В идеале я хотел бы отключить действие щелчка на родительском компоненте и изменить непрозрачность, чтобы пользователь знал, что он отключен во время загрузки. Я сделал это следующими способами:
- статические методы в компоненте приложения и статические переменные
- @Методы ввода для дочернего компонента с объектами в качестве входных данных
- @Методы вывода дочернего компонента с примитивными значениями в качестве выходных данных
Однако все эти решения приводили к NG0100: Expression has changed after it was checked
ошибке в консоли. Я не хочу внедрять хакерское решение, которое работает, но выдает ошибки. Каков правильный путь?
certificationView: View = {
shouldEnable: true,
shouldRender: false
}
toggleViewShouldRender(view: View): void {
if (view.shouldEnable) {
view.shouldRender = !view.shouldRender;
}
}
приложение.компонент.ts
<div>
<mat-card
[ngClass]="{'disabled': !certificationView.shouldEnable}"
(click)="toggleViewShouldRender(certificationView)"
>
Certifications
</mat-card>
<app-certifications *ngIf="certificationView.shouldRender"></app-certifications>
</div>
app.component.html
ngOnInit(): void {
this.facade.retrieve()
.pipe(
takeUntil(this.destroyed$),
filter(state => !!state?.data)
)
.subscribe(state => {
this.state = state;
});
}
child.component.ts
Примечание: Я удалил всю логику, которая вызывала ошибку. Чистое выполнение для меня важнее, чем эта функция. Ранее я изменял свойство View.shouldEnable внутри ngOnInit перед подпиской на службу данных, а также внутри подписки.
Например,
<div>
<mat-card
[ngClass]="{'disabled': !certificationView.shouldEnable}"
(click)="toggleViewShouldRender(certificationView)"
>
Certifications
</mat-card>
<app-certifications
*ngIf="certificationView.shouldRender"
[(shouldEnable)]="certificationView.shouldEnable"
>
</app-certifications>
</div>
app.component.html
@Input shouldEnable: boolean;
@Output shouldEnableChange = new EventEmitter<boolean>();
ngOnInit(): void {
// disable header in parent
this.shouldEnableChange.emit(false);
// load data
this.facade.retrieve()
.pipe(
takeUntil(this.destroyed$),
filter(state => !!state?.data)
)
.subscribe(state => {
this.state = state;
// enable header in parent
this.shouldEnableChange.emit(true);
});
}
дочерний компонент.ts
Ответ №1:
Вы можете перенаправить ссылку родителя на дочерний компонент с помощью InjectionToken
. Затем вы можете вызвать methods
или настроить properties
родителя от ребенка.
Сначала вы создадите токен инъекции, который я поместил бы в его собственный файл, например tokens.ts
.
export const PARENT_COMPONENT = new InjectionToken<ParentComponent>('MyParentInjectionToken')
Затем вы должны предоставить маркер для родительского компонента следующим образом. Затем он переадресует себя своим дочерним элементам (см. Следующий блок кода).
@Comonent({
providers: [{
provide: PARENT_COMPONENT,
useExisting: forwardRef(() => ParentComponent)
}]
})
export class ParentComponent {
valueToSet = false;
setValue(val: boolean) {
this.valueToSet = val
}
}
Вот где мы @Inject()
помещаем родителя в конструктор, чтобы у нас был доступ. Иногда не всегда есть родитель, и в этом случае вы можете его использовать @Optional() @Inject()
.
@Component({
template: '<button (click)="doSomething()">Click Me</button>'
})
export class ChildComponent {
constructor(
@Inject(PARENT_COMPONENT) parentComponent: ParentComponent
){}
doSomething() {
this.parentCompnent.setValue(true);
}
}
Комментарии:
1. Спасибо, что сработало. У меня действительно случилась небольшая заминка. Я должен был пометить родительский компонент как общедоступный в конструкторе дочернего компонента. например, @Inject(PARENT_COMPONENT) публичный родительский компонент: родительский компонент
2. Я ничего не менял, но ошибка вернулась. Не знаю почему.. Я действительно заблудился.