firebase auth

本文最初发布在Auth0.com博客上 ,并经许可在此处重新发布。

在这个分为两部分的系列教程中,我们将学习如何构建一个使用Auth0身份验证保护Node后端和Angular前端安全的应用程序。 我们的服务器和应用程序还将使用自定义令牌对Firebase Cloud Firestore数据库进行身份验证,以便用户在使用Auth0登录后可以安全方式留下实时评论。 可以在angular-firebase GitHub存储库中找到Angular应用程序代码,在firebase-auth0-nodeserver存储库中找到Node API。

本教程的第一部分“ 使用Auth0验证Firebase和Angular:第1部分”介绍了:

  • Auth0和Firebase的介绍和设置
  • 实施安全的Node API,以生成自定义Firebase令牌并为我们的应用程序提供数据
  • 具有模块和延迟加载的Angular应用程序体系结构
  • 具有服务和路由保护的Auth0的角度身份验证
  • 共享的Angular组件和API服务。

使用Auth0对Firebase和Angular进行身份验证:第2部分

本教程的第2部分将介绍:

  1. 显示狗:异步和NgIfElse
  2. 带路由参数的狗详细信息
  3. 注释模型类
  4. Firebase Cloud Firestore和规则
  5. 评论组件
  6. 评论表格组件
  7. 实时评论
  8. 结论

我们完成的应用程序将如下所示:

带有Auth0自定义令牌的Angular Firebase应用

让我们从Auth0:Part 1在对Firebase和Angular进行身份验证的结尾处停下来的地方继续 。

显示狗:异步和NgIfElse

让我们实现应用程序的主页-狗列表。 设置Angular应用程序的体系结构时,我们为此组件创建了脚手架。

重要说明:确保您的Node.js API正在运行。 如果您需要有关API的更新知识,请参阅如何使用Auth0认证Firebase和Angular:第1部分-Node API 。

狗组件类

立即打开dogs.component.ts类文件并实现以下代码:

// src/app/dogs/dogs/dogs.component.ts
import { Component, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { ApiService } from '../../core/api.service';
import { Dog } from './../../core/dog';
import { Observable } from 'rxjs/Observable';
import { tap, catchError } from 'rxjs/operators';
@Component({
selector: 'app-dogs',
templateUrl: './dogs.component.html'
})
export class DogsComponent implements OnInit {
pageTitle = 'Popular Dogs';
dogsList$: Observable<Dog[]>;
loading = true;
error: boolean;
constructor(
private title: Title,
private api: ApiService
) {
this.dogsList$ = api.getDogs$().pipe(
tap(val => this._onNext(val)),
catchError((err, caught) => this._onError(err, caught))
);
}
ngOnInit() {
this.title.setTitle(this.pageTitle);
}
private _onNext(val: Dog[]) {
this.loading = false;
}
private _onError(err, caught): Observable<any> {
this.loading = false;
this.error = true;
return Observable.throw('An error occurred fetching dogs data.');
}
}

导入后,我们将设置一些本地属性:

  • pageTitle :设置页面的<h1><title>
  • dogsList$ :我们的API HTTP请求返回的可观察值,以获取狗列表数据
  • loading :在发出API请求时显示加载图标
  • error :如果从API提取数据时出现问题,则显示错误。

我们将使用声明性异步管道来响应API GET请求返回的dogsList$ observable。 使用异步管道,我们不需要在DogsComponent类中进行订阅或取消订阅:订阅过程将自动进行管理! 我们只需要建立可观察的。

通过将TitleApiService传递给构造函数,将它们提供给我们的类,然后将其dogsList$设置dogsList$可观察的。 我们将使用RxJS运算符tap (以前称为do运算符)和catchError来调用处理程序函数。 tap运算符执行副作用,但不影响发射的数据,因此非常适合设置其他属性。 _onNext()函数会将loading设置为false (因为已成功发射数据)。 _onError()函数将适当地设置loadingerror并抛出错误。 如前所述,我们不需要订阅取消订阅可观察到的dogsList$因为异步管道(我们将在模板中添加)将为我们处理。

在初始化组件时,我们将使用ngOnInit()监视OnInit生命周期挂钩以设置文档<title>

我们的Dogs组件类就是这样!

狗组件模板

让我们转到dogs.component.html的模板:

<!-- src/app/dogs/dogs/dogs.component.html -->
<h1 class="text-center">{{ pageTitle }}</h1>
<ng-template #noDogs>
<app-loading *ngIf="loading"></app-loading>
<app-error *ngIf="error"></app-error>
</ng-template>
<div *ngIf="dogsList$ | async as dogsList; else noDogs">
<p class="lead">
These were the top <a href="http://www.akc.org/content/news/articles/the-labrador-retriever-wins-top-breed-for-the-26th-year-in-a-row/">10 most popular dog breeds in the United States in 2016</a>, ranked by the American Kennel Club (AKC).
</p>
<div class="row mb-3">
<div *ngFor="let dog of dogsList" class="col-xs-12 col-sm-6 col-md-4">
<div class="card my-2">
<img class="card-img-top" [src]="dog.image" [alt]="dog.breed">
<div class="card-body">
<h5 class="card-title">#{{ dog.rank }}: {{ dog.breed }}</h5>
<p class="text-right mb-0">
<a class="btn btn-primary" [routerLink]="['/dog', dog.rank]">Learn more</a>
</p>
</div>
</div>
</div>
</div>
</div>
<app-comments></app-comments>

此模板中有几件事,我们将仔细研究:

...
<ng-template #noDogs>
<app-loading *ngIf="loading"></app-loading>
<app-error *ngIf="error"></app-error>
</ng-template>
<div *ngIf="dogsList$ | async as dogsList; else noDogs">
...
<div *ngFor="let dog of dogsList" ...>
...

这段代码声明性地做了一些非常有用的事情。 让我们来探索。

首先,我们有一个带有模板引用变量 ( #noDogs )的<ng-template>元素 。 <ng-template>元素永远不会直接呈现。 它旨在与结构性指令(例如NgIf)一起使用。 在这种情况下,我们使用<ng-template #noDogs>创建了一个嵌入式视图,其中包含加载和错误组件。 这些组件中的每个组件都将根据条件进行渲染。 除非指示,否则noDogs嵌入式视图本身不会渲染。

免费学习PHP!

全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。

原价$ 11.95 您的完全免费

那么我们如何(何时)告诉该视图渲染呢?

下一个<div *ngIf="...实际上是一个NgIfElse,使用星号前缀作为语法糖 。我们还将异步管道与我们的dogsList$一起使用,并设置一个变量,以便我们可以在我们的流中引用流的发射值template( as dogsList )。如果dogsList$ observable出现问题,我们可以通过else noDogs语句告诉模板渲染<ng-template #noDogs>视图,这是在成功从中获取数据之前进行的。 API,或者观察者抛出错误。

如果dogsList$ | async dogsList$ | async已成功发出一个值,div将呈现,并且我们可以使用NgForOf( *ngFor )结构化指令来显示每个dogsList值(在我们的组件类中指定为Dog的数组)。狗的信息。

正如您在其余HTML中所看到的那样,将为每只狗显示图片,等级,品种以及指向其个人详细信息页面的链接,我们将在接下来创建这些页面。

通过浏览到应用程序的主页http:// localhost:4200来查看浏览器中的Dogs组件。 Angular应用程序应该向API发出请求,以获取狗的列表并显示它们!

注意:我们还包括了<app-comments>组件。 由于我们已经生成了此组件,但尚未实现其功能,因此它应该在用户界面中显示为“注释有效!”的文本。

要测试错误处理,可以停止API服务器(服务器命令提示符或终端中的Ctrl+c )。 然后尝试重新加载页面。 由于无法访问API,因此应该显示错误组件,并且我们应该在浏览器控制台中看到相应的错误:

带有Node.js API的Angular应用显示数据错误

带路由参数的狗详细信息

接下来,我们将实现Dog组件。 该路由组件用作每条狗的详细信息页面。 在本教程的第一部分中,我们已经设置了Dog模块架构以及路由和延迟加载 。 我们现在要做的就是执行!

提醒:您可能从第1部分中回想起,狗详细信息页面受AuthGuard路由保护器保护 。 这意味着访问者必须经过身份验证才能访问页面。 此外, API调用需要访问令牌才能返回数据。

狗组件类

打开dog.component.ts类文件并添加:

// src/app/dog/dog/dog.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Title } from '@angular/platform-browser';
import { ApiService } from '../../core/api.service';
import { DogDetail } from './../../core/dog-detail';
import { Subscription } from 'rxjs/Subscription';
import { Observable } from 'rxjs/Observable';
import { tap, catchError } from 'rxjs/operators';
@Component({
selector: 'app-dog',
templateUrl: './dog.component.html',
styles: [`
.dog-photo {
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: cover;
min-height: 250px;
width: 100%;
}
`]
})
export class DogComponent implements OnInit, OnDestroy {
paramSub: Subscription;
dog$: Observable<DogDetail>;
loading = true;
error: boolean;
constructor(
private route: ActivatedRoute,
private api: ApiService,
private title: Title
) { }
ngOnInit() {
this.paramSub = this.route.params
.subscribe(
params => {
this.dog$ = this.api.getDogByRank$(params.rank).pipe(
tap(val => this._onNext(val)),
catchError((err, caught) => this._onError(err, caught))
);
}
);
}
private _onNext(val: DogDetail) {
this.loading = false;
}
private _onError(err, caught): Observable<any> {
this.loading = false;
this.error = true;
return Observable.throw('An error occurred fetching detail data for this dog.');
}
getPageTitle(dog: DogDetail): string {
const pageTitle = `#${dog.rank}: ${dog.breed}`;
this.title.setTitle(pageTitle);
return pageTitle;
}
getImgStyle(url: string) {
return `url(${url})`;
}
ngOnDestroy() {
this.paramSub.unsubscribe();
}
}

该组件与我们的Dogs清单组件非常相似,只是有一些主要区别。

我们将导入必要的依赖项,并在类中私下使用ApiServiceTitle服务。

狗细节组件依赖于路由参数来确定我们需要为获取数据狗。 route参数与十种最受欢迎​​的狗的列表中所需的狗的排名相匹配,如下所示:

# URL for dog #2:
http://localhost:4200/dog/2

为了在组件类中访问此参数,我们需要导入ActivatedRoute接口 ,将其传递给构造函数,然后订阅激活的路由的observable params

然后,我们可以将rank参数传递给我们的getDogByRank$() API服务方法。 当组件被破坏时,我们还应该取消订阅可观察到的路由参数。 我们的dog$catchError可以使用类似于我们的Dogs列表组件的tapcatchError处理程序。

我们还需要两种方法来帮助我们的模板。

getPageTitle()方法使用API​​数据来生成包含狗的等级和品种的页面标题。

getImgStyle()方法使用API​​数据返回背景图像CSS值。

狗组件模板

现在,让我们在dog.component.html模板中使用以下方法:

<!-- src/app/dog/dog/dog.component.html -->
<ng-template #noDog>
<app-loading *ngIf="loading"></app-loading>
<app-error *ngIf="error"></app-error>
</ng-template>
<div *ngIf="dog$ | async as dog; else noDog">
<h1 class="text-center">{{ getPageTitle(dog) }}</h1>
<div class="row align-items-center pt-2">
<div class="col-12 col-sm-6">
<div
class="dog-photo rounded mb-2 mb-sm-0"
[style.backgroundImage]="getImgStyle(dog.image)"></div>
</div>
<ul class="list-unstyled col-12 col-sm-6">
<li><strong>Group:</strong> {{ dog.group }}</li>
<li><strong>Personality:</strong> {{ dog.personality }}</li>
<li><strong>Energy Level:</strong> {{ dog.energy }}</li>
</ul>
</div>
<div class="row">
<div class="col">
<p class="lead mt-3" [innerHTML]="dog.description"></p>
<p class="clearfix">
<a routerLink="/" class="btn btn-link float-left">&larr; Back</a>
<a
class="btn btn-primary float-right"
[href]="dog.link"
target="_blank">{{ dog.breed }} AKC Info</a>
</p>
</div>
</div>
</div>

总体而言,此模板的外观和功能与“狗列表”组件模板相似,不同之处在于,我们没有遍历数组。 相反,我们只显示一只狗的信息,页面标题是动态生成的,而不是静态生成的。 我们将在Bootstrap CSS类的帮助下,使用可观察对象发出的dog数据(来自dog$ | async as dog )显示详细信息。

完成后,组件在浏览器中应如下所示:

具有异步管道和身份验证的Angular应用-狗详细信息

要进入任何狗的详细信息页面, AuthGuard会提示未经AuthGuard验证的用户首先登录。 他们通过身份验证后,将被重定向到其请求的详细信息页面。 试试看!

注释模型类

现在我们的狗列表和详细信息页面已经完成,是时候添加实时评论了!

我们要做的第一件事是建立注释的形状,以及初始化新注释实例的方法。 让我们在Angular应用中实现comment.ts类:

// src/app/comments/comment.ts
export class Comment {
constructor(
public user: string,
public uid: string,
public picture: string,
public text: string,
public timestamp: number
) {}
// Workaround because Firestore won't accept class instances
// as data when adding documents; must unwrap instance to save.
// See: https://github.com/firebase/firebase-js-sdk/issues/311
public get getObj(): object {
const result = {};
Object.keys(this).map(key => result[key] = this[key]);
return result;
}
}

与我们的DogDogDetail模型不同,我们的Comment模型是一个 ,而不是一个interface 。 我们最终将在我们的评论表单组件中初始化Comment实例,为此,必须有一个类。 此外,Firestore在将文档添加到集合时仅接受常规JS对象,因此我们需要在类中添加一个将实例解包到对象的方法。 另一方面,接口仅提供对象的描述 。 这足以满足DogDogDetail ,但不足以进行Comment

渲染后,我们希望注释看起来像这样:

带有注释的Angular Firebase应用

如您所见,每个评论都有一个用户名,图片,评论文本以及日期和时间。 注释还需要一个唯一标识符,在数据中以uid 。 此唯一ID确保用户有权删除自己的评论,但不能删除其他人留下的评论。

现在,我们已经对注释的外观有了一定的了解,让我们开始设置Firebase Firestore规则。

Firebase Cloud Firestore和规则

我们将使用Firebase的Cloud Firestore数据库存储应用程序的评论。 Cloud Firestore是NoSQL,灵活,可扩展,由云托管的数据库,可提供实时功能。 在撰写本文时,Firestore处于beta版,但它是所有新的移动和Web应用程序的推荐数据库。 您可以在此处阅读有关在实时数据库(RTDB)和Cloud Firestore之间进行选择的更多信息。

提醒:如果您需要快速入门Firebase产品,请重新阅读如何使用Auth0 –第1部分:Firebase和Auth0来认证Firebase和Angular 。

Firestore将数据组织为集合中的 文档 。 如果您具有像MongoDB这样的面向文档的NoSQL数据库的经验,则应该熟悉此数据模型。 现在,选择Cloud Firestore作为我们的数据库。

  1. 登录到在本教程的第1部分中创建的Firebase项目 。
  2. 在侧边栏菜单中单击数据库
  3. 在数据库页面标题旁边的下拉菜单中,选择Cloud Firestore

添加收藏夹和第一个文档

默认情况下将显示“ 数据”选项卡,并且数据库中目前没有任何内容。 让我们添加集合和文档,以便我们可以在Angular中查询数据库并返回某些内容。

点击+添加收藏集 。 为您的收藏comments命名,然后单击“ 下一步”按钮。 系统将提示您添加第一个文档。

Firebase控制台-添加文档

文档ID字段中,点击自动ID 。 这将自动为您填充一个ID。 接下来,添加我们之前在comment.ts模型中建立的字段,以及适当的类型和一些占位符数据。 我们只需要此种子文档,直到我们知道清单在Angular应用程序中正确呈现为止,然后我们可以使用Firebase控制台将其删除,并在前端使用表单正确输入评论。

但是,由于我们还没有构建表单,因此种子数据会有所帮助。 输入正确的字段和类型后,可以随意填充值。 这是一个建议:

user <string>: Test User
uid <string>: abc-123
picture <string>: https://cdn.auth0.com/avatars/tu.png
text <string>: This is a test comment from Firebase console.
timestamp <number>: 1514584235257

注意:一旦设置了Firebase安全规则, 带有uid值的注释将 不会对任何经过身份验证的真实用户进行验证。 如果我们以后想要删除种子文件,则需要使用Firebase控制台删除它。 正如您将在以下规则中看到的那样,我们无权使用Angular应用中的SDK方法将其删除。

输入假用户的评论后,单击“ 保存”按钮。 新的集合和文档应填充在数据库中。 这提供了我们可以在Angular应用程序中查询的数据。

Firebase规则

接下来,让我们设置Firestore数据库的安全性。 现在切换到“ 规则”选项卡。

Firebase安全规则提供了后端安全性验证 。 在应用程序的Node API中,我们验证了用户是否已使用Auth0和JWT身份验证中间件授权访问端点 。 我们已经在API和Angular应用中设置了Firebase身份验证,并且将使用规则功能来授权数据库后端的权限。

规则是一种表达式,可以对其进行评估以确定是否允许请求执行所需的操作。 — Cloud Firestore安全规则参考

在Firebase数据库规则编辑器中添加以下代码。 我们将在下面详细介绍。

// Firebase Database Rules for Cloud Firestore
service cloud.firestore {
match /databases/{database}/documents {
match /comments/{document=**} {
allow read: if true;
allow create: if request.auth != null
&& request.auth.uid == request.resource.data.uid
&& request.resource.data.text is string
&& request.resource.data.text.size() <= 200;
allow delete: if request.auth != null
&& request.auth.uid == resource.data.uid;
}
}
}

Firestore具有规则请求方法 : readwrite 。 读取包括getlist操作。 写入包括createupdatedelete操作。 我们将实现readcreatedelete规则。

注意:我们不会在我们的应用程序中添加评论编辑功能,因此不包括update 但是,如果您想自己添加此功能,请随时添加update规则!

当用户请求match文档路径match时,将执行规则。 路径可以全称,也可以使用通配符。 我们的规则适用于我们创建的comments集合中的所有文档。

我们希望所有人 (匿名用户和经过身份验证的用户)都能阅读评论。 因此, allow read的条件就是if true

我们只希望经过身份验证的用户能够创建新评论。 我们将验证用户是否登录,并确保要保存的数据具有与用户的身份验证uid (Firebase规则中的request.auth.uid )匹配的uid属性。 此外,我们可以在此处进行一些字段验证。 我们将检查请求的数据是否具有text属性,该属性是字符串,并且为200个字符或更少(我们还将很快在Angular应用中添加此验证)。

最后,我们只希望用户能够删除自己的评论。 如果通过身份验证的用户的UID使用resource.data.uid匹配现有注释的uid属性,我们可以allow delete

注意:您可以在Firebase文档中了解有关request和resource关键字的更多信息。

评论组件

现在我们的数据库已经准备好了,是时候返回我们的Angular应用程序并实现实时注释了!

我们要做的第一件事是显示评论。 我们希望评论能够实时异步更新,因此让我们探索如何使用Cloud Firestore数据库和angularfire2 SDK进行操作 。

注释组件类

我们已经为Comments模块创建了架构,所以让我们开始构建comments.component.ts

// src/app/comments/comments/comments.component.ts
import { Component } from '@angular/core';
import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument } from 'angularfire2/firestore';
import { Observable } from 'rxjs/Observable';
import { map, catchError } from 'rxjs/operators';
import { Comment } from './../comment';
import { AuthService } from '../../auth/auth.service';
@Component({
selector: 'app-comments',
templateUrl: './comments.component.html',
styleUrls: ['./comments.component.css']
})
export class CommentsComponent {
private _commentsCollection: AngularFirestoreCollection<Comment>;
comments$: Observable<Comment[]>;
loading = true;
error: boolean;
constructor(
private afs: AngularFirestore,
public auth: AuthService
) {
// Get latest 15 comments from Firestore, ordered by timestamp
this._commentsCollection = afs.collection<Comment>(
'comments',
ref => ref.orderBy('timestamp').limit(15)
);
// Set up observable of comments
this.comments$ = this._commentsCollection.snapshotChanges()
.pipe(
map(res => this._onNext(res)),
catchError((err, caught) => this._onError(err, caught))
);
}
private _onNext(res) {
this.loading = false;
this.error = false;
// Add Firestore ID to comments
// The ID is necessary to delete specific comments
return res.map(action => {
const data = action.payload.doc.data() as Comment;
const id = action.payload.doc.id;
return { id, ...data };
});
}
private _onError(err, caught): Observable<any> {
this.loading = false;
this.error = true;
return Observable.throw('An error occurred while retrieving comments.');
}
onPostComment(comment: Comment) {
// Unwrap the Comment instance to an object for Firestore
// See https://github.com/firebase/firebase-js-sdk/issues/311
const commentObj = <Comment>comment.getObj;
this._commentsCollection.add(commentObj);
}
canDeleteComment(uid: string): boolean {
if (!this.auth.loggedInFirebase || !this.auth.userProfile) {
return false;
}
return uid === this.auth.userProfile.sub;
}
deleteComment(id: string) {
// Delete comment with confirmation prompt first
if (window.confirm('Are you sure you want to delete your comment?')) {
const thisDoc: AngularFirestoreDocument<Comment> = this.afs.doc<Comment>(`comments/${id}`);
thisDoc.delete();
}
}
}

首先,我们将导入必要的angularfire2依赖项以使用Firestore,集合和文档。 我们还需要catchError Observablemap和catchError,我们的Comment模型和AuthService

接下来,我们将宣布成员。 私有_commentsCollection是一个Firestore集合,其中包含Comment形状的项目。 comments$ observable是一个流,该流的值采用Comment数组的形式。 然后,我们有了通常的loadingerror属性。

AngularFirestoreAuthService传递给构造函数后,我们需要从Cloud Firestore获取集合数据。 我们将使用angularfire2方法collection()进行此操作,将Comment指定为类型,传递集合的名称( comments ), 按timestamp对结果进行排序,并限制为最后15条注释。

接下来,我们将使用_commentsCollection创建可观察的_commentsCollection comments$ 。 我们将使用map()catchError() RxJS运算符来处理发出的数据和错误。

在我们的_onNext()私有处理程序中,我们将loadingerror设置为false 。 我们还将Firestore文档ID添加到comments$流发出的数组中的每个项目中。 我们需要这些ID,以便允许用户删除个别评论。 为了将ID添加到发出的值,我们将使用snapshotChanges()方法访问meta 。 然后,我们可以使用Spread运算符将文档id map()到返回的数据中。

注意:您可能会注意到,在我们的狗或狗可观察对象的成功方法中,我们没有将error设置为false ,但是我们在这里这样做。 每当 任何用户实时添加评论时, 评论流就会发出一个值 。 因此,作为响应,我们可能需要异步重置错误状态。

私有_onError()处理函数应该从我们的其他组件中看起来非常熟悉。 它设置loadingerror属性并引发错误。

当用户使用评论表单组件(稍后将构建)提交评论时,将运行onPostComment()方法。 onPostComment()有效负载将包含一个Comment实例,该实例包含用户的注释数据,然后需要将其解包装为普通对象才能保存在Firestore中。 我们将使用Angular Firestore add()方法保存解包后的注释对象。

canDeleteComment()方法检查当前用户是否为任何给定注释的所有者。 如果他们创建了评论,则也可以将其删除。 此方法验证登录用户的userProfile.sub属性是否与注释的uid相匹配。

当用户单击图标删除评论时, deleteComment()方法将运行。 此方法将打开一个确认对话框,以确认操作,如果确认,则使用id参数从Firestore集合中删除正确的注释文档。 (这就是为什么我们在映射observations comments$ observable发出的值时需要在数据中添加文档id的原因。)

注意:请记住,我们的Firestore规则还阻止用户删除他们未创建的评论。 我们应始终确保在前端和后端 强制执行访问权限,以确保适当的安全性。

注释组件模板

现在,让我们在UI中使用我们的类功能。 打开comments.component.html文件并添加:

<!-- src/app/comments/comments/comments.component.html -->
<section class="comments py-3">
<h3>Comments</h3>
<ng-template #noComments>
<p class="lead" *ngIf="loading">
<app-loading [inline]="true"></app-loading>Loading comments...
</p>
<app-error *ngIf="error"></app-error>
</ng-template>
<div *ngIf="comments$ | async; let commentsList; else noComments">
<ul class="list-unstyled">
<li *ngFor="let comment of commentsList" class="pt-2">
<div class="row mb-1">
<div class="col">
<img [src]="comment.picture" class="avatar rounded">
<strong>{{ comment.user }}</strong>
<small class="text-info">{{ comment.timestamp | date:'short' }}</small>
<strong>
<a
*ngIf="canDeleteComment(comment.uid)"
class="text-danger"
title="Delete"
(click)="deleteComment(comment.id)">&times;</a>
</strong>
</div>
</div>
<div class="row">
<div class="col">
<p class="comment-text rounded p-2 my-2" [innerHTML]="comment.text"></p>
</div>
</div>
</li>
</ul>
<div *ngIf="auth.loggedInFirebase; else logInToComment">
<app-comment-form (postComment)="onPostComment($event)"></app-comment-form>
</div>
<ng-template #logInToComment>
<p class="lead" *ngIf="!auth.loggedIn">
Please <a class="text-primary" (click)="auth.login()">log in</a> to leave a comment.
</p>
</ng-template>
</div>
</section>

我们将主要使用Bootstrap类来设置注释的样式,然后再添加一些自定义CSS。 我们的注释模板(如我们的狗和狗组件模板)具有一个<ng-template> ,并将异步管道与NgIfElse一起使用以显示适当的UI。

评论列表应显示评论的picture (作者的用户头像),用户name以及使用DatePipe格式化的timestamp 。 我们将注释的uid传递给canDeleteComment()方法,以确定是否应显示删除链接。 然后,使用绑定到innerHTML属性来显示注释text

最后,我们将创建元素以显示评论表单或一条消息,指示用户如果要发表评论,请登录。

注意:当用户提交评论时,我们的<app-comment-form>将使用事件绑定来发出名为postComment的事件。 CommentsComponent类侦听该事件,并使用我们创建的onPostComment()方法处理该事件,并使用$event有效负载将提交的注释保存到Firestore数据库。 在下一节中创建表单时,我们将连接(postComment)事件。

注释组件CSS

最后,打开comments.component.css文件,让我们在注释列表中添加一些样式:

/* src/app/comments/comments/comments.component.css */
.avatar {
display: inline-block;
height: 30px;
}
.comment-text {
background: #eee;
position: relative;
}
.comment-text::before {
border-bottom: 10px solid #eee;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
content: '';
display: block;
height: 1px;
position: absolute;
top: -10px; left: 9px;
width: 1px;
}

评论表格组件

现在我们有了一个实时更新的评论列表,我们需要能够在前端添加新评论。

注释表单组件类

打开comment-form.component.ts文件,让我们开始吧:

// src/app/comments/comment-form/comment-form.component.ts
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
import { Comment } from './../../comment';
import { AuthService } from '../../../auth/auth.service';
@Component({
selector: 'app-comment-form',
templateUrl: './comment-form.component.html'
})
export class CommentFormComponent implements OnInit {
@Output() postComment = new EventEmitter<Comment>();
commentForm: Comment;
constructor(private auth: AuthService) { }
ngOnInit() {
this._newComment();
}
private _newComment() {
this.commentForm = new Comment(
this.auth.userProfile.name,
this.auth.userProfile.sub,
this.auth.userProfile.picture,
'',
null);
}
onSubmit() {
this.commentForm.timestamp = new Date().getTime();
this.postComment.emit(this.commentForm);
this._newComment();
}
}

如前所述,我们需要从该组件向父CommentsComponent发出一个事件,该事件将新注释发送到Firestore。 CommentFormComponent负责使用从经过身份验证的用户及其表单输入中收集的适当信息构造Comment实例,并将该数据发送给父级。 为了发出postComment事件,我们将导入OutputEventEmitter 。 我们还需要我们的Comment类和AuthService来获取用户数据。

我们的评论表单组件的构件包括输出装饰 ( postComment ),其为EventEmitter类型的Comment ,并commentForm ,这将是一个实例Comment来存储表单数据。

ngOnInit()方法中,我们将使用私有_newComment()方法创建一个新的Comment实例。 此方法将本地commentForm属性设置为带有已验证用户的namesubpictureComment的新实例。 注释text为空字符串, timestamp设置为null (提交表单时将添加timestamp )。

在模板中提交注释表单时,将执行onSubmit()方法。 此方法添加timestamp并发出带有commentForm数据作为其有效负载的postComment事件。 它还调用_newComment()方法来重置评论表单。

注释表单组件模板

打开comment-form.component.html文件并添加以下代码:

<!-- src/app/comments/comment-form/comment-form.component.html -->
<form (ngSubmit)="onSubmit()" #tplForm="ngForm">
<div class="row form-inline m-1">
<input
type="text"
class="form-control col-sm-10 mb-2 mb-sm-0"
name="text"
[(ngModel)]="commentForm.text"
maxlength="200"
required>
<button
class="btn btn-primary col ml-sm-2"
[disabled]="!tplForm.valid">Send</button>
</div>
</form>

评论表单模板非常简单。 表单的唯一字段是文本输入,因为所有其他注释数据(如名称,图片,UID等)都动态添加到了类中。 我们将使用简单的模板驱动表单来实现我们的评论表单。

<form>元素侦听(ngOnSubmit)事件,我们将使用onSubmit()方法处理该事件。 我们还将添加一个名为#tplForm的模板引用变量,并将其设置为ngForm 。 这样,我们可以在模板本身中访问表单的属性。

<input>元素应具有绑定到commentForm.text[(ngModel)] 。 这是我们在用户在表单字段中键入内容时要更新的属性。 回想一下,我们设置了Firestore规则以接受200个字符或更少的注释文本,因此我们将此maxlengthrequired属性一起添加到前端,以便用户不能提交空注释。

最后,如果表单无效,则应[disabled] <button>提交表单的<button> 。 我们可以使用添加到<form>元素的tplForm参考变量来参考valid属性。

实时评论

在浏览器中验证注释是否按预期显示。 到目前为止,唯一的注释应该是我们直接在Firebase中添加的种子注释。 提取并呈现后,我们的评论列表应如下所示:

带有Angular的Firebase Firestore注释

如果用户已通过身份验证,则应显示评论表单。 登录并尝试添加评论。

删除种子评论

用户可以删除自己的评论。 如果用户是评论的所有者,则评论的日期和时间旁边应显示一个红色的x 。 单击此删除图标提示确认,然后实时删除评论。

请记住,我们在Firebase中添加的种子文档无法在Angular应用中删除,因为其uid属性与任何实际用户的数据都不匹配。 让我们现在手动将其删除。

打开Firebase控制台,然后查看您的Firestore comments集合。 查找包含种子注释的文档。 使用右上角的菜单下拉菜单,选择删除文档以将其删除:

Firebase删除评论

现在,添加到我们数据库中的所有注释都应该可以由其作者在后端删除。

在Angular App中添加评论

添加注释后,它们应该会显示出来,这很棒,但是并不能真正显示Firestore数据库的真正实时性。 我们也可以使用传统的服务器和数据库在UI中添加注释,而无需刷新,只需更新视图即可。

Firebase Firestore的角度形式

为了真正看到我们的实时数据库在工作,请在第二个浏览器中打开该应用程序,然后使用其他登录名进行身份验证。 在两个浏览器都可见的情况下,在一个浏览器中添加评论。 它将同时出现在第二个浏览器中。

在Angular中使用Firestore实时评论

这就是Firebase的实时数据库可以做到的!

结论

恭喜你! 您现在有了一个Angular应用,该应用通过Auth0对Firebase进行身份验证,并基于可扩展的体系结构构建。

我们的教程的第一部分, 如何使用Auth0验证Firebase和Angular:第1部分 ,内容包括:

  • Auth0和Firebase的介绍和设置
  • 实施安全的Node API,以生成自定义Firebase令牌并为我们的应用程序提供数据
  • 具有模块和延迟加载的Angular应用程序体系结构
  • 具有服务和路由保护的Auth0的角度身份验证
  • 共享的Angular组件和API服务。

本教程的第二部分介绍:

  • 使用异步管道和NgIfElse显示数据
  • 使用路线参数
  • 用类建模数据
  • Firebase Cloud Firestore数据库和安全规则
  • 使用angularfire2在Angular中实现Firestore数据库
  • 具有组件交互的简单模板驱动形式。

角度测试资源

如果您有兴趣了解有关在Angular中进行测试的更多信息,而本教程未涵盖此内容,请查看以下一些资源:

  • 角度-测试
  • 深度角度测试:服务
  • 深度角度测试:HTTP服务
  • 深度角度测试:组件
  • 如何使用Auth0集成正确测试Angular 4应用程序

其他资源

您可以在以下位置找到有关Firebase,Auth0和Angular的更多资源:

  • Firebase文档
  • Cloud Firestore文档
  • angularfire2文档
  • Auth0文档
  • Auth0定价和功能
  • Angular文档
  • 角度CLI
  • 角备忘单

下一步是什么?

希望您学到了很多有关使用Angular构建可扩展应用程序以及使用自定义令牌对Firebase进行身份验证的知识。 如果您正在寻找可以扩展我们所构建内容的想法,请参考以下建议:

  • 对评论实施不适当的语言过滤器
  • 实施授权角色以创建具有删除其他人评论的权限的管理员用户
  • 添加功能以支持评论编辑
  • 使用其他Firestore集合向单个狗详细信息页面添加评论
  • 添加测试
  • 以及更多!

翻译自: https://www.sitepoint.com/authenticating-firebase-angular-auth0-2/

firebase auth

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 如何使用Django REST框架实现令牌身份验证

    在本教程中&#xff0c;您将学习如何使用Django REST框架(DRF)实现基于标记的身份验证。令牌身份验证的工作方式是将用户名和密码交换为令牌&#xff0c;以便在所有后续请求中使用该令牌来标识服务器端的用户。 1. 设置项目 让我们从头开始。安装Django和DRF: pip install dj…...

    2024/5/2 9:38:48
  2. web应用程序的身份验证_向任何应用程序添加身份验证的最简单方法

    web应用程序的身份验证TL;DR: Implementing authentication in modern web apps is a pain, but it doesnt have to be. Auth0 is the easiest way to add authentication to your app and you can get started easily with a free plan that includes up to 7,000 regular act…...

    2024/4/21 6:24:05
  3. AngularJS 如何做身份验证

    权限的设计中比较常见的就是RBAC基于角色的访问控制,基本思想是&#xff0c;对系统操作的各种权限不是直接授予具体的用户&#xff0c;而是在用户集合与权限集合之间建立一个角色集合。每一种角色对应一组相应的权限。 一旦用户被分配了适当的角色后&#xff0c;该用户就拥有此…...

    2024/4/28 0:47:56
  4. 做双眼皮手术哪家好

    ...

    2024/5/7 20:45:29
  5. 令牌提交的身份验证失败_AngularJS和Laravel应用的基于令牌的身份验证

    令牌提交的身份验证失败Adding authentication to an AngularJS and Laravel application is not the most straight-forward, especially if we take the approach of creating independent front-end and backend applications and connecting them with an API exposed by L…...

    2024/5/8 0:56:07
  6. 在20分钟内创建一个带有身份验证的Angular应用

    本文最初发布在OKTA开发人员博客上 。 感谢您支持使SitePoint成为可能的合作伙伴。 Angular&#xff08;以前称为Angular 2.0&#xff09;正迅速成为构建现代单页应用程序的最强大方法之一。 Angular的核心优势是致力于构建可重用组件&#xff0c;这有助于您消除应用程序中的各…...

    2024/4/21 6:24:01
  7. 使用Auth0对Firebase和Angular进行身份验证:第2部分

    本文最初发布在Auth0.com博客上 &#xff0c;并经许可在此处重新发布。 在这个由两部分组成的教程系列中&#xff0c;我们将学习如何构建一个使用Auth0身份验证保护Node后端和Angular前端安全的应用程序。 我们的服务器和应用程序还将使用自定义令牌对Firebase Cloud Firestore…...

    2024/4/21 6:24:01
  8. ASP.NET Core:使用IdentityServer构建可靠的身份验证和授权系统

    目录 OAuth 2.0到底是什么&#xff1f; 为什么使用OpenID Connect&#xff1f; 向IdentityServer打个招呼&#xff01;&#x1f44b; Hello World程序 设置中央身份验证系统所需的步骤——从1000英尺开始查看 从头开始设置身份验证服务器 构建一个MVC应用 设置一个Angu…...

    2024/5/7 19:33:41
  9. 埋线双眼皮医院哪家好

    ...

    2024/5/7 15:27:53
  10. 使用Auth0对Firebase和Angular进行身份验证:第1部分

    本文最初发布在Auth0.com博客上 &#xff0c;并经许可在此处重新发布。 在这个由两部分组成的教程系列中&#xff0c;我们将学习如何构建一个使用Auth0身份验证保护Node后端和Angular前端安全的应用程序。 我们的服务器和应用程序还将使用自定义令牌对Firebase Cloud Firestore…...

    2024/4/23 9:16:55
  11. Angular:路由守卫AuthGuardProvider中的checkLogin验证登录状态

    系统中路由想要被访问&#xff0c;需要先通过路由守卫验证&#xff0c;看当前用户是否登录&#xff1a; AuthGuardProvider路由守卫服务&#xff1a; /** Author: Wangcan* Date: 2018-05-16 13:34:23* Last Modified by: Wangcan* Last Modified time: 2019-08-17 15:46:20*…...

    2024/4/29 0:09:30
  12. 在Angular应用程序中实现身份验证

    身份验证和授权是几乎每个严肃应用程序中的重要部分。 单页应用程序&#xff08;SPA&#xff09;也不例外。 该应用程序可能不会仅向任何用户公开其所有数据和功能。 用户可能必须进行身份验证才能查看应用程序的某些部分&#xff0c;或对应用程序执行某些操作。 要在应用程序中…...

    2024/4/20 19:07:45
  13. 荆州哪里割双眼皮很好

    ...

    2024/4/20 19:07:43
  14. flask身份验证_Flask基于令牌的身份验证

    flask身份验证This tutorial takes a test-first approach to implementing token-based authentication in a Flask app using JSON Web Tokens (JWTs). 本教程采用测试优先的方法&#xff0c;使用JSON Web令牌&#xff08;JWT&#xff09;在Flask应用中实现基于令牌的身份验证…...

    2024/4/20 19:07:43
  15. 在SPA应用中利用JWT进行身份验证

    在SPA应用中利用JWT进行身份验证 2018.3.21版权声明&#xff1a;本文为博主chszs的原创文章&#xff0c;未经博主允许不得转载。 SPA SPA即Single Page Application&#xff0c;单页应用程序&#xff0c;是一种Web应用程序或网站&#xff0c;通过动态地重写当前的页面&#…...

    2024/4/20 19:07:41
  16. angular学习之路14-表单验证

    1&#xff0c;模板驱动验证 每当表单控件中的值发生变化时&#xff0c;Angular 就会进行验证&#xff0c;并生成一个验证错误的列表&#xff08;对应着 INVALID 状态&#xff09;或者 null&#xff08;对应着 VALID 状态&#xff09;。 你可以通过把 ngModel 导出成局部模板变…...

    2024/4/21 6:24:00
  17. 埋线双眼皮哪里做的好

    ...

    2024/4/21 6:23:59
  18. angular 异步验证_在Angular应用程序中实现身份验证

    angular 异步验证身份验证和授权是几乎所有严肃应用程序中的重要组成部分。 单页应用程序&#xff08;SPA&#xff09;也不例外。 该应用程序可能不会仅向任何用户公开其所有数据和功能。 用户可能必须进行身份验证才能查看应用程序的某些部分&#xff0c;或对应用程序执行某些…...

    2024/4/21 6:23:58
  19. 韩式双眼皮需要多少钱

    ...

    2024/4/21 6:24:00
  20. 北京生物焊接内双 割合肥韩式三点北京双眼皮多少钱

    ...

    2024/4/21 6:23:55

最新文章

  1. 借助Aspose.SVG图像控件,在线将 PNG 转换为 Base64 字符串

    Aspose.SVG for .NET 是用于SVG文件处理的灵活库&#xff0c;并且与其规范完全兼容。API可以轻松加载&#xff0c;保存和转换SVG文件&#xff0c;以及通过其文档对象模型&#xff08;DOM&#xff09;读取和遍历文件的元素。API独立于任何其他软件&#xff0c;使开发人员无需使用…...

    2024/5/8 7:18:33
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/7 10:36:02
  3. Vue 有哪些常用的指令

    目录 1. 指令 v-html 1.1. 作用 1.2. 语法 1.3. 练习 2. 指令 v-show 2.1. 作用 2.2. 语法 3. 原理 4. 场景 3. 指令 v-if 3.1. 作用 3.2. 语法 3.3. 原理 3.4. 场景 4. 指令 v-else与 v-else-if 4.1. 作用 4.2. 语法 4.3. 注意 4.4. 使用场景 5. 指令 v-on 5…...

    2024/4/30 12:03:19
  4. 【嵌入式开发 Linux 常用命令系列 4.3 -- git add 不 add untracked file】

    请阅读【嵌入式开发学习必备专栏 】 文章目录 git add 不add untracked file git add 不add untracked file 如果你想要Git在执行git add .时不添加未跟踪的文件&#xff08;untracked files&#xff09;&#xff0c;你可以使用以下命令&#xff1a; git add -u这个命令只会加…...

    2024/5/5 8:53:25
  5. 深入理解springboot

    第五章 访问数据库 1.配置数据源 在applicaiton.properties中 spring.datasource.urljdbc:mysql://localhost:3306/chapter5 第九章 springmvc 1.总体流程 http请求发送给控制器&#xff0c;控制器与业务层交互&#xff0c;业务层使用noSQL缓存&#xff0c;业务层与数据访问层…...

    2024/5/5 15:07:17
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/8 6:01:22
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/7 9:45:25
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/7 14:25:14
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/7 11:36:39
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/6 1:40:42
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/7 9:26:26
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/6 21:42:42
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57