Angular/Supabase project will not load upon "ng serve," causing infinite rendering

Discussão em 'Angular' iniciado por BraedenTurner22, Maio 4, 2025.

  1. I have been working on a login feature for a website I am making using Angular and Supabase.

    I have two separate services, one where the Supabase client is initialized, and another where authentication occurs.

    Here is my Supabase client service:

    import { Injectable } from '@angular/core';
    import { createClient, SupabaseClient } from '@supabase/supabase-js';
    import { environment } from 'src/environments/environment';

    @Injectable({
    providedIn: 'root'
    })
    export class SupabaseService {

    public client: SupabaseClient;

    constructor() {
    this.client = createClient(environment.supabaseUrl, environment.supabaseKey);
    }
    }


    and here is my Auth service:

    import { Injectable } from '@angular/core';
    import { RealtimeChannel, User } from '@supabase/supabase-js';
    import { BehaviorSubject, first, Observable, skipWhile } from 'rxjs';
    import { SupabaseService } from './supabase-service.service';

    export interface Profile {
    id: string;
    email: string;
    }

    @Injectable({
    providedIn: 'root',
    })
    export class AuthService {

    // Supabase user state
    private _$user = new BehaviorSubject<User | null | undefined>(undefined);
    $user = this._$user.pipe(skipWhile(_ => typeof _ === 'undefined')) as Observable<User | null>;
    private id?: string;

    // Profile state
    private _$profile = new BehaviorSubject<Profile | null | undefined>(undefined);
    $profile = this._$profile.pipe(skipWhile(_ => typeof _ === 'undefined')) as Observable<Profile | null>;
    private profile_subscription?: RealtimeChannel;

    constructor(private supabase: SupabaseService) {

    // Initialize Supabase user
    // Get initial user from the current session, if it exists
    this.supabase.client.auth.getUser().then(({ data, error }) => {
    this._$user.next(data && data.user && !error ? data.user : null);

    // After the initial value is set, listen for auth state changes (sign in/sign out)
    this.supabase.client.auth.onAuthStateChange((event, session) => {
    this._$user.next(session?.user ?? null);
    });
    });

    // Initialize the user's profile
    // The state of the user's profile is dependent on there being a user. If no user is set, there shouldn't be a profile.
    this.$user.subscribe(user => {
    if (user) {
    // We only make changes if the user is different
    if (user.id !== this.id) {
    const id = user.id;
    this.id = id;

    // One-time API call to Supabase to get the user's profile
    this.supabase.client.from('profiles').select('*').match({ id }).single().then(res => {

    //Update our profile Behavior Subject with the current value
    this._$profile.next(res.data ?? null);

    //Listen to any changes to our user's profile using Supabase Realtime
    this.profile_subscription = this.supabase
    .client
    .channel('public:profiles')
    .on('postgres_changes', {
    event: '*',
    schema: 'public',
    table: 'profiles',
    filter: 'id=eq.' + user.id}, (payload: any) => {

    //Update our profile Behavior Subject with the newest value
    this._$profile.next(payload.new);

    })
    .subscribe()

    })
    }
    }
    else {
    // If there is no user, update the profile BehaviorSubject, delete the user's id (id), and unsubscribe from the Supabase Realtime
    this._$profile.next(null);
    delete this.id;
    if (this.profile_subscription) {
    this.supabase.client.removeChannel(this.profile_subscription).then(res => {
    console.log('Removed profile channel subscription with status: ', res);
    });
    }
    }
    })

    //Since our _$profile BehaviorSubject is dependent on the $user Observable,
    // when we sign out our user, the _$profile is automatically updated.
    // We can now use the $profile Observable everywhere in our app.

    //End of constructor
    }


    //Registering account
    registerUser(email: string, password: string) {
    return new Promise<void>((resolve, reject) => {
    this.supabase.client.auth.signUp({ email, password })
    .then(({ data, error }) => {
    if (error != null || data == null) return reject(new Error('User couldn\'t be registered'));
    this.$profile.pipe(first()).subscribe(() => resolve());
    })
    .catch(error => reject(error));
    });
    }

    //Sign In
    signIn(email: string, password: string) {
    return new Promise<void>((resolve, reject) => {

    // Set _$profile back to undefined. This will mean that $profile will wait to emit a value
    this._$profile.next(undefined);
    this.supabase.client.auth.signInWithPassword({ email, password })
    .then(({ data, error }) => {
    if (error || !data) return reject(new Error('Invalid email/password combination'));

    // Wait for $profile to be set again.
    // We don't want to proceed until our API request for the user's profile has completed
    this.$profile.pipe(first()).subscribe(() => {
    resolve();
    });
    })
    })
    }


    //Sign Out
    signOut() {
    return this.supabase.client.auth.signOut();
    }
    }


    Upon 'ng serve" I get a message stating that the "Application bundle generation is complete" and I can view my project on http://localhost:4200/, but then I am left with an infinite loading screen.

    I also tried using runOutsideAngular to run the Supabase service outside of the Angular zone:

    import { inject, Injectable, NgZone } from '@angular/core';
    import { createClient, SupabaseClient } from '@supabase/supabase-js';
    import { environment } from '../../../environments/environment';

    @Injectable({
    providedIn: 'root'
    })
    export class SupabaseService {

    public client: SupabaseClient;
    private readonly ngZone = inject(NgZone);

    constructor() {
    this.client = this.ngZone.runOutsideAngular(() =>
    createClient(environment.supabase.url, environment.supabase.key)
    );
    }
    }


    but this does not work either.

    Any help would be greatly appreciated!

    Continue reading...

Compartilhe esta Página