Двусторонняя привязка Angular нарушена в производственной сборке - TypeError «st не является функцией»

У меня есть проект, в котором используется 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

А что происходит в prepareEventNames()? Можете ли вы поделиться кодом из этого файла?

SwissCodeMen 29.08.2024 07:16

@SwissCodeMen подготовитьEventNames находится в Zone.js. Ссылка на Github github.com/angular/angular/blob/…

ZrSiO4 29.08.2024 19:03

Если я собираю с отключенной оптимизацией, приложение работает нормально ng build {project} --configuration=production --optimization=false

ZrSiO4 31.08.2024 01:50
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
1
3
64
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Насколько я понимаю, исходя из вашего кода, у вас есть собственный компонент с именем 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);
}

ОБНОВЛЕНО ДЕМО

Компонент текстового поля имеет вход «value» и выход «valueChange», поэтому двусторонняя привязка должна работать. И на самом деле это так, в стадии разработки. Просто производственная сборка сломана.

ZrSiO4 29.08.2024 18:50

Я обновил свой ответ, вы используете оператор сигнала model? Если да, то прикрепляете ли вы сигнал к своему [(value)]?. Дайте мне знать, поможет ли это решить вашу проблему.

Andres2142 29.08.2024 23:52

Я использую @Input("value") и @Output("valueChange") в компоненте текстового поля. Модель представляет собой простой объект json {Имя пользователя: строка}. Опять же, это не проблема синтаксиса или вопрос о том, как выполнить двустороннюю привязку. Существует проблема с производственной сборкой, возможно, дерево вытряхивает основной код Angular/зоны.

ZrSiO4 30.08.2024 02:24

Ну, я точно не знаю, в чем проблема с выводом терминала, но, по крайней мере, вы можете попробовать реорганизовать свой компонент, чтобы использовать сигнал model, а не @input @output, поскольку вы используете Angular v17. Я не уверен, как можно работать на dev, но не на prod.

Andres2142 30.08.2024 04:11

Кроме того, вы также можете попробовать старый способ, обрабатывая (valueChange) и [value] отдельно, продолжая использовать @Input & @Output, и посмотреть, работает ли prod. Я должен сказать, что то, как вы справляетесь со своей двойной привязкой, лично я сам не пробовал, даже не подозревал об этом. Сегодня я кое-что узнал, спасибо за это

Andres2142 30.08.2024 04:37
Ответ принят как подходящий

В поисках любой соответствующей информации по этой проблеме я столкнулся с этим: Включение оптимизации ломает сайт Angular без видимой причины

И вот, вот оно. Теперь моя производственная сборка работает нормально.

Подведем итоги проблемы и ее исправления:

  • Казалось, что производственные сборки были сломаны. Функции в Zone.js не определены или имеют скалярные значения, что приводит к ошибкам типа и неработоспособности приложения.
  • В сообщении SO, упомянутом выше, говорится о различных файлах javascript, которые забивают функции и переменные друг друга.
  • Исправление заключалось в добавлении type = "module" к ссылкам на скрипт. от <script src = "main.js"></script> до <script src = "main.js" type = "module"></script>

Определенно стоит запомнить на будущее

Другие вопросы по теме