ng11 开发记录
1.nzViewContainerRef 什么作用?
NzModalRef
实例上的 setTempValue
事件NzModalRef
实例的 componentInstance
属性中的 maxValue
属性值设置为 this.maxValueCopy
的值。子组件的值就会更新nzViewContainerRef
是 ng-zorro-antd 库中 ModalService
的一个选项参数,用于指定模态框内容要插入的视图容器。它的作用是将模态框的内容组件插入到指定的视图容器中,以便在模态框中显示该组件的内容。
通常情况下,nzViewContainerRef
会被设置为组件类中的 ViewContainerRef
对象,以确保模态框的内容组件被正确地插入到组件的视图容器中。这个参数是可选的,如果不指定,则默认为 body
,即将模态框的内容插入到页面的 body 元素中。
2.求数组中 t_data 转为number类型时的最大值
const arr = [
{maxT:1,minT:2,t_data:""},
{maxT:1,minT:2,t_data:" "},
{maxT:1,minT:2,t_data:"123"},
{maxT:1,minT:2,t_data:"13"},
{maxT:1,minT:2,t_data:undefined},
{maxT:1,minT:2,t_data:null},
{maxT:1,minT:2,t_data:222},
{maxT:1,minT:2,t_data:0},
];
const maxTData = arr.reduce((max, obj) => {
const curr = Number(obj.t_data);
if (!isNaN(curr)) {
// 如果t_data可以转换为数字,进行比较
max = Math.max(max, curr);
}
return max;
}, -Infinity);
console.log(maxTData); // 输出 222
3.修改nz-modal弹窗样式
好的,我会给出两个更详细的 Angular 表单示例,一个是响应式表单的示例,另一个是模板驱动表单的示例。
1. Angular 响应式表单示例
```typescript
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-reactive-form',
template: `
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
<label>
Name:
<input type="text" formControlName="name">
<div *ngIf="profileForm.controls.name.invalid && profileForm.controls.name.touched">
Name is required.
</div>
</label>
<label>
Email:
<input type="email" formControlName="email">
<div *ngIf="profileForm.controls.email.invalid && profileForm.controls.email.touched">
Enter a valid email.
</div>
</label>
<button type="submit" [disabled]="!profileForm.valid">Submit</button>
</form>
`
})
export class ReactiveFormComponent implements OnInit {
profileForm: FormGroup;
constructor(private fb: FormBuilder) { }
ngOnInit() {
this.profileForm = this.fb.group({
name: ['', Validators.required],
email: ['', [Validators.required, Validators.email]],
});
}
onSubmit() {
// Handle submit logic.
console.log(this.profileForm.value);
}
}
```
上述示例中,我们使用模板中的 `formGroup` 指令绑定表单控制器,使用 `formControlName` 绑定每个表单控件到相应的表单控制器中。
我们还为每个控件添加了一些基本验证(例如必填和电子邮件格式),并利用 `*ngIf` 指令来显示错误信息。
2. Angular 模板驱动表单示例
```html
<form #profileForm="ngForm" (ngSubmit)="onSubmit(profileForm.value)">
<label>
Name:
<input type="text" name="name" [(ngModel)]="name" required>
<div *ngIf="profileForm.controls['name'].invalid && (profileForm.controls['name'].dirty || profileForm.submitted)">
Name is required.
</div>
</label>
<label>
Email:
<input type="email" name="email" [(ngModel)]="email" required email>
<div *ngIf="profileForm.controls['email'].invalid && (profileForm.controls['email'].dirty || profileForm.submitted)">
Enter a valid email.
</div>
</label>
<button type="submit" [disabled]="!profileForm.valid">Submit</button>
</form>
```
上述示例中,我们使用 `ngForm` 指令绑定表单控制器,使用 `ngModel` 绑定每个表单控件到组件的相应属性中。我们还为每个控件添加了一些基本验证(例如必填和电子邮件格式),
并利用 `*ngIf` 指令来显示错误信息。
我们在模板中也添加了一些逻辑,例如利用 `#profileForm` 变量来引用 `ngForm` 指令创建的表单控制器,以及在表单提交时调用自定义 `onSubmit()` 方法。
import { Component, Input, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
@Component({
selector: 'app-example',
template: `
<h2>{{title}}</h2>
<p>{{text}}</p>
<button (click)="updateTitle()">Update Title</button>
`,
changeDetection: ChangeDetectionStrategy.OnPush // 将变化检测策略设置为OnPush
})
export class ExampleComponent {
@Input() title: string;
@Input() text: string;
constructor(private cdr: ChangeDetectorRef) {} // 注入 ChangeDetectorRef
updateTitle() {
this.title = 'New Title';
this.cdr.detectChanges(); // 手动触发变化检测
}
}
案例二 单独组件 手动触发 接口请求时:
import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
import { UserService } from '../user.service';
@Component({
selector: 'app-list',
template: `
<div *ngFor="let user of users">{{ user.name }} - {{ user.email }}</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ListComponent {
users: any[] = [];
constructor(private userService: UserService, private cd: ChangeDetectorRef) {}
ngOnInit(): void {
this.userService.getUsers().subscribe(users => {
this.users = users;
// 手动触发变更检测
this.cd.detectChanges();
});
}
}
### 注意事项
1. 当使用 `OnPush` 策略时,你应该避免在组件内部直接修改输入属性,而是通过父组件进行修改。这是因为 `OnPush` 只在输入属性发生变化时才会触发变更检测。
2. 如果你需要在组件内部更新视图,请使用 `ChangeDetectorRef` 的 `markForCheck()` 方法或者使用detectChanges()。这将告诉 Angular 下一次变更检测周期应该检查该组件及其子组件。
3. 在使用 `OnPush` 策略的组件中,`ngDoCheck()` 和 `ngAfterViewChecked()` 生命周期钩子将更少地被触发,因为变更检测的频率减少了。
### 代码示例
以下示例展示了如何使用 `OnPush` 策略:
**parent.component.html**
```html
<app-child [data]="data" (update)="onUpdate($event)"></app-child>
<button (click)="changeData()">Change Data</button>
```
**parent.component.ts**
```typescript
import { Component } from '@angular/core';
@Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.css']
})
export class ParentComponent {
data = {
value: 'Initial data'
};
changeData() {
this.data = { ...this.data, value: 'Updated data' };
}
onUpdate(newData) {
this.data = newData;
}
}
```
**child.component.html**
```html
<p>{{ data.value }}</p>
<button (click)="updateData()">Update Data</button>
```
**child.component.ts**
```typescript
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent {
@Input() data;
@Output() update = new EventEmitter();
updateData() {
const newData = { ...this.data, value: 'Updated data from child' };
this.update.emit(newData);
}
}
```
在这个示例中,`ChildComponent` 使用了 `OnPush` 策略。当父组件中的按钮被点击时,输入属性 `data` 的值会发生变化,从而触发子组件的变更检测。
子组件内部的更新操作通过 `EventEmitter` 传递给父组件,父组件负责实际更新数据。这样保持了 OnPush 策略的性能优化。
7.Subscription
Subscription是RxJS中的一种对象类型,用于管理Observable流数据的订阅和取消订阅。
在Angular应用中,我们通常使用Subscription来订阅组件或服务中的Observable对象。通常情况下,我们应该将Subscription对象保存在组件或服务的属性中,以便在组件或服务销毁时及时取消订阅,避免内存泄漏。 可以通过调用Subscription对象的unsubscribe()方法来取消订阅,也可以使用RxJS提供的诸如takeUntil等操作符来自动取消订阅。
以下是一个Angular组件中订阅Observable并管理Subscription的示例代码:
```typescript
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Observable, Subscription } from 'rxjs';
import { DataService } from './data.service';
@Component({
selector: 'app-example',
template: `
<h1>{{ data }}</h1>
`,
})
export class ExampleComponent implements OnInit, OnDestroy {
data: string;
data$: Observable<string>;
subscription: Subscription;
constructor(private dataService: DataService) {}
ngOnInit() {
this.data$ = this.dataService.getData();
this.subscription = this.data$.subscribe(
(data) => {
this.data = data;
},
(error) => {
console.error(error);
}
);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
```
在上面的代码中,我们首先在组件中声明了一个名为data的属性和data$的Observable属性以用于在模板中显示数据。
在ngOnInit生命周期钩子中,我们通过调用getData()方法订阅了一个数据流,并将数据流中的数据保存在组件中声明的data属性中。
在ngOnDestroy生命周期钩子中,我们在组件销毁时通过调用subscription对象的unsubscribe()方法来取消订阅该数据流,以便避免内存泄漏。
this.platform.isBrowser是指当前应用是否在浏览器中运行,它是Angular中一个与平台相关的服务PlatformLocation中的属性。
在Angular应用中,由于我们通常会将应用部署在不同的平台和环境中,因此我们需要知道应用当前是在哪个平台上运行。Angular提供了PlatformLocation服务来获取该信息。
在浏览器中运行的Angular应用使用BrowserPlatformLcoation对象,而在Web Worker中运行的应用使用WebWorkerPlatformLocation对象。这两个对象都继承了PlatformLocation基类,因此都具有isBrowser属性。
isBrowser属性是一个布尔类型的值,用于判断当前应用是否在浏览器中运行。如果值为true,表示应用在浏览器中运行;如果值为false,则表示应用不在浏览器中运行。在当前代码中,如果判断isBrowser为false,则直接返回,不再执行后面的逻辑。
Angular 使用 `Renderer2` 是为了提供一种抽象层,使您可以在不同的平台(如浏览器、服务器端渲染、Web Workers 和原生移动应用)之间跨平台地操作DOM元素。
`Renderer2` 解决了直接使用 `ElementRef` 和原生 DOM API 时可能出现的跨平台兼容性问题。
使用 `Renderer2` 可以保持您的组件代码更加干净、可维护,同时避免与特定平台相关的问题。
下面是一个使用 `Renderer2` 的简单示例:
```typescript
import { Component, ElementRef, Renderer2 } from '@angular/core';
@Component({
selector: 'app-example',
template: `
<button (click)="changeBackgroundColor('red')">Change background color to red</button>
<div #content>Some content here...</div>
`,
})
export class ExampleComponent {
constructor(private el: ElementRef, private renderer: Renderer2) {}
changeBackgroundColor(color: string): void {
const contentDiv = this.el.nativeElement.querySelector('div');
this.renderer.setStyle(contentDiv, 'background-color', color);
}
}
```
在这个示例中,我们创建了一个组件,其中有一个按钮和一个包含文本内容的 `div`。当用户点击按钮时,`div` 的背景颜色会发生变化。
我们使用 `Renderer2` 的 `setStyle` 方法来实现这个功能,而不是直接操作 DOM 元素。这样,无论我们在哪个平台上使用这个组件,它都能正常工作。
11.
1 2 3 4 5 6 7 | import { AfterViewInit, Component, OnDestroy, OnInit, ViewChild } from '@angular/core' ; import { Subject } from 'rxjs' ; import { takeUntil } from 'rxjs/operators' ; import { NzTableComponent } from 'ng-zorro-antd/table' ; private destroy$ = new Subject(); this .nzTableComponent?.cdkVirtualScrollViewport?.scrolledIndexChange .pipe(takeUntil( this .destroy$)) .subscribe((data: number) => { console.log( 'scroll index to' , data); }); 这段代码监听了 `(cdkVirtualScrollViewport)` 对象的 `scrolledIndexChange` 事件,当用户滚动列表,且当前选择的行发生变化时,触发一个回调函数,该回调函数将参数 `data` 设置为当前被选择的行的索引值,然后在控制台中输出一条日志信息。 `takeUntil( this .destroy$)` 用来确保在组件被销毁前取消订阅,以避免内存泄漏。此判断表达式中的 `?.` 表示如果该变量存在,才会访问该变量的属性或方法,否则跳过该语句不执行。 |
接上
ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); } 这段代码是一个 Angular 组件的生命周期钩子函数 `ngOnDestroy()`。它在组件被销毁时被调用,用于清理组件中的变量、取消未完成的 HTTP 请求或 RxJS 订阅等。
在这个例子中,它通过调用 `next()` 方法通知订阅它的可观察对象停止发送事件,并通过调用 `complete()` 方法标记这个可观察对象的结束。这样做可以确保在组件销毁时,
所有的订阅和异步操作都被正确地清理,以避免内存泄漏等问题。
12.angular TemplateRef<{ $implicit: T, index: number }> 作用和使用
`TemplateRef<{ $implicit: T, index: number }> ` 是 Angular 中的一种模板引用类型,用于在组件中将一个模板作为参数传递到另一个组件或指令中,并在那里进行使用。
其中,`T` 是模板中的数据类型,`$implicit` 和 `index` 是对象属性名称,分别表示数据和索引。在模板中,可以通过前缀 `let-` 来访问 `$implicit` 和 `index` 属性,并将它们绑定到当前模板的变量中。
使用 `TemplateRef<{ $implicit: T, index: number }>` 时,我们可以通过以下步骤进行定义和使用:
1. 在模板中,定义一个模板引用变量,并将其类型设置为 `TemplateRef<{ $implicit: T, index: number }> `。例如:
```html
<ng-template #myTemplate let-data>
<div>{{data.index}} : {{data.name}}</div>
</ng-template>
```
在上面的例子中,我们定义了一个模板引用变量 `myTemplate`,并将其类型设置为 `TemplateRef<{ $implicit: T, index: number }> `。
模板中的 `let-data` 表示将模板输入变量 `$implicit` 绑定到名为 `data` 的变量上,其中 `$implicit` 表示模板中的数据,`index` 表示当前渲染的对象在数据数组中的索引值。
2. 在需要使用该模板的组件或指令中,使用 `@ViewChild` 装饰器获取到该模板引用变量,并将其类型设置为 `TemplateRef<{ $implicit: T, index: number }> `。
例如:
```typescript
@ViewChild('myTemplate', { static: true }) myTemplate!: TemplateRef<{ $implicit: VirtualDataInterface, index: number }>;
```
在上面的例子中,我们通过 `@ViewChild` 装饰器获取了名为 `myTemplate` 的模板引用变量,并将其类型设置为 `TemplateRef<{ $implicit: VirtualDataInterface, index: number }> `,
其中 `VirtualDataInterface` 表示模板中绑定的数据类型。
3. 在使用该模板的组件中,使用 `ngTemplateOutlet` 指令将该模板注入到指定的 HTML 元素中。例如:
```html
<div *ngFor="let data of datas; let i=index" [ngTemplateOutlet]="myTemplate" [ngTemplateOutletContext]="{ $implicit: data, index: i }"></div>
```
在上面的例子中,我们使用 `*ngFor` 指令遍历组件中的 `datas` 数组,将每个数组元素渲染成一个 HTML 元素,然后使用 `ngTemplateOutlet` 指令将模板注入到该 HTML 元素中,
并使用 `ngTemplateOutletContext` 属性将数据和索引传递到模板中。这样,模板就可以使用 `data` 绑定模板输入变量 `$implicit`,使用 `i` 绑定模板输入变量 `index`,并展示出渲染后的结果。
使用案例:
在 `nz-table` 中,可以使用 `ng-template` 来自定义每行的渲染方式。在这个 `ng-template` 中,可以使用 `TemplateRef<{ $implicit: T, index: number }>` 模板引用变量来传递当前行数据和索引。 例如: ```html <nz-table #myTable [nzData]="data"> <thead> <tr> <th>Name</th> <th>Age</th> </tr> </thead> <tbody> <ng-container *ngFor="let item of myTable.data; let i=index"> <tr> <td> <ng-container *ngTemplateOutlet="nameTemplate; context: { $implicit: item, index: i }"></ng-container> </td> <td>{{ item.age }}</td> </tr> </ng-container> </tbody> </nz-table> <!-- 定义一个名字的模板 --> <ng-template #nameTemplate let-data let-i="index"> {{ i + 1 }} - {{ data.name }} </ng-template> ``` 在上面的代码中,我们使用了 `ng-container` 来遍历 `nz-table` 的数据,并使用 `TemplateRef<{ $implicit: T, index: number }>` 模板引用变量来传递当前行的数据和索引到 `nameTemplate` 模板中。
这样,在 `nameTemplate` 模板中,我们就可以使用 `data` 和 `i` 来访问当前行的数据和索引了。
13.viewProviders
在Angular中,`@Component`装饰器用于定义组件。其中,`viewProviders`是一个可选的属性,用于在组件的模板中注册需要的依赖。
具体来说,如果我们需要在组件的模板中使用某个服务,就可以将该服务的提供者添加到`viewProviders`中。这样,该服务就会被注入到组件的视图(即模板)中,并可以在模板中使用。
下面是一个示例,展示了如何在`viewProviders`中注册一个服务:
```typescript
import { Component } from '@angular/core';
import { Greeter } from './greeter.service';
@Component({
selector: 'app-greeting',
template: '<h1>{{ greeting }}</h1>',
viewProviders: [Greeter],
})
export class GreetingComponent {
constructor(private greeter: Greeter) {}
get greeting(): string {
return this.greeter.greet('world');
}
}
```
在这个示例中,我们定义了一个组件`GreetingComponent`,并指定了它的选择器(`selector`)、模板(`template`)以及`viewProviders`属性,将`Greeter`服务的提供者添加到组件的模板中。
在组件的构造函数中,我们通过将`Greeter`标记为私有成员变量,将该服务注入到组件中,并在`get greeting()`方法中使用它来生成欢迎语句。
最终,当我们在应用中使用`<app-greeting></app-greeting>`标签时,该组件会渲染出一个包含欢迎语句的`<h1>`元素。
总之,`viewProviders`通常情况下,如果一个服务只需要在一个组件的视图中使用,而不需要在整个组件及其子组件中使用,那么我们就可以将该服务的提供者添加到组件的viewProviders
属性中。这样可以使该服务只在该组件及其视图中可用,而不会影响到其他组件。
14.
1. *ngIf和ng-template是用于条件渲染的结构指令和结构指令的模板。
2. 结构指令*ngTemplateOutlet
和ng-template
来实现模板重用和条件渲染。
`*ngIf`和`ng-template`通常与`div`标签一起使用,以实现条件渲染的目的。具体来说,在模板中可以使用以下语法:
```html
<div *ngIf="condition; else elseBlock">...</div>
<ng-template #elseBlock>...</ng-template>
```
在这段代码中,当`condition`为`true`时,`div`标签和其中的内容就会被渲染出来。否则,模板则会根据`#elseBlock`来进行渲染。
类似地,`ng-container`和`*ngTemplateOutlet`和`ng-template`可以一起使用,以实现模板的重用。具体来说,在模板中可以使用以下语法:
```html
<ng-container *ngTemplateOutlet="templateRef"></ng-container>
<ng-template #templateRef>...</ng-template>
```
或者
<ng-container *ngIf="isLoading; else loaded">
<ng-container *ngTemplateOutlet="loading"></ng-container>
</ng-container>
<ng-template #loading>
<div class="loading-spinner">Loading...</div>
</ng-template>
<ng-template #loaded>
<!-- loaded显示内容 -->
</ng-template>
在这段代码中,`ng-container`不会影响样式和布局,充当一个临时容器的角色,它会从`ng-template`中引用内容,并将其插入到DOM中。`ng-template`则定义了一个可复用的模板内容,当需要显示这个模板时,
可以通过`*ngTemplateOutlet`指令来引用它并将其渲染到`ng-container`中。
总之,`*ngIf`和`ng-template`结合`div`等常用标签使用,用于条件渲染;`ng-container`和`*ngTemplateOutlet`和`ng-template`结合使用,用于模板的重用。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体