У меня есть проект, в котором используется Angular 17, сборщик приложений, автономные компоненты и импорт NgModule. В разработке оно собирается и работает нормально, но при сборке в производстве приложение вылетает со следующей ошибкой:
core.mjs:6373 ERROR TypeError: st is not a function
at HTMLElement.addEventListener (zone.js:1907:21)
at n.addEventListener (platform-browser.mjs:785:15)
at n.addEventListener (platform-browser.mjs:212:21)
at Dd.listen (platform-browser.mjs:665:30)
at sf.listen (browser.mjs:4309:26)
at G0 (core.mjs:24973:34)
at _t (core.mjs:26820:3)
at Kle (createAccount.component.html:31:53)
at o0 (core.mjs:10988:5)
at LA (core.mjs:12150:7)
createAccount.comComponent.html:31:53 выглядит так, строка 31 — [(value)] = "model.Username"
<my-textbox [label] = "ResourceKey.Portals_EditProfile_Username"
[(value)] = "model.Username"
[readonly] = "model.IsSSO"
[required] = "true">
</my-textbox>
Если я закомментирую это текстовое поле, то та же ошибка появится в строке [(value)] следующего текстового поля.
Из того, что я могу сказать, копаясь в стеке, st, похоже, сопоставлен с подготовитьEventNames вzone.js:
if (!symbolEventNames) {
prepareEventNames(eventName, eventNameToString);
symbolEventNames = zoneSymbolEventNames[eventName];
}
попытка войти в метод подготовкиEventNames() приводит к ошибке.
Так что же это значит? Оптимизированная сборка изменила слишком многое? Как исправить/найти проблему?
Среда:
Angular CLI: 17.3.8
Node: 20.16.0
Package Manager: npm 10.8.1
OS: win32 x64
Angular: 17.3.12
... animations, common, compiler, compiler-cli, core, forms
... localize, platform-browser, platform-browser-dynamic, router
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1703.8
@angular-devkit/build-angular 17.3.8
@angular-devkit/core 17.3.8
@angular-devkit/schematics 17.3.8
@angular/cdk 17.3.10
@angular/cli 17.3.8
@angular/flex-layout 15.0.0-beta.42
@angular/material 17.3.10
@schematics/angular 17.3.8
ng-packagr 17.3.0
rxjs 7.8.1
typescript 5.3.3
zone.js 0.14.4
Конфигурация приложения:
export const appConfig: ApplicationConfig = {
providers: [
{ provide: LocalizationSource, useClass: MinIDLocalizationSource },
provideRouter(routes),
importProvidersFrom(BrowserAnimationsModule),
provideHttpClient(
withInterceptors([
DateInterceptorFn
])
)
]
};
bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));
Обновление: если я собираю с отключенной оптимизацией, приложение работает нормально.
ng build {project} --configuration=production --optimization=false
🤔 А знаете ли вы, что...
Angular предлагает мощную систему инъекции зависимостей для управления зависимостями и компонентами приложения.
Насколько я понимаю, исходя из вашего кода, у вас есть собственный компонент с именем my-textbox
, а у этого есть (судя по всему) input
с именем value
, независимо от того, используете ли вы ввод как signals
или @Input()
, вы не можете ожидать, что иметь двустороннюю привязку для value
, просто используя [()]
. Вы должны получить сообщение об ошибке, вы не сможете его скомпилировать.
Поскольку вы не опубликовали соответствующий код для my-textbox
, если вы хотите отправить и изменить model.Username
, попробуйте добавить output
для прослушивания вашего специального события.
// using input/output annotations
@Componet({...})
export class MyTextBox {
@Input() value!: string;
@Output() valueChanged = new EventEmitter<string>();
onChange(val: string): void {
this.valueChanged.emit(val);
}
}
// using input/output signals
@Componet({...})
export class MyTextBox {
value = input<string>();
valueChanged = output<string>();
onChange(val: string): void {
this.valueChanged.emit(val);
}
}
// a parent component using my-texbox
@Component({
template: `<my-textbox
[value] = "model.Username"
(valueChanged) = "onUsername($event)">
</my-textbox>`,
...
})
export class ParentComponent {
model!: any; // I don't know the type, that's why I use any
onUsername(name: string): void {
this.model.Username = name;
}
}
Таким образом, my-texbox
будет реагировать на изменения, внесенные пользовательским событием valueChanged
.
Другой способ обработки ввода/вывода компонента уровня — использование сигнала model
, который заменяет @Input
и @Output
, но он ведет себя не совсем так, как ngModel
:
@Componet({...})
export class MyTextBox {
value = model<string>();
onChange(val: string): void {
this.value.set(val);
}
}
Затем в родительском компоненте вам нужно будет прикрепить сигнал к вашему [(value)]
следующим образом:
@Component({
template: `<my-textbox [(value)] = "username"> </my-textbox>
<!-- without parenthesis -->
`,
...
})
export class ParentComponent {
person = { username: 'Bob' }
username = signal(this.person.username);
}
ОБНОВЛЕНО ДЕМО
В поисках любой соответствующей информации по этой проблеме я столкнулся с этим: Включение оптимизации ломает сайт Angular без видимой причины
И вот, вот оно. Теперь моя производственная сборка работает нормально.
Подведем итоги проблемы и ее исправления:
type = "module"
к ссылкам на скрипт. от <script src = "main.js"></script>
до <script src = "main.js" type = "module"></script>
Определенно стоит запомнить на будущее