目录(6 - 12 章)
6. 管道 Pipe
管道的作用是格式化组件模板数据。
1. 内置管道
- date 日期格式化
- currency 货币格式化
- uppercase 转大写
- lowercase 转小写
- json 格式化json 数据
2. 自定义管道
需求:指定字符串不能超过规定的长度
运行: ng g p pipes/summary 创建管道
// summary.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'summary'
});
export class SummaryPipe implements PipeTransform {
transform (value: string, limit?: number) {
if (!value) return null;
let actualLimit = (limit) ? limit : 50;
return value.substr(0, actualLimit) + '...';
}
}
// app.module.ts
import { SummaryPipe } from './summary.pipe'
@NgModule({
declarations: [
SummaryPipe
]
});
7. 组件通讯
1. 向组件内部传递数据
// favorite.component.ts
import { Input } from '@angular/core';
export class FavoriteComponent {
@Input() isFavorite: boolean = false;
}
注意:在属性的外面加 [] 表示绑定动态值,在组件内接收后是布尔类型,不加 [] 表示绑定普通值,在组
件内接收后是字符串类型。
import { Input } from '@angular/core';
export class FavoriteComponent {
@Input("is-Favorite") isFavorite: boolean = false
}
2. 组件向外部传递数据
举例:在子组件中通过点击按钮将数据传递给父组件
<!-- 子组件模板 -->
<button (click)="onClick()">click</button>
// 子组件类
import { EventEmitter, Output } from "@angular/core"
export class FavoriteComponent {
@Output() change = new EventEmitter()
onClick() {
this.change.emit({ name: "张三" }
)
}
}
<!-- 父组件模板 -->
<app-favorite (change)="onChange($event)"></app-favorite>
// 父组件类
export class AppComponent {
onChange(event: { name: string }) {
console.log(event)
}
}
8. 组件生命周期
1. 挂载阶段
挂载阶段的生命周期函数只在挂载阶段执行一次,数据更新时不再执行。
1. constructor
Angular 在实例化组件类时执行, 可以用来接收 Angular 注入的服务实例对象。
2. ngOnInit
在首次接收到输入属性值后执行,在此处可以执行请求操作。
3. ngAfterContentInit
当内容投影初始渲染完成后调用。
4. ngAfterViewInit
当组件视图渲染完成后调用。
2. 更新阶段
1. ngOnChanges
- 当输入属性值发生变化时执行,初始设置时也会执行一次,顺序优于 ngOnInit
- 不论多少输入属性同时变化,钩子函数只会执行一次,变化的值会同时存储在参数中
- 参数类型为 SimpleChanges,子属性类型为 SimpleChange
- 对于基本数据类型来说, 只要值发生变化就可以被检测到
- 对于引用数据类型来说, 可以检测从一个对象变成另一个对象, 但是检测不到同一个对象中属
性值的变化,但是不影响组件模板更新数据。
2. ngDoCheck
主要用于调试,只要输入属性发生变化,不论是基本数据类型还是引用数据类型还是引用数据类型中的属性变化,都会执行。
3. ngAfterContentChecked
内容投影更新完成后执行。
4. ngAfterViewChecked
组件视图更新完成后执行。
3. 卸载阶段
ngOnDestroy
当组件被销毁之前调用, 用于清理操作。
9. 依赖注入
1. 概述
依赖注入 ( Dependency Injection ) 简称DI,是面向对象编程中的一种设计原则,用来减少代码之间的耦合度。
2. DI 框架
Angular 的 DI 框架中有四个核心概念:
- Dependency:组件要依赖的实例对象,服务实例对象
- Token:获取服务实例对象的标识
- Injector:注入器,负责创建维护服务类的实例对象并向组件中注入服务实例对象。
- Provider:配置注入器的对象,指定创建服务实例对象的服务类和获取实例对象的标识。
10. 服务 Service
1. 创建服务
// 创建
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class TestService { }
// 引用
export class AppComponent {
constructor (private testService: TestService) {}
}
2. 服务的作用域
使用服务可以轻松实现跨模块跨组件共享数据,这取决于服务的作用域。
- 在根注入器中注册服务,所有模块使用同一个服务实例对象。
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class CarListService {
}
- 在模块级别注册服务,该模块中的所有组件使用同一个服务实例对象。
import { Injectable } from '@angular/core';
import { CarModule } from './car.module';
@Injectable({
providedIn: CarModule,
})
export class CarListService {
}
import { CarListService } from './car-list.service';
@NgModule({
providers: [CarListService],
})
export class CarModule {
}
- 在组件级别注册服务,该组件及其子组件使用同一个服务实例对象。
import { Component } from '@angular/core';
import { CarListService } from '../car-list.service.ts'
@Component({
selector: 'app-car-list',
templateUrl: './car-list.component.html',
providers: [ CarListService ]
})
11. 表单
1. 模板驱动
1. 简介
表单控制逻辑写在组件模板中,适用于简单表单类型
2. 快速上手
1. 引入依赖模板 FormsModule
import { FormsModule } from "@angular/forms"
@NgModule({
imports: [FormsModule],
})
export class AppModule {}
2. 将 DOM 表单转换为 ngForm
3. 声明表单字段为 ngModel
<form #f="ngForm" (submit)="onSubmit(f)">
<input type="text" name="username" ngModel />
<button>提交</button>
</form>
4. 获取表单字段值
import { NgForm } from "@angular/forms"
export class AppComponent {
onSubmit(form: NgForm) {
console.log(form.value)
}
}
5. 表单分组
<form #f="ngForm" (submit)="onSubmit(f)">
<div ngModelGroup="user">
<input type="text" name="username" ngModel />
</div>
<div ngModelGroup="contact">
<input type="text" name="phone" ngModel />
</div>
<button>提交</button>
</form>
3. 表单验证
- required 必填字段
- minlength 字段最小长度
- maxlength 字段最大长度
- pattern 验证正则 例如:pattern=“\d” 匹配一个数值
<form #f="ngForm" (submit)="onSubmit(f)">
<input type="text" name="username" ngModel required pattern="\d" />
<button>提交</button>
</form>
export class AppComponent {
onSubmit(form: NgForm) {
// 查看表单整体是否验证通过
console.log(form.valid)
}
}
<!-- 表单整体未通过验证时禁用提交表单 -->
<button type="submit" [disabled]="f.invalid">提交</button>
在组件模板中显示表单项未通过时的错误信息。
<form #f="ngForm" (submit)="onSubmit(f)">
<input #username="ngModel" />
<div *ngIf="username.touched && !username.valid && username.errors">
<div *ngIf="username.errors.required">请填写用户名</div>
<div *ngIf="username.errors.pattern">不符合正则规则</div>
</div>
</form>
指定表单项未通过验证时的样式。
input.ng-touched.ng-invalid {
border: 2px solid red;
}
2. 模型驱动
1. 概述
表单的控制逻辑写在组件类中,对验证逻辑拥有更多的控制权,适合复杂的表单的类型。
在模型驱动表单中,表单字段需要是 FormControl 类的实例,实例对象可以验证表单字段中的值,值是
否被修改过等等
一组表单字段构成整个表单,整个表单需要是 FormGroup 类的实例,它可以对表单进行整体验证。
- FormControl:表单组中的一个表单项
- FormGroup:表单组,表单至少是一个 FormGroup
- FormArray:用于复杂表单,可以动态添加表单项或表单组,在表单验证时,FormArray 中有一项
没通过,整体没通过。
2. 快速上手
- 引入 ReactiveFormsModule
import { ReactiveFormsModule } from "@angular/forms"
@NgModule({
imports: [ReactiveFormsModule]
})
export class AppModule {}
- 在组件类中创建 FormGroup 表单控制对象
import { FormControl, FormGroup } from "@angular/forms"
export class AppComponent {
contactForm: FormGroup = new FormGroup({
name: new FormControl(),
phone: new FormControl()
})
}
- 关联组件模板中的表单
<form [formGroup]="contactForm" (submit)="onSubmit()">
<input type="text" formControlName="name" />
<input type="text" formControlName="phone" />
<button>提交</button>
</form>
- 获取表单值
export class AppComponent {
onSubmit() {
console.log(this.contactForm.value)
}
}
- 设置表单默认值
contactForm: FormGroup = new FormGroup({
name: new FormControl("默认值"),
phone: new FormControl(15888888888)
})
- 表单分组
contactForm: FormGroup = new FormGroup({
fullName: new FormGroup({
firstName: new FormControl(),
lastName: new FormControl()
}),
phone: new FormControl()
})
<form [formGroup]="contactForm" (submit)="onSubmit()">
<div formGroupName="fullName">
<input type="text" formControlName="firstName" />
<input type="text" formControlName="lastName" />
</div>
<input type="text" formControlName="phone" />
<button>提交</button>
</form>
onSubmit() {
console.log(this.contactForm.value.name.username)
console.log(this.contactForm.get(["name", "username"])?.value)
}
3. FormArray
需求:在页面中默认显示一组联系方式,通过点击按钮可以添加更多联系方式组。
import { Component, OnInit } from "@angular/core"
import { FormArray, FormControl, FormGroup } from "@angular/forms"
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styles: []
})
export class AppComponent implements OnInit {
// 表单
contactForm: FormGroup = new FormGroup({
contacts: new FormArray([])
})
get contacts() {
return this.contactForm.get("contacts") as FormArray
}
// 添加联系方式
addContact() {
// 联系方式
const myContact: FormGroup = new FormGroup({
name: new FormControl(),
address: new FormControl(),
phone: new FormControl()
})
// 向联系方式数组中添加联系方式
this.contacts.push(myContact)
}
// 删除联系方式
removeContact(i: number) {
this.contacts.removeAt(i)
}
ngOnInit() {
// 添加默认的联系方式
this.addContact()
}
onSubmit() {
console.log(this.contactForm.value)
}
}
<form [formGroup]="contactForm" (submit)="onSubmit()">
<div formArrayName="contacts">
<div
*ngFor="let contact of contacts.controls; let i = index"
[formGroupName]="i"
>
<input type="text" formControlName="name" />
<input type="text" formControlName="address" />
<input type="text" formControlName="phone" />
<button (click)="removeContact(i)">删除联系方式</button>
</div>
</div>
<button (click)="addContact()">添加联系方式</button>
<button>提交</button>
</form>
4. 内置表单验证器
- 使用内置验证器提供的验证规则验证表单字段
import { FormControl, FormGroup, Validators } from "@angular/forms"
contactForm: FormGroup = new FormGroup({
name: new FormControl("默认值", [
Validators.required,
Validators.minLength(2)
])
})
- 获取整体表单是否验证通过
onSubmit() {
console.log(this.contactForm.valid)
}
<!-- 表单整体未验证通过时禁用表单按钮 -->
<button [disabled]="contactForm.invalid">提交</button>
- 在组件模板中显示为验证通过时的错误信息
get name() {
return this.contactForm.get("name")!
}
<form [formGroup]="contactForm" (submit)="onSubmit()">
<input type="text" formControlName="name" />
<div *ngIf="name.touched && name.invalid && name.errors">
<div *ngIf="name.errors.required">请填写姓名</div>
<div *ngIf="name.errors.maxlength">
姓名长度不能大于
{{ name.errors.maxlength.requiredLength }} 实际填写长度为
{{ name.errors.maxlength.actualLength }}
</div>
</div>
</form>
5. 自定义同步表单验证器
- 自定义验证器的类型是 TypeScript 类
- 类中包含具体的验证方法,验证方法必须为静态方法
- 验证方法有一个参数 control,类型为 AbstractControl。其实就是 FormControl 类的实例对象的
类型 - 如果验证成功,返回 null
- 如果验证失败,返回对象,对象中的属性即为验证标识,值为 true,标识该项验证失败
- 验证方法的返回值为 ValidationErrors | null
import { AbstractControl, ValidationErrors } from "@angular/forms"
export class NameValidators {
// 字段值中不能包含空格
static cannotContainSpace(control: AbstractControl): ValidationErrors | null {
// 验证未通过
if (/\s/.test(control.value)) return { cannotContainSpace: true }
// 验证通过
return null
}
}
import { NameValidators } from "./Name.validators"
contactForm: FormGroup = new FormGroup({
name: new FormControl("", [
Validators.required,
NameValidators.cannotContainSpace
])
})
<div *ngIf="name.touched && name.invalid && name.errors">
<div *ngIf="name.errors.cannotContainSpace">姓名中不能包含空格</div>
</div>
6. 自定义异步表单验证器
import { AbstractControl, ValidationErrors } from "@angular/forms"
import { Observable } from "rxjs"
export class NameValidators {
static shouldBeUnique(control: AbstractControl): Promise<ValidationErrors | null> {
return new Promise(resolve => {
if (control.value == "admin") {
resolve({ shouldBeUnique: true })
} else {
resolve(null)
}
})
}
}
contactForm: FormGroup = new FormGroup({
name: new FormControl(
"",
[
Validators.required
],
NameValidators.shouldBeUnique
)
})
<div *ngIf="name.touched && name.invalid && name.errors">
<div *ngIf="name.errors.shouldBeUnique">用户名重复</div>
</div>
<div *ngIf="name.pending">正在检测姓名是否重复</div>
7. FormBuilder
创建表单的快捷方式。
- this.fb.control :表单项
- this.fb.group :表单组,表单至少是一个 FormGroup
- this.fb.array :用于复杂表单,可以动态添加表单项或表单组,在表单验证时,FormArray 中有一项没通过,整体没通过。
import { FormBuilder, FormGroup, Validators } from "@angular/forms"
export class AppComponent {
contactForm: FormGroup
constructor(private fb: FormBuilder) {
this.contactForm = this.fb.group({
fullName: this.fb.group({
firstName: ["😝", [Validators.required]],
lastName: [""]
}),
phone: []
})
}
}
8. 练习
- 获取一组复选框中选中的值
<form [formGroup]="form" (submit)="onSubmit()">
<label *ngFor="let item of Data">
<input type="checkbox" [value]="item.value" (change)="onChange($event)"
/>
{{ item.name }}
</label>
<button>提交</button>
</form>
import { Component } from "@angular/core"
import { FormArray, FormBuilder, FormGroup } from "@angular/forms"
interface Data {
name: string
value: string
}
@Component({
selector: "app-checkbox",
templateUrl: "./checkbox.component.html",
styles: []
})
export class CheckboxComponent {
Data: Array<Data> = [
{ name: "Pear", value: "pear" },
{ name: "Plum", value: "plum" },
{ name: "Kiwi", value: "kiwi" },
{ name: "Apple", value: "apple" },
{ name: "Lime", value: "lime" }
]
form: FormGroup
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
checkArray: this.fb.array([])
})
}
onChange(event: Event) {
const target = event.target as HTMLInputElement
const checked = target.checked
const value = target.value
const checkArray = this.form.get("checkArray") as FormArray
if (checked) {
checkArray.push(this.fb.control(value))
} else {
const index = checkArray.controls.findIndex(
control => control.value === value
)
checkArray.removeAt(index)
}
}
onSubmit() {
console.log(this.form.value)
}
}
- 获取单选框中选中的值
export class AppComponent {
form: FormGroup
constructor(public fb: FormBuilder) {
this.form = this.fb.group({ gender: "" })
}
onSubmit() {
console.log(this.form.value)
}
}
<form [formGroup]="form" (submit)="onSubmit()">
<input type="radio" value="male" formControlName="gender" /> Male
<input type="radio" value="female" formControlName="gender" /> Female
<button type="submit">Submit</button>
</form>
9. 其他
- patchValue:设置表单控件的值(可以设置全部,也可以设置其中某一个,其他不受影响)
- setValue:设置表单控件的值 (设置全部,不能排除任何一个)
- valueChanges:当表单控件的值发生变化时被触发的事件
- reset:表单内容置空
12. 路由
1. 概述
在 Angular 中,路由是以模块为单位的,每个模块都可以有自己的路由。
2. 快速上手
1. 创建页面组件、Layout 组件以及 Navigation 组件,供路由使用
- 创建首页页面组件 ng g c pages/home
- 创建关于我们页面组件 ng g c pages/about
- 创建布局组件 ng g c pages/layout
- 创建导航组件 ng g c pages/navigation
2. 创建路由规则
// app.module.ts
import { Routes } from "@angular/router"
const routes: Routes = [
{
path: "home",
component: HomeComponent
},
{
path: "about",
component: AboutComponent
}
]
3. 引入路由模块并启动
// app.module.ts
import { RouterModule, Routes } from "@angular/router"
@NgModule({
imports: [RouterModule.forRoot(routes, { useHash: true })],
})
export class AppModule {}
4. 添加路由插座
<!-- 路由插座即占位组件 匹配到的路由组件将会显示在这个地方 -->
<router-outlet></router-outlet>
5. 在导航组件中定义链接
<a routerLink="/home">首页</a>
<a routerLink="/about">关于我们</a>
3. 匹配规则
const routes: Routes = [
{
path: "home",
component: HomeComponent
},
{
path: "about",
component: AboutComponent
},
{
path: "",
redirectTo: "home", // 重定向
pathMatch: "full" // 完全匹配
},
{
path: "**",
component: NotFoundComponent // 404 页面
}
]
4. 路由传参
1. 查询参数
import { ActivatedRoute } from "@angular/router"
export class AboutComponent implements OnInit {
constructor(private route: ActivatedRoute) {}
ngOnInit(): void {
this.route.queryParamMap.subscribe(query => {
query.get("name")
})
}
}
2. 动态参数
const routes: Routes = [
{
path: "home",
component: HomeComponent
},
{
path: "about/:name",
component: AboutComponent
}
]
import { ActivatedRoute } from "@angular/router"
export class AboutComponent implements OnInit {
constructor(private route: ActivatedRoute) {}
ngOnInit(): void {
this.route.paramMap.subscribe(params => {
params.get("name")
})
}
}
5. 路由嵌套
路由嵌套指的是如何定义子级路由。
const routes: Routes = [
{
path: "about",
component: AboutComponent,
children: [
{
path: "introduce",
component: IntroduceComponent
},
{
path: "history",
component: HistoryComponent
}
]
}
]
<!-- about.component.html -->
<app-layout>
<p>about works!</p>
<a routerLink="/about/introduce">公司简介</a>
<a routerLink="/about/history">发展历史</a>
<div>
<router-outlet></router-outlet>
</div>
</app-layout>
6. 命名插座
将子级路由组件显示到不同的路由插座中。
{
path: "about",
component: AboutComponent,
children: [
{
path: "introduce",
component: IntroduceComponent,
12.7 导航路由
outlet: "left"
},
{
path: "history",
component: HistoryComponent,
outlet: "right"
}
]
}
<!-- about.component.html -->
<app-layout>
<p>about works!</p>
<router-outlet name="left"></router-outlet>
<router-outlet name="right"></router-outlet>
</app-layout>
<a
[routerLink]="[
'/about',
{
outlets: {
left: ['introduce'],
right: ['history']
}
}
]"
>关于我们
</a>
7. 导航路由
<!-- app.component.html -->
<button (click)="jump()">跳转到发展历史</button>
// app.component.ts
import { Router } from "@angular/router"
export class HomeComponent {
constructor(private router: Router) {}
jump() {
this.router.navigate(["/about/history"], {
queryParams: {
name: "Kitty"
}
})
}
}
8. 路由模块
将根模块中的路由配置抽象成一个单独的路由模块,称之为根路由模块,然后在根模块中引入根路由模块。
import { NgModule } from "@angular/core"
import { HomeComponent } from "./pages/home/home.component"
import { NotFoundComponent } from "./pages/not-found/not-found.component"
const routes: Routes = [
{
path: "",
component: HomeComponent
},
{
path: "**",
component: NotFoundComponent
}
]
@NgModule({
declarations: [],
imports: [RouterModule.forRoot(routes, { useHash: true })],
// 导出 Angular 路由功能模块,因为在根模块的根组件中使用了 RouterModule 模块中提供的路由插座组件
exports: [RouterModule]
})
export class AppRoutingModule {}
import { BrowserModule } from "@angular/platform-browser"
import { NgModule } from "@angular/core"
import { AppComponent } from "./app.component"
import { AppRoutingModule } from "./app-routing.module"
import { HomeComponent } from "./pages/home/home.component"
import { NotFoundComponent } from "./pages/not-found/not-found.component"
@NgModule({
declarations: [AppComponent,HomeComponent, NotFoundComponent],
imports: [BrowserModule, AppRoutingModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
9. 路由懒加载
路由懒加载是以模块为单位的。
- 创建用户模块 ng g m user --routing=true 一并创建该模块的路由模块
- 创建登录页面组件 ng g c user/pages/login
- 创建注册页面组件 ng g c user/pages/register
- 配置用户模块的路由规则
import { NgModule } from "@angular/core"
import { Routes, RouterModule } from "@angular/router"
import { LoginComponent } from "./pages/login/login.component"
import { RegisterComponent } from "./pages/register/register.component"
const routes: Routes = [
{
path: "login",
component: LoginComponent
},
{
path: "register",
component: RegisterComponent
}
]
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class UserRoutingModule {}
- 将用户路由模块关联到主路由模块
// app-routing.module.ts
const routes: Routes = [
{
path: "user",
loadChildren: () => import("./user/user.module").then(m => m.UserModule)
}
]
- 在导航组件中添加访问链接
<a routerLink="/user/login">登录</a>
<a routerLink="/user/register">注册</a>
10. 路由守卫
1. CanActivate
检查用户是否可以访问某一个路由。
创建路由守卫: ng g guard guards/auth
import { Injectable } from "@angular/core"
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from "@angular/router"
import { Observable } from "rxjs"
@Injectable({
providedIn: "root"
})
export class AuthGuard implements CanActivate {
constructor(private router: Router) {}
canActivate(): boolean | UrlTree {
// 用于实现跳转
return this.router.createUrlTree(["/user/login"])
// 禁止访问目标路由
return false
// 允许访问目标路由
return true
}
}
{
path: "about",
component: AboutComponent,
canActivate: [AuthGuard]
}
2. CanActivateChild
检查用户是否方可访问某个子路由。
创建路由守卫: ng g guard guards/admin
import { Injectable } from "@angular/core"
import { CanActivateChild, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from"@angular/router"
import { Observable } from "rxjs"
@Injectable({
providedIn: "root"
})
export class AdminGuard implements CanActivateChild {
canActivateChild(): boolean | UrlTree {
return true
}
}
{
path: "about",
component: AboutComponent,
canActivateChild: [AdminGuard],
children: [
{
path: "introduce",
component: IntroduceComponent
}
]
}
3. CanDeactivate
检查用户是否可以退出路由。比如用户在表单中输入的内容没有保存,用户又要离开路由,此时可以调
用该守卫提示用户。
import { Injectable } from "@angular/core"
import {
CanDeactivate,
ActivatedRouteSnapshot,
RouterStateSnapshot,
UrlTree
} from "@angular/router"
import { Observable } from "rxjs"
export interface CanComponentLeave {
canLeave: () => boolean
}
@Injectable({
providedIn: "root"
})
export class UnsaveGuard implements CanDeactivate<CanComponentLeave> {
canDeactivate(component: CanComponentLeave): boolean {
if (component.canLeave()) {
return true
}
return false
}
}
{
path: "",
component: HomeComponent,
canDeactivate: [UnsaveGuard]
}
import { CanComponentLeave } from "src/app/guards/unsave.guard"
export class HomeComponent implements CanComponentLeave {
myForm: FormGroup = new FormGroup({
username: new FormControl()
})
canLeave(): boolean {
if (this.myForm.dirty) {
if (window.confirm("有数据未保存, 确定要离开吗")) {
return true
} else {
return false
}
}
return true
}
}
4. Resolve
允许在进入路由之前先获取数据,待数据获取完成之后再进入路由。
ng g resolver
import { Injectable } from "@angular/core"
import { Resolve } from "@angular/router"
type returnType = Promise<{ name: string }>
@Injectable({
providedIn: "root"
})
export class ResolveGuard implements Resolve<returnType> {
resolve(): returnType {
return new Promise(function (resolve) {
setTimeout(() => {
resolve({ name: "张三" })
}, 2000)
})
}
}
{
path: "",
component: HomeComponent,
resolve: {
user: ResolveGuard
}
}
export class HomeComponent {
constructor(private route: ActivatedRoute) {}
ngOnInit(): void {
console.log(this.route.snapshot.data.user)
}
}