Angular 애플리케이션을 구축하는 방법은 무엇입니까? 다음 기사에서는 MemFire Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법을 소개하겠습니다. 도움이 되길 바랍니다.
【관련 튜토리얼 추천: "angular Tutorial"】
MemFire Cloud는 사용자에게 클라우드 데이터베이스 생성, 데이터베이스 관리, 데이터베이스 백업 기능을 제공하는 클라우드 데이터베이스입니다. 또한 백엔드 서비스를 제공합니다. 사용자는 1분 안에 새로운 애플리케이션을 생성하고, 자동으로 생성된 API 및 SDK를 사용할 수 있으며, 클라우드 데이터베이스, 객체 스토리지, 사용자 인증 및 권한 부여 등의 기능에 액세스할 수 있습니다. 애플리케이션 코드를 종료하고 WEB 또는 APP 애플리케이션 개발을 가속화합니다.
학습 영상 주소: www.bilibili.com/video/BV1wt…
이 예에서는 MemFire Cloud 및 Angle을 사용하여 간단한 사용자 관리 애플리케이션을 처음부터 구축하는 단계를 제공합니다. 여기에는 다음이 포함됩니다.
이 가이드가 끝나면 사용자가 로그인하고 일부 기본 프로필 세부 정보를 업데이트할 수 있는 앱이 제공됩니다.
목적: 여기에서 앱이 만들어집니다. 일련의 리소스를 얻습니다. 데이터베이스 및 클라우드 스토리지와 같은 애플리케이션의 독점 API 액세스 링크 및 액세스 키를 얻습니다. 사용자는 위 리소스와 쉽게 상호 작용할 수 있습니다.
Logincloud.memfiredb.com/auth/login애플리케이션 생성
애플리케이션을 클릭하여 데이터 테이블을 시각적으로 생성하세요
1. 프로필 테이블을 생성하세요.
데이터 테이블에서. 페이지에서 "새 데이터 테이블 만들기"를 클릭하면 페이지 구성은 다음과 같습니다.
프로필 테이블 필드 ID는 auth.users 테이블에 있는 id 필드(uuid 유형)의 외래 키와 관련됩니다.
2. 프로필에 대한 RLS 데이터 보안 액세스 규칙을 활성화합니다.
생성된 프로필 테이블을 선택하고 아래 그림과 같이 테이블 권한 표시줄을 클릭한 다음 "RLS 활성화" 버튼을 클릭합니다.
3.
"새 규칙" 버튼을 클릭하고 팝업 상자에서 "모든 사용자에 대한 액세스 활성화"를 선택한 후 정책 이름을 입력하고 "SELECT(쿼리)" 작업을 선택한 다음 아래와 같이 "정책 만들기" 버튼을 클릭합니다.
4. 사용자에게만 개인 프로필 정보를 추가, 삭제, 수정 및 확인할 수 있도록 허용합니다.
"새 규칙" 버튼을 클릭하고 팝업 상자에서 "사용자에 따라 액세스 권한 활성화"를 선택합니다. ID"를 선택하고 정책 이름을 입력합니다. 아래와 같이 "ALL" 작업을 선택하고 "정책 생성" 버튼을 클릭합니다.
사용자의 아바타 이미지를 저장할 클라우드 저장소 버킷을 만듭니다. 관련 작업은 다음과 같습니다.
1 버킷 아바타 만들기
애플리케이션 열의 클라우드 저장소로 이동하여 클릭합니다. 버킷 아바타를 생성하려면 "새 버킷" 버튼을 클릭하세요.
2. 각 사용자가 버킷 아바타를 볼 수 있도록 허용합니다.
버킷 아바타를 선택하고 권한 설정 열로 전환한 후 "새 규칙" 버튼을 클릭하면 정책 편집 팝업 상자가 나타납니다. 아래 그림과 같이 "사용자 정의"를 선택하십시오.
아래 그림과 같이 SELECT 연산을 선택하고 전략 이름을 입력한 후 "전략 생성" 버튼을 클릭하세요.
3. 사용자가 버킷 아바타를 업로드하도록 허용합니다.
버킷 아바타를 선택하고 권한 설정 열로 전환한 다음 "새 규칙" 버튼을 클릭하면 정책 편집 팝업 상자가 나타납니다. Custom", 아래와 같이 표시됩니다.
아래 그림과 같이 INSERT 작업을 선택하고 전략 이름을 입력한 다음 "전략 생성" 버튼을 클릭합니다.
결과 보기
RLS의 모든 데이터 테이블 및 SQL(정책 이름은 영어로 대체됨)
-- 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입니다. API 설정에서 URL과 익명 키만 가져오면 됩니다.
애플리케이션->요약 페이지에서 서비스 주소와 토큰 정보를 얻으세요.
Anon(공개) 키는 클라이언트 API 키입니다. 사용자가 로그인할 때까지 데이터베이스에 대한 "익명 액세스"가 허용됩니다. 로그인 후 키는 사용자 자신의 로그인 토큰으로 전환됩니다. 이렇게 하면 데이터에 대한 행 수준 보안이 활성화됩니다.
참고: service_role(비밀) 키를 사용하면 모든 보안 정책을 우회하여 데이터에 대한 전체 액세스를 허용할 수 있습니다. 이 키는 비밀로 유지되어야 하며 클라이언트나 브라우저에서는 절대 사용되지 않고 서버 환경에서 사용해야 합니다. 후속 샘플 코드에서는 supabaseUrl 및 supabaseKey를 제공해야 합니다.
사용자가 로그인하기 위해 이메일에 있는 매직 링크를 클릭하면 애플리케이션의 로그인 인터페이스로 이동해야 합니다. 여기에서는 인증 설정에서 관련 URL 리디렉션을 구성해야 합니다.
최종 애플리케이션은 로컬 포트 4200(또는 다른 포트)에서 시작되므로 여기서는 일시적으로 URL을 http://localhost:4200
으로 설정합니다. 또한 이 인터페이스를 사용자 정의할 수도 있습니다. 자체 smtp 사용 섬기는 사람.
Angular 앱을 처음부터 만들어 보겠습니다.
Angular CLI를 사용하여 memfiredb-angular
라는 프로젝트를 초기화할 수 있습니다. 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(); } }그런 다음 추가 종속성만 설치해 보겠습니다. 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); } }마지막으로, Environment.ts에 환경 변수를 저장해야 합니다. 필요한 것은
anon
위에 복사한 API URL입니다. src/environments/environment.ts 파일npm run start
이제 API 자격 증명이 있으므로 ng g s supabase
를 통해
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="MemFire Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법에 대해 이야기해 보겠습니다." > </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.comComponent.ts🎜rrreeeng g c 계정
🎜🎜src/app/account/account.comComponent.ts🎜rrreee🎜🎜프로젝트 항목 파일 수정🎜🎜🎜이제 모든 구성 요소가 준비되었으므로 🎜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="MemFire Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법에 대해 이야기해 보겠습니다." > </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(); } }
恭喜!在这个阶段,您拥有一个功能齐全的应用程序!
更多编程相关知识,请访问:编程视频!!
위 내용은 MemFire Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법에 대해 이야기해 보겠습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!