MemFire ​​Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법에 대해 이야기해 보겠습니다.

青灯夜游
풀어 주다: 2022-08-30 20:44:12
앞으로
1719명이 탐색했습니다.

Angular 애플리케이션을 구축하는 방법은 무엇입니까? 다음 기사에서는 MemFire ​​Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법을 소개하겠습니다. 도움이 되길 바랍니다.

MemFire ​​Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법에 대해 이야기해 보겠습니다.

【관련 튜토리얼 추천: "angular Tutorial"】

MemFire ​​​​Cloud는 사용자에게 클라우드 데이터베이스 생성, 데이터베이스 관리, 데이터베이스 백업 기능을 제공하는 클라우드 데이터베이스입니다. 또한 백엔드 서비스를 제공합니다. 사용자는 1분 안에 새로운 애플리케이션을 생성하고, 자동으로 생성된 API 및 SDK를 사용할 수 있으며, 클라우드 데이터베이스, 객체 스토리지, 사용자 인증 및 권한 부여 등의 기능에 액세스할 수 있습니다. 애플리케이션 코드를 종료하고 WEB 또는 APP 애플리케이션 개발을 가속화합니다.

학습 영상 주소: www.bilibili.com/video/BV1wt…

이 예에서는 MemFire ​​​​Cloud 및 Angle을 사용하여 간단한 사용자 관리 애플리케이션을 처음부터 구축하는 단계를 제공합니다. 여기에는 다음이 포함됩니다.

  • MemFire ​​​​클라우드: 사용자 데이터를 저장하기 위한 MemFireDB 데이터베이스.
  • MemFire ​​​​클라우드 사용자 확인: 사용자는 매직 링크를 사용하여 로그인할 수 있습니다(이메일만 필요, 비밀번호 필요 없음).
  • MemFire ​​​​클라우드 저장소: 사용자는 사진을 업로드할 수 있습니다.
  • 행 수준 보안 정책: 개인은 자신의 데이터에만 액세스할 수 있도록 데이터가 보호됩니다.
  • 인스턴트 API: 데이터베이스 테이블 생성 시 API가 자동으로 생성됩니다.

이 가이드가 끝나면 사용자가 로그인하고 일부 기본 프로필 세부 정보를 업데이트할 수 있는 앱이 제공됩니다.

MemFire ​​Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법에 대해 이야기해 보겠습니다.

앱 만들기

목적: 여기에서 앱이 만들어집니다. 일련의 리소스를 얻습니다. 데이터베이스 및 클라우드 스토리지와 같은 애플리케이션의 독점 API 액세스 링크 및 액세스 키를 얻습니다. 사용자는 위 리소스와 쉽게 상호 작용할 수 있습니다.

Logincloud.memfiredb.com/auth/login애플리케이션 생성

MemFire ​​Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법에 대해 이야기해 보겠습니다.

데이터 테이블 생성

애플리케이션을 클릭하여 데이터 테이블을 시각적으로 생성하세요

1. 프로필 테이블을 생성하세요.

데이터 테이블에서. 페이지에서 "새 데이터 테이블 만들기"를 클릭하면 페이지 구성은 다음과 같습니다.

MemFire ​​Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법에 대해 이야기해 보겠습니다.

프로필 테이블 필드 ID는 auth.users 테이블에 있는 id 필드(uuid 유형)의 외래 키와 관련됩니다.

2. 프로필에 대한 RLS 데이터 보안 액세스 규칙을 활성화합니다.

생성된 프로필 테이블을 선택하고 아래 그림과 같이 테이블 권한 표시줄을 클릭한 다음 "RLS 활성화" 버튼을 클릭합니다.

MemFire ​​Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법에 대해 이야기해 보겠습니다.

3.

"새 규칙" 버튼을 클릭하고 팝업 상자에서 "모든 사용자에 대한 액세스 활성화"를 선택한 후 정책 이름을 입력하고 "SELECT(쿼리)" 작업을 선택한 다음 아래와 같이 "정책 만들기" 버튼을 클릭합니다.

MemFire ​​Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법에 대해 이야기해 보겠습니다.

4. 사용자에게만 개인 프로필 정보를 추가, 삭제, 수정 및 확인할 수 있도록 허용합니다.

"새 규칙" 버튼을 클릭하고 팝업 상자에서 "사용자에 따라 액세스 권한 활성화"를 선택합니다. ID"를 선택하고 정책 이름을 입력합니다. 아래와 같이 "ALL" 작업을 선택하고 "정책 생성" 버튼을 클릭합니다.

MemFire ​​Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법에 대해 이야기해 보겠습니다.

아바타 버킷 만들기

사용자의 아바타 이미지를 저장할 클라우드 저장소 버킷을 만듭니다. 관련 작업은 다음과 같습니다.

1 버킷 아바타 만들기

애플리케이션 열의 클라우드 저장소로 이동하여 클릭합니다. 버킷 아바타를 생성하려면 "새 버킷" 버튼을 클릭하세요.

MemFire ​​Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법에 대해 이야기해 보겠습니다.

2. 각 사용자가 버킷 아바타를 볼 수 있도록 허용합니다.

버킷 아바타를 선택하고 권한 설정 열로 전환한 후 "새 규칙" 버튼을 클릭하면 정책 편집 팝업 상자가 나타납니다. 아래 그림과 같이 "사용자 정의"를 선택하십시오.

MemFire ​​Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법에 대해 이야기해 보겠습니다.

아래 그림과 같이 SELECT 연산을 선택하고 전략 이름을 입력한 후 "전략 생성" 버튼을 클릭하세요.

MemFire ​​Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법에 대해 이야기해 보겠습니다.

3. 사용자가 버킷 아바타를 업로드하도록 허용합니다.

버킷 아바타를 선택하고 권한 설정 열로 전환한 다음 "새 규칙" 버튼을 클릭하면 정책 편집 팝업 상자가 나타납니다. Custom", 아래와 같이 표시됩니다.

MemFire ​​Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법에 대해 이야기해 보겠습니다.

아래 그림과 같이 INSERT 작업을 선택하고 전략 이름을 입력한 다음 "전략 생성" 버튼을 클릭합니다.

1MemFire ​​Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법에 대해 이야기해 보겠습니다.

결과 보기

1MemFire ​​Cloud를 사용하여 Angular 애플리케이션을 구축하는 방법에 대해 이야기해 보겠습니다.

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입니다. 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

Angular에는 Node.js가 필요합니다(>=14.15
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를 통해

SupabaseService🎜를 생성하여 Supabase 클라이언트를 초기화하고 Supabase와 통신하는 기능을 구현합니다. API. 🎜🎜src/app/supabase.service.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>
로그인 후 복사
로그인 후 복사

🎜스타일 업데이트🎜🎜🎜인터페이스가 실제로 그다지 우아하지 않다는 것을 알 수 있습니다. 스타일을 업데이트하여 스타일을 업데이트하세요. 더 나은. 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();
  }
}
로그인 후 복사
로그인 후 복사

🎜로그인 구성 요소 설정🎜🎜🎜🎜🎜관리할 Angular 구성 요소를 설정해 보겠습니다. 로그인하고 등록하세요. 우리는 Magic Links를 사용하여 사용자가 비밀번호를 사용하지 않고도 이메일을 사용하여 로그인할 수 있도록 할 것입니다. Angular CLI 명령을 사용하여 🎜AuthComponent를 만듭니다. 🎜 ng g c auth🎜🎜src/app/auth/auth.comComponent.ts🎜rrreee

🎜사용자 정보 페이지🎜🎜🎜사용자 로그인 후 , 우리는 프로필 세부 정보를 편집하고 계정을 관리하도록 허용할 수 있습니다. Angular CLI 명령을 사용하여 🎜AccountComponent를 만듭니다. 🎜 ng 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:juejin.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!