Как настроить Angular 18 Zone.js, принимая обещание Supabase?

Я хочу использовать аутентификацию Supabase для своего приложения и просто создал эту службу аутентификации:

import { Injectable } from '@angular/core';
import { AuthResponse, createClient } from '@supabase/supabase-js';
import { environment } from '../../environments/environment';
import { from, Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  supabase = createClient(environment.supabaseUrl, environment.supabaseKey);

  register(email: string, password: string): Observable<AuthResponse> {
    const promise = this.supabase.auth.signUp({
      email,
      password
    })

    return from(promise);
  }

  login(email: string, password: string): Observable<AuthResponse> {
    const promise = this.supabase.auth.signInWithPassword({
      email,
      password
    });

    return from(promise);
  }
}

в компоненте входа я вызываю метод входа из службы аутентификации:

import { Component, inject } from '@angular/core';
import { FormGroup, FormControl, ReactiveFormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { AuthService } from '../auth/auth.service';

@Component({
  selector: 'app-login',
  standalone: true,
  imports: [ReactiveFormsModule],
  templateUrl: './login.component.html',
  styleUrl: './login.component.css'
})
export class LoginComponent {
  authService = inject(AuthService);
  router = inject(Router);

  errorMessage: string | null = null;

  contactForm = new FormGroup({
    email: new FormControl(''),
    password: new FormControl(''),
  });

  handleSubmit() {
    const rawForm = this.contactForm.getRawValue();
    if (rawForm.email && rawForm.password) {
      this.authService
        .login(rawForm.email, rawForm.password)
        .subscribe((result) => {
          if (result.error) {
            this.errorMessage = result.error.message;
          } else {
            this.router.navigateByUrl('/');
          }
        });
    }
  }
}

Я также включил withFetch() для HttpClient, чтобы улучшить возможный SSR:

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(routes),
    provideClientHydration(),
    provideHttpClient(withFetch()),
    importProvidersFrom(HttpClientModule)]
};

но когда я вызываю http://localhost:4200/login, я получаю эту ошибку:

[vite] Internal server error: Page /login did not render in 30 seconds.
      at Timeout.<anonymous> (C:\Users\kursp\Desktop\deluxe-detail\node_modules\@angular\build\src\utils\server-rendering\render-page.js:90:90)
      at Timeout.timer (C:\Users\kursp\Desktop\deluxe-detail\node_modules\zone.js\fesm2015\zone-node.js:2249:37)
      at _ZoneDelegate.invokeTask (C:\Users\kursp\Desktop\deluxe-detail\node_modules\zone.js\fesm2015\zone-node.js:398:33)
      at _ZoneImpl.runTask (C:\Users\kursp\Desktop\deluxe-detail\node_modules\zone.js\fesm2015\zone-node.js:158:47)
      at invokeTask (C:\Users\kursp\Desktop\deluxe-detail\node_modules\zone.js\fesm2015\zone-node.js:479:34)
      at Timeout.ZoneTask.invoke (C:\Users\kursp\Desktop\deluxe-detail\node_modules\zone.js\fesm2015\zone-node.js:468:48)
      at Timeout.data.args.<computed> (C:\Users\kursp\Desktop\deluxe-detail\node_modules\zone.js\fesm2015\zone-node.js:2231:32)
      at listOnTimeout (node:internal/timers:573:17)
      at process.processTimers (node:internal/timers:514:7)

Я попробовал и добавил в конфигурацию опцию withFetch() для провайдеров. Я думал, что это может иметь какое-то влияние на SSR, хотя я использую супабазу, чтобы не нуждаться в явном бэкэнде для аутентификации.

Фактический результат: Тем не менее, ошибка тайм-аута через 30 секунд, я предполагаю, что у Zone.js есть некоторые задачи/процедуры для завершения соединения, но он никогда не достигает их фактического выполнения --> застревает на этом

Вы поделились полным кодом для LoginComponent, есть ли у вас код для ngOnInit или ngAfterViewInit?

Naren Murali 09.07.2024 13:17
Тестирование функциональных 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
1
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вероятно, метод инициализации createSupabase создает постоянное соединение с данным environment.supabaseUrl сервером, учитывая использование realtime-js под капотом (это упоминается в документации по инициализации)

Когда Angular инициализирует приложение, он создаст файл AuthService. В рамках этого он вызовет метод createSupabase для создания атрибута supabase в AuthService. Он выполнит эту инициализацию и создаст постоянное соединение.

Но эта связь никогда не разорвется. Если только для этого не будет вызван соответствующий API. Это означает, что Angular Zone.js будет постоянно иметь ожидающие выполнения задачи, связанные с открытым соединением, и поэтому никогда не будет считать приложение стабильным. А Angular требует, чтобы приложение было стабильным (без каких-либо ожидающих задач), чтобы его можно было считать инициализированным.

Чтобы решить эту проблему, вы можете запустить соединение после того, как Angular завершит инициализацию приложения. Для этого вы можете создать/инициализировать клиент Supabase, приложение стабильно, используя токен APP_INITIALIZER DI

Таким образом, приложение запустится как обычно. Затем, когда соединение станет стабильным, будет создано постоянное соединение, из-за которого приложение будет нестабильным. Но это нормально, когда все загрузилось. Имеет смысл, что приложение в этот момент нестабильно, поскольку данные постоянно поступают от клиента Supabase, пока вы не закроете его.

Например, это может быть обновленный код AuthService:

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  supabase!: ReturnType<typeof createClient>

  constructor() {
    inject(ApplicationRef)
      .isStable.pipe(first((isStable) => isStable))
      .subscribe(() => {
        this.supabase = createClient(
          environment.supabaseUrl,
          environment.supabaseKey,
        )
      })
  }
}

Дополнительную информацию об этой проблеме нестабильной инициализации можно найти в документации: https://angular.dev/errors/NG0506

Большое спасибо! Это помогло. Ты спас меня день или два.

Haris Kurspahic 10.07.2024 12:40

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