Heim >Web-Frontend >js-Tutorial >Lassen Sie uns darüber sprechen, wie Sie MemFire Cloud zum Erstellen von Angular-Anwendungen verwenden
Wie erstellt man eine Angular-Anwendung? Der folgende Artikel stellt Ihnen vor, wie Sie MemFire Cloud zum Erstellen von Angular-Anwendungen verwenden. Ich hoffe, er wird Ihnen hilfreich sein!
【Verwandte Tutorial-Empfehlung: „angular Tutorial“】
MemFire Cloud ist eine Cloud-Datenbank, die Benutzern die Möglichkeit bietet, Cloud-Datenbanken zu erstellen, die Datenbank zu verwalten und die Datenbank zu sichern. Es bietet außerdem Backend-as-a-Service. Benutzer können in einer Minute eine neue Anwendung erstellen, automatisch generierte APIs und SDKs verwenden und auf Cloud-Datenbanken, Objektspeicher, Benutzerauthentifizierung und -autorisierung sowie andere Funktionen zugreifen Code und beschleunigen Sie die Entwicklung von WEB- oder APP-Anwendungen.
Lernvideoadresse: www.bilibili.com/video/BV1wt…
Dieses Beispiel enthält die Schritte zum Erstellen einer einfachen Benutzerverwaltungsanwendung (von Grund auf) mit MemFire Cloud und Angular. Dazu gehört:
Am Ende dieser Anleitung steht Ihnen eine App zur Verfügung, mit der sich Benutzer anmelden und einige grundlegende Profildetails aktualisieren können:
Zweck: Unsere App wird hier erstellt. Erhalten Sie eine Reihe von Ressourcen B. Datenbanken und Cloud-Speicher, und erhalten Sie den exklusiven API-Zugriffslink und Zugriffsschlüssel der Anwendung. Benutzer können problemlos mit den oben genannten Ressourcen interagieren.
Anmeldencloud.memfiredb.com/auth/loginAnwendung erstellen
Klicken Sie auf die Anwendung, um die Datentabelle visuell zu erstellen
1. Erstellen Sie die Profiltabelle;
Auf den Daten Klicken Sie auf der Tabellenseite auf „Neue Datentabelle“. Die Seitenkonfiguration lautet wie folgt:
wobei die Feld-ID der Profiltabelle mit dem Fremdschlüssel des ID-Felds (UUID-Typ) in der Tabelle auth.users verknüpft ist.
2. Aktivieren Sie die RLS-Datensicherheitszugriffsregeln für Profile.
Wählen Sie die erstellte Profiltabelle aus, klicken Sie auf die Tabellenberechtigungsleiste, wie in der Abbildung unten gezeigt, und klicken Sie auf die Schaltfläche „RLS aktivieren“. Jeder Benutzer kann öffentliche persönliche Informationen anzeigen.
Klicken Sie auf die Schaltfläche „Neue Regel“, wählen Sie im Popup-Feld „Zugriff für alle Benutzer aktivieren“, geben Sie den Richtliniennamen ein, wählen Sie den Vorgang „AUSWÄHLEN (Abfrage)“ aus und Klicken Sie auf die Schaltfläche „Richtlinie erstellen“ (siehe unten).
4. Erlauben Sie Benutzern nur das Hinzufügen, Löschen, Ändern und Überprüfen ihrer persönlichen Profilinformationen.
Klicken Sie auf die Schaltfläche „Neue Regel“ und wählen Sie im Popup-Fenster „Zugriffsberechtigungen für Benutzer basierend auf dem Benutzer aktivieren“ aus ID“ und geben Sie den Richtliniennamen ein. Wählen Sie den Vorgang „ALLE“ und klicken Sie auf die Schaltfläche „Richtlinie erstellen“, wie unten gezeigt.
Avatar-Bucket erstellenEinen Cloud-Speicher-Bucket erstellen, um das Avatar-Bild des Benutzers zu speichern:2. Erlauben Sie jedem Benutzer, die Bucket-Avatare anzuzeigen. Wählen Sie die Bucket-Avatare aus, wechseln Sie zur Spalte „Berechtigungseinstellungen“. Klicken Sie auf die Schaltfläche „Neue Regel“. Ein Popup-Fenster zur Richtlinienbearbeitung wird angezeigt. Wählen Sie „Benutzerdefiniert“, wie im Bild unten gezeigt:
Wählen Sie die Operation SELECT aus, geben Sie den Strategienamen ein und klicken Sie auf die Schaltfläche „Strategie generieren“, wie in der Abbildung unten gezeigt.
3. Erlauben Sie Benutzern das Hochladen von Bucket-Avataren.
Wählen Sie die Bucket-Avatare aus, wechseln Sie zur Spalte „Berechtigungseinstellungen“, klicken Sie auf die Schaltfläche „Neue Regel“. Es erscheint ein Popup-Fenster zur Richtlinienbearbeitung „Benutzerdefiniert“, wie unten gezeigt:
Wählen Sie die Operation INSERT aus, geben Sie den Strategienamen ein und klicken Sie auf die Schaltfläche „Strategie generieren“, wie in der Abbildung unten gezeigt.
Ergebnisse anzeigen
Alle Datentabellen und SQL von RLS (Richtlinienname wird durch Englisch ersetzt)
-- Create a table for public "profiles" create table profiles ( id uuid references auth.users not null, updated_at timestamp with time zone, username text unique, avatar_url text, website text, primary key (id), unique(username), ); alter table profiles enable row level security; create policy "Public profiles are viewable by everyone." on profiles for select using ( true ); create policy "Users can insert their own profile." on profiles for insert with check ( auth.uid() = id ); create policy "Users can update own profile." on profiles for update using ( auth.uid() = id ); -- Set up Storage! insert into storage.buckets (id, name) values ('avatars', 'avatars'); create policy "Avatar images are publicly accessible." on storage.objects for select using ( bucket_id = 'avatars' ); create policy "Anyone can upload an avatar." on storage.objects for insert with check ( bucket_id = 'avatars' );API-Schlüssel abrufen
Auf der Seite „Anwendung“ -> „Zusammenfassung“ erhalten Sie die Dienstadresse und die Token-Informationen.
Anon (öffentlicher) Schlüssel ist der Client-API-Schlüssel. Es ermöglicht den „anonymen Zugriff“ auf Ihre Datenbank, bis sich der Benutzer anmeldet. Nach der Anmeldung wird der Schlüssel auf den eigenen Login-Token des Benutzers umgestellt. Dadurch wird die Sicherheit der Daten auf Zeilenebene aktiviert.
HINWEIS: Der service_role (geheime) Schlüssel ermöglicht den vollständigen Zugriff auf Ihre Daten unter Umgehung jeglicher Sicherheitsrichtlinien. Dieser Schlüssel muss geheim gehalten und in einer Serverumgebung verwendet werden, niemals auf dem Client oder Browser. Im folgenden Beispielcode müssen supabaseUrl und supabaseKey bereitgestellt werden.
AuthentifizierungseinstellungenDa unsere endgültige Anwendung auf dem lokalen Port 4200 (oder anderen Ports) gestartet wird, setzen wir hier die URL vorübergehend auf http://localhost:4200
Darüber hinaus kann diese Schnittstelle auch angepasst werden. Verwenden Sie unser eigenes SMTP Server.
Erstellen der App verwenden, um ein Projekt mit dem Namen memfiredb-angular
zu initialisieren: Angular erfordert Node.js (>=14.15 memfiredb-angular:
Angular 需要 Node.js (>=14.15
npm install -g @angular/cli npx ng new memfiredb-angular --routing false --style css cd memfiredb-angular
然后让我们安装唯一的附加依赖项:supabase-js
npm install @supabase/supabase-js
最后,我们要将环境变量保存在environment.ts, 我们需要的是 API URL 和您上面anon
复制的密钥。
src/environments/environment.ts文件
export const environment = { production: false, supabaseUrl: "YOUR_SUPABASE_URL", supabaseKey: "YOUR_SUPABASE_KEY" };
现在我们已经有了 API 凭证,通过ng g s supabase
创建一个SupabaseService来初始化 Supabase 客户端并实现与 Supabase API 通信的函数。
src/app/supabase.service.ts
import { Injectable } from '@angular/core'; import {AuthChangeEvent, createClient, Session, SupabaseClient} from '@supabase/supabase-js'; import {environment} from "../environments/environment"; export interface Profile { username: string; website: string; avatar_url: string; } @Injectable({ providedIn: 'root' }) export class SupabaseService { private supabase: SupabaseClient; constructor() { this.supabase = createClient(environment.supabaseUrl, environment.supabaseKey); } get user() { return this.supabase.auth.user(); } get session() { return this.supabase.auth.session(); } get profile() { return this.supabase .from('profiles') .select(`username, website, avatar_url`) .eq('id', this.user?.id) .single(); } authChanges(callback: (event: AuthChangeEvent, session: Session | null) => void) { return this.supabase.auth.onAuthStateChange(callback); } signIn(email: string) { return this.supabase.auth.signIn({email}); } signOut() { return this.supabase.auth.signOut(); } updateProfile(profile: Profile) { const update = { ...profile, id: this.user?.id, updated_at: new Date() } return this.supabase.from('profiles').upsert(update, { returning: 'minimal', // Don't return the value after inserting }); } downLoadImage(path: string) { return this.supabase.storage.from('avatars').download(path); } uploadAvatar(filePath: string, file: File) { return this.supabase.storage .from('avatars') .upload(filePath, file); } }
可以看到界面实在是不怎么优雅,更新下样式,让它好看一些。 修改src/styles.css
文件。
html, body { --custom-font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; --custom-bg-color: #101010; --custom-panel-color: #222; --custom-box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.8); --custom-color: #fff; --custom-color-brand: #24b47e; --custom-color-secondary: #666; --custom-border: 1px solid #333; --custom-border-radius: 5px; --custom-spacing: 5px; padding: 0; margin: 0; font-family: var(--custom-font-family); background-color: var(--custom-bg-color); } * { color: var(--custom-color); font-family: var(--custom-font-family); box-sizing: border-box; } html, body, #__next { height: 100vh; width: 100vw; overflow-x: hidden; } /* Grid */ .container { width: 90%; margin-left: auto; margin-right: auto; } .row { position: relative; width: 100%; } .row [class^='col'] { float: left; margin: 0.5rem 2%; min-height: 0.125rem; } .col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12 { width: 96%; } .col-1-sm { width: 4.33%; } .col-2-sm { width: 12.66%; } .col-3-sm { width: 21%; } .col-4-sm { width: 29.33%; } .col-5-sm { width: 37.66%; } .col-6-sm { width: 46%; } .col-7-sm { width: 54.33%; } .col-8-sm { width: 62.66%; } .col-9-sm { width: 71%; } .col-10-sm { width: 79.33%; } .col-11-sm { width: 87.66%; } .col-12-sm { width: 96%; } .row::after { content: ''; display: table; clear: both; } .hidden-sm { display: none; } @media only screen and (min-width: 33.75em) { /* 540px */ .container { width: 80%; } } @media only screen and (min-width: 45em) { /* 720px */ .col-1 { width: 4.33%; } .col-2 { width: 12.66%; } .col-3 { width: 21%; } .col-4 { width: 29.33%; } .col-5 { width: 37.66%; } .col-6 { width: 46%; } .col-7 { width: 54.33%; } .col-8 { width: 62.66%; } .col-9 { width: 71%; } .col-10 { width: 79.33%; } .col-11 { width: 87.66%; } .col-12 { width: 96%; } .hidden-sm { display: block; } } @media only screen and (min-width: 60em) { /* 960px */ .container { width: 75%; max-width: 60rem; } } /* Forms */ label { display: block; margin: 5px 0; color: var(--custom-color-secondary); font-size: 0.8rem; text-transform: uppercase; } input { width: 100%; border-radius: 5px; border: var(--custom-border); padding: 8px; font-size: 0.9rem; background-color: var(--custom-bg-color); color: var(--custom-color); } input[disabled] { color: var(--custom-color-secondary); } /* Utils */ .block { display: block; width: 100%; } .inline-block { display: inline-block; width: 100%; } .flex { display: flex; } .flex.column { flex-direction: column; } .flex.row { flex-direction: row; } .flex.flex-1 { flex: 1 1 0; } .flex-end { justify-content: flex-end; } .flex-center { justify-content: center; } .items-center { align-items: center; } .text-sm { font-size: 0.8rem; font-weight: 300; } .text-right { text-align: right; } .font-light { font-weight: 300; } .opacity-half { opacity: 50%; } /* Button */ button, .button { color: var(--custom-color); border: var(--custom-border); background-color: var(--custom-bg-color); display: inline-block; text-align: center; border-radius: var(--custom-border-radius); padding: 0.5rem 1rem; cursor: pointer; text-align: center; font-size: 0.9rem; text-transform: uppercase; } button.primary, .button.primary { background-color: var(--custom-color-brand); border: 1px solid var(--custom-color-brand); } /* Widgets */ .card { width: 100%; display: block; border: var(--custom-border); border-radius: var(--custom-border-radius); padding: var(--custom-spacing); } .avatar { border-radius: var(--custom-border-radius); overflow: hidden; max-width: 100%; } .avatar.image { object-fit: cover; } .avatar.no-image { background-color: #333; border: 1px solid rgb(200, 200, 200); border-radius: 5px; } .footer { position: absolute; max-width: 100%; bottom: 0; left: 0; right: 0; display: flex; flex-flow: row; border-top: var(--custom-border); background-color: var(--custom-bg-color); } .footer div { padding: var(--custom-spacing); display: flex; align-items: center; width: 100%; } .footer div > img { height: 20px; margin-left: 10px; } .footer > div:first-child { display: none; } .footer > div:nth-child(2) { justify-content: left; } @media only screen and (min-width: 60em) { /* 960px */ .footer > div:first-child { display: flex; } .footer > div:nth-child(2) { justify-content: center; } } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } .mainHeader { width: 100%; font-size: 1.3rem; margin-bottom: 20px; } .avatarPlaceholder { border: var(--custom-border); border-radius: var(--custom-border-radius); width: 35px; height: 35px; background-color: rgba(255, 255, 255, 0.2); display: flex; align-items: center; justify-content: center; } /* Auth */ .auth-widget { display: flex; flex-direction: column; gap: 20px; } .auth-widget > .button { display: flex; align-items: center; justify-content: center; border: none; background-color: #444444; text-transform: none !important; transition: all 0.2s ease; } .auth-widget .button:hover { background-color: #2a2a2a; } .auth-widget .button > .loader { width: 17px; animation: spin 1s linear infinite; filter: invert(1); } /* Account */ .account { display: flex; flex-direction: column; gap: 20px; } .account > * > .avatarField { display: flex; align-items: center; margin-bottom: 30px; } .account > * > .avatarField > .avatarContainer { margin-right: 20px; } /* Profile Card */ .profileCard { border-radius: 5px; display: flex; border: var(--custom-border); background-color: var(--custom-panel-color); padding: 20px 20px; margin-bottom: 20px; } .profileCard:last-child { margin-bottom: 0px; } .profileCard > .userInfo { margin-left: 20px; font-weight: 300; display: flex; flex-direction: column; justify-content: center; } .profileCard > .userInfo > p { margin: 0; } .profileCard > .userInfo > .username { font-size: 1.3rem; font-weight: 500; margin-bottom: 5px; } .profileCard > .userInfo > .website { font-size: 0.9rem; color: var(--custom-color-brand); margin-bottom: 10px; text-decoration: none; }
让我们设置一个 Angular 组件来管理登录和注册。我们将使用 Magic Links,因此用户无需使用密码即可使用电子邮件登录。使用Angular CLI 命令创建一个AuthComponent 。 ng g c auth
src/app/auth/auth.component.ts
import { Component } from '@angular/core'; import {SupabaseService} from "../supabase.service"; @Component({ selector: 'app-auth', template: ` <div> <form> <h1>Memfiredb + Angular</h1> <p>使用下面的电子邮件通过魔术链接登录</p> <div> <input> </div> <div> <button> {{loading ? 'Loading' : 'Send magic link'}} </button> </div> </form> </div> `, }) export class AuthComponent { loading = false; constructor(private readonly supabase: SupabaseService) { } async handleLogin(input: string) { try { this.loading = true; await this.supabase.signIn(input); alert('请检查您的电子邮件以获取登录链接!'); } catch (error:any) { alert(error.error_description || error.message) } finally { this.loading = false; } } }
用户登录后,我们可以允许他们编辑他们的个人资料详细信息并管理他们的帐户。使用Angular CLI 命令创建一个AccountComponent 。 ng g c account
import {Component, Input, OnInit} from '@angular/core'; import {Profile, SupabaseService} from "../supabase.service"; import {Session} from "@supabase/supabase-js"; @Component({ selector: 'app-account', template: ` <div> <div> <label>邮箱</label> <input> </div> <div> <label>名称</label> <input> </div> <div> <label>网址</label> <input> </div> <div> <button> {{loading ? 'Loading ...' : 'Update'}} </button> </div> <div> <button> 退出登录 </button> </div> </div> ` }) export class AccountComponent implements OnInit { loading = false; profile: Profile | undefined; @Input() session: Session | undefined; constructor(private readonly supabase: SupabaseService) { } ngOnInit() { this.getProfile(); } async getProfile() { try { this.loading = true; let {data: profile, error, status} = await this.supabase.profile; if (error && status !== 406) { throw error; } if (profile) { this.profile = profile; } } catch (error:any) { alert(error.message) } finally { this.loading = false; } } async updateProfile(username: string, website: string, avatar_url: string = '') { try { this.loading = true; await this.supabase.updateProfile({username, website, avatar_url}); } catch (error:any) { alert(error.message); } finally { this.loading = false; } } async signOut() { await this.supabase.signOut(); } }Dann installieren wir die einzige zusätzliche Abhängigkeit: supabase-js
import {Component, OnInit} from '@angular/core'; import {SupabaseService} from "./supabase.service"; @Component({ selector: 'app-root', template: ` <div> <app-account></app-account> <ng-template> <app-auth></app-auth> </ng-template> </div> ` }) export class AppComponent implements OnInit { session = this.supabase.session; constructor(private readonly supabase: SupabaseService) { } ngOnInit() { this.supabase.authChanges((_, session) => this.session = session); } }
anon
kopiert haben. src/environments/environment.ts-Dateinpm run start
Da wir nun die API-Anmeldeinformationen haben, erstellen Sie über ng g s supabase
einen SupabaseService, um den Supabase-Client zu initialisieren und Funktionen für die Kommunikation mit der Supabase zu implementieren API.
import {Component, EventEmitter, Input, Output} from '@angular/core'; import {SupabaseService} from "../supabase.service"; import {DomSanitizer, SafeResourceUrl} from "@angular/platform-browser"; @Component({ selector: 'app-avatar', template: ` <div> <img alt="Lassen Sie uns darüber sprechen, wie Sie MemFire Cloud zum Erstellen von Angular-Anwendungen verwenden" > </div> <div></div> <div> <label> {{uploading ? 'Uploading ...' : 'Upload'}} </label> <input> </div> `, }) export class AvatarComponent { _avatarUrl: SafeResourceUrl | undefined; uploading = false; @Input() set avatarUrl(url: string | undefined) { if (url) { this.downloadImage(url); } }; @Output() upload = new EventEmitter<string>(); constructor( private readonly supabase: SupabaseService, private readonly dom: DomSanitizer ) { } async downloadImage(path: string) { try { const {data} = await this.supabase.downLoadImage(path); if (data instanceof Blob) { this._avatarUrl = this.dom.bypassSecurityTrustResourceUrl( URL.createObjectURL(data) ); } } catch (error:any) { console.error('下载图片出错: ', error.message); } } async uploadAvatar(event: any) { try { this.uploading = true; if (!event.target.files || event.target.files.length === 0) { throw new Error('必须选择要上载的图像。'); } const file = event.target.files[0]; const fileExt = file.name.split('.').pop(); const fileName = `${Math.random()}.${fileExt}`; const filePath = `${fileName}`; await this.supabase.uploadAvatar(filePath, file); this.upload.emit(filePath); this.downloadImage(filePath) } catch (error:any) { alert(error.message); } finally { this.uploading = false; } } }</string>
src/styles.css
. 🎜import {Component, Input, OnInit} from '@angular/core'; import {Profile, SupabaseService} from "../supabase.service"; import {Session} from "@supabase/supabase-js"; @Component({ selector: 'app-account', template: ` <app-avatar> </app-avatar> <div> <div> <label>邮箱</label> <input> </div> <div> <label>名称</label> <input> </div> <div> <label>网址</label> <input> </div> <div> <button> {{loading ? 'Loading ...' : 'Update'}} </button> </div> <div> <button> 退出登录 </button> </div> </div> ` }) export class AccountComponent implements OnInit { loading = false; profile: Profile | undefined; @Input() session: Session | undefined; constructor(private readonly supabase: SupabaseService) { } ngOnInit() { this.getProfile(); } async getProfile() { try { this.loading = true; let {data: profile, error, status} = await this.supabase.profile; if (error && status !== 406) { throw error; } if (profile) { this.profile = profile; } } catch (error:any) { alert(error.message) } finally { this.loading = false; } } async updateProfile(username: string, website: string, avatar_url: string = '') { try { this.loading = true; await this.supabase.updateProfile({username, website, avatar_url}); alert("修改成功") } catch (error:any) { alert(error.message); } finally { this.loading = false; } } async signOut() { await this.supabase.signOut(); } }
ng g c auth
🎜🎜src/app/auth/auth.component.ts🎜rrreeeng g c account
🎜🎜src/app/account/account.component.ts🎜rrreee🎜🎜Ändern Sie die Projekteintragsdatei🎜🎜🎜Da wir nun alle Komponenten bereit haben, aktualisieren wir die 🎜AppComponent 🎜 :🎜src/app/app.component.ts
import {Component, OnInit} from '@angular/core'; import {SupabaseService} from "./supabase.service"; @Component({ selector: 'app-root', template: ` <div> <app-account></app-account> <ng-template> <app-auth></app-auth> </ng-template> </div> ` }) export class AppComponent implements OnInit { session = this.supabase.session; constructor(private readonly supabase: SupabaseService) { } ngOnInit() { this.supabase.authChanges((_, session) => this.session = session); } }
完成后,在终端窗口中运行它:
npm run start
然后打开浏览器到 http://localhost:4200,你应该会看到完整的应用程序。
每个 MemFire Cloud项目都配置了存储,用于管理照片和视频等大文件。
让我们为用户创建一个头像,以便他们可以上传个人资料照片。使用Angular CLI 命令创建AvatarComponent 。 ng g c avatar
src/app/avatar/avatar.component.ts
import {Component, EventEmitter, Input, Output} from '@angular/core'; import {SupabaseService} from "../supabase.service"; import {DomSanitizer, SafeResourceUrl} from "@angular/platform-browser"; @Component({ selector: 'app-avatar', template: ` <div> <img alt="Lassen Sie uns darüber sprechen, wie Sie MemFire Cloud zum Erstellen von Angular-Anwendungen verwenden" > </div> <div></div> <div> <label> {{uploading ? 'Uploading ...' : 'Upload'}} </label> <input> </div> `, }) export class AvatarComponent { _avatarUrl: SafeResourceUrl | undefined; uploading = false; @Input() set avatarUrl(url: string | undefined) { if (url) { this.downloadImage(url); } }; @Output() upload = new EventEmitter<string>(); constructor( private readonly supabase: SupabaseService, private readonly dom: DomSanitizer ) { } async downloadImage(path: string) { try { const {data} = await this.supabase.downLoadImage(path); if (data instanceof Blob) { this._avatarUrl = this.dom.bypassSecurityTrustResourceUrl( URL.createObjectURL(data) ); } } catch (error:any) { console.error('下载图片出错: ', error.message); } } async uploadAvatar(event: any) { try { this.uploading = true; if (!event.target.files || event.target.files.length === 0) { throw new Error('必须选择要上载的图像。'); } const file = event.target.files[0]; const fileExt = file.name.split('.').pop(); const fileName = `${Math.random()}.${fileExt}`; const filePath = `${fileName}`; await this.supabase.uploadAvatar(filePath, file); this.upload.emit(filePath); this.downloadImage(filePath) } catch (error:any) { alert(error.message); } finally { this.uploading = false; } } }</string>
然后我们可以在AccountComponent html 模板的顶部添加小部件:
src/app/account/account.component.ts
import {Component, Input, OnInit} from '@angular/core'; import {Profile, SupabaseService} from "../supabase.service"; import {Session} from "@supabase/supabase-js"; @Component({ selector: 'app-account', template: ` <app-avatar> </app-avatar> <div> <div> <label>邮箱</label> <input> </div> <div> <label>名称</label> <input> </div> <div> <label>网址</label> <input> </div> <div> <button> {{loading ? 'Loading ...' : 'Update'}} </button> </div> <div> <button> 退出登录 </button> </div> </div> ` }) export class AccountComponent implements OnInit { loading = false; profile: Profile | undefined; @Input() session: Session | undefined; constructor(private readonly supabase: SupabaseService) { } ngOnInit() { this.getProfile(); } async getProfile() { try { this.loading = true; let {data: profile, error, status} = await this.supabase.profile; if (error && status !== 406) { throw error; } if (profile) { this.profile = profile; } } catch (error:any) { alert(error.message) } finally { this.loading = false; } } async updateProfile(username: string, website: string, avatar_url: string = '') { try { this.loading = true; await this.supabase.updateProfile({username, website, avatar_url}); alert("修改成功") } catch (error:any) { alert(error.message); } finally { this.loading = false; } } async signOut() { await this.supabase.signOut(); } }
恭喜!在这个阶段,您拥有一个功能齐全的应用程序!
更多编程相关知识,请访问:编程视频!!
Das obige ist der detaillierte Inhalt vonLassen Sie uns darüber sprechen, wie Sie MemFire Cloud zum Erstellen von Angular-Anwendungen verwenden. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!