angular html原理,Angular 4.x ngModel 双向绑定原理揭秘

2023-11-17

在 Angular 4.x 中对于使用 Template-Driven 表单场景,如果需要实现表单数据绑定。我们就需要引入 ngModel 指令。该指令用于基于 domain 模型,创建 FormControl 实例,并将创建的实例绑定到表单控件元素上。

ngModel 使用示例

ngModel

app.component.ts

@Component({

selector: 'exe-app',

template: `

Name:

{{ f.value | json }}

`,

})

export class AppComponent implements OnInit { }

表单中使用 ngModel 时,我们需要设置一个 name 属性,以便该控件可以使用该名称在表单中进行注册。

单向绑定 - [ngModel]

app.component.ts

@Component({

selector: 'exe-app',

template: `

Name:

{{ user | json }}

`,

})

export class AppComponent implements OnInit {

user: { username: string };

ngOnInit() {

this.user = { username: 'Semlinker' };

}

}

双向绑定 - [(ngModel)]

表单中应用

app.component.ts

@Component({

selector: 'exe-app',

template: `

Name:

{{ user | json }}

`,

})

export class AppComponent implements OnInit {

user: { username: string };

ngOnInit() {

this.user = { username: 'Semlinker' };

}

}

单独应用

import { Component } from '@angular/core';

@Component({

selector: 'exe-app',

template: `

{{username}}

`,

})

export class AppComponent {

username: string;

}

ngModelOptions - [ngModelOptions]

当你在使用 ngModel 时未设置 name 属性,如下所示:

Name:

当你运行时,浏览器控制台将会抛出以下异常信息:

Error: If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions.

以上异常信息告诉我们,如果在表单标签中使用 ngModel,则必须设置 name 属性,或者在 ngModelOptions 中必须将表单控件定义为 "standalone"。依据上述异常信息,我们做如下调整:

Name:

[ngModelOptions]="{standalone: true}">

接下来我们看一下 ngModelOptions 支持的对象类型:

@Input('ngModelOptions') options: {name?: string, standalone?: boolean};

禁用控件 - disabled

Name:

[(ngModel)]="user.username" disabled="true">

监听 ngModelChange 事件 - (ngModelChange)

app.component.ts

@Component({

selector: 'exe-app',

template: `

Name:

[(ngModel)]="user.username">

{{ user | json }}

`,

})

export class AppComponent implements OnInit {

user: { username: string };

ngOnInit() {

this.user = { username: 'Semlinker' };

}

userNameChange(name: string) {

console.log(name);

}

}

获取关联的 NgModel 对象

app.component.ts

@Component({

selector: 'exe-app',

template: `

Name:

[(ngModel)]="user.username">

{{ userName.control | json }}

`,

})

export class AppComponent implements OnInit {

user: { username: string };

ngOnInit() {

this.user = { username: 'Semlinker' };

}

}

通过使用 userName="ngModel" 方式,我们可以获取表单控件关联的 NgModel 对象,进而获取控件当前控件的相关信息,如控件的当前的状态或控件验证信息等。

完整示例

import { Component } from '@angular/core';

import { NgForm } from '@angular/forms';

@Component({

selector: 'exe-app',

template: `

Submit

First name value: {{ first.value }}

First name valid: {{ first.valid }}

Form value: {{ f.value | json }}

Form valid: {{ f.valid }}

`,

})

export class AppComponent {

onSubmit(f: NgForm) {

console.log(f.value); // { first: '', last: '' }

console.log(f.valid); // false

}

}

ngModel 指令详解

ngModel 指令定义

@Directive({

selector: '[ngModel]:not([formControlName]):not([formControl])',

providers: [formControlBinding],

exportAs: 'ngModel'

})

formControlBinding 定义

export const formControlBinding: any = {

provide: NgControl,

useExisting: forwardRef(() => NgModel)

};

相关说明

selector 中 [ngModel]:not([formControlName]):not([formControl]) 表示该指令只应用于 Template-Driven 表单中。

exportAs - 表示可以使用 first="ngModel" 语法获取 NgModel 对象

ngModel 指令输入与输出属性

输入属性

@Input() name: string;

@Input('disabled') isDisabled: boolean;

@Input('ngModel') model: any;

@Input('ngModelOptions') options: {name?: string, standalone?: boolean};

输出属性

@Output('ngModelChange') update = new EventEmitter();

NgModel 类

// angular2/packages/forms/src/directives/ng_model.ts

export class NgModel extends NgControl implements OnChanges,

OnDestroy {

/** @internal */

_control = new FormControl(); // 创建FormControl对象

/** @internal */

_registered = false; // 用于标识控件是否已注册

viewModel: any; // 用于保存前一次model的值

...

}

NgModel 构造函数

constructor(

@Optional() @Host() parent: ControlContainer,

@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array,

@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators:

Array,

@Optional() @Self() @Inject(NG_VALUE_ACCESSOR)

valueAccessors: ControlValueAccessor[]) {

super();

this._parent = parent;

this._rawValidators = validators || [];

this._rawAsyncValidators = asyncValidators || [];

this.valueAccessor = selectValueAccessor(this, valueAccessors);

}

相关说明

@Optional() - 表示该依赖对象是可选的

@Host() - 表示从宿主元素注入器获取依赖对象

@Self() - 表示从当前注入器获取依赖对象

@Inject() - 用于注入 Token (new InjectionToken) 对应的非 Type 类型依赖对象

构造函数执行的操作:

获取 ControlContainer (控件容器)对象

获取控件上的同步验证器

获取控件上的异步验证器

获取控件上的 ControlValueAccessor

NgModel 生命周期钩子

ngOnChanges

ngOnChanges(changes: SimpleChanges) {

this._checkForErrors();

if (!this._registered) this._setUpControl();

if ('isDisabled' in changes) {

this._updateDisabled(changes);

}

if (isPropertyUpdated(changes, this.viewModel)) {

this._updateValue(this.model);

this.viewModel = this.model;

}

}

_checkForErrors()

private _checkForErrors(): void {

if (!this._isStandalone()) {

this._checkParentType();

}

this._checkName();

}

// 判断是否设置standalone属性

private _isStandalone(): boolean {

return !this._parent || (this.options && this.options.standalone);

}

/**

* 1.ngModel指令不能与formGroupName或formArrayName指令一起使用,需改用

* formControlName或调整ngModel的父控件使用的指令为ngModelGroup。

*

* 2.ngModel不能被注册到使用formGroup指令的表单中,需改用formControlName或设置

* ngModelOptions对象中的standalone属性,避免注册该控件。

*/

private _checkParentType(): void {

if (!(this._parent instanceof NgModelGroup) &&

this._parent instanceof AbstractFormGroupDirective) {

TemplateDrivenErrors.formGroupNameException();

} else if (!(this._parent instanceof NgModelGroup) &&

!(this._parent instanceof NgForm)) {

TemplateDrivenErrors.modelParentException();

}

}

/**

* 验证是否设置name属性

*

* 如果在表单标签中使用 ngModel,则必须设置 name 属性,或者在ngModelOptions中必须将

* 表单控件定义为"standalone"。

*

*

* true}">

*/

private _checkName(): void {

if (this.options && this.options.name) this.name = this.options.name;

if (!this._isStandalone() && !this.name) {

TemplateDrivenErrors.missingNameException();

}

}

_setUpControl()

// 初始化控件

private _setUpControl(): void {

this._isStandalone() ? this._setUpStandalone() :

// 在ControlContainer所属的form中注册该控件

this.formDirective.addControl(this);

this._registered = true; // 标识已注册

}

// 若设置standalone属性,则初始化该控件,并更新控件的值和验证状态

private _setUpStandalone(): void {

setUpControl(this._control, this);

this._control.updateValueAndValidity({emitEvent: false});

}

// 获取ControlContainer所属的form

get formDirective(): any {

return this._parent ? this._parent.formDirective : null;

}

_updateDisabled()

若设置 isDisabled 输入属性,则更新控件的 disabled 属性:

// 更新控件的disabled状态

private _updateDisabled(changes: SimpleChanges) {

// 获取disabled输入属性的当前值

const disabledValue = changes['isDisabled'].currentValue;

// 判断是否设置为disabled

const isDisabled = disabledValue === '' ||

(disabledValue && disabledValue !== 'false');

resolvedPromise.then(() => {

if (isDisabled && !this.control.disabled) {

this.control.disable(); // 禁用控件

} else if (!isDisabled && this.control.disabled) {

this.control.enable(); // 启用控件

}

});

}

isPropertyUpdated()

// 判断属性是否更新

export function isPropertyUpdated(changes: {[key: string]: any},

viewModel: any): boolean {

if (!changes.hasOwnProperty('model')) return false; // @Input('ngModel') model: any;

const change = changes['model'];

if (change.isFirstChange()) return true; // 判断是否首次改变

return !looseIdentical(viewModel, change.currentValue);

}

// JS has NaN !== NaN

export function looseIdentical(a: any, b: any): boolean {

return a === b || typeof a === 'number' && typeof b === 'number' && isNaN(a)

&& isNaN(b);

}

_updateValue()

// 更新控件的值

private _updateValue(value: any): void {

resolvedPromise.then(

() => { this.control.setValue(value, {emitViewToModelChange: false});

});

}

const resolvedPromise = Promise.resolve(null);

ngOnDestroy()

// 指令销毁时,从formDirective中移除该控件

ngOnDestroy(): void {

this.formDirective && this.formDirective.removeControl(this);

}

NgModel 方法

get control(): FormControl

// 获取控件

get control(): FormControl { return this._control; }

/** @internal */

_control = new FormControl();

get path(): string[]

// 获取控件的访问路径

get path(): string[] {

return this._parent ? controlPath(this.name, this._parent) : [this.name];

}

get validator(): ValidatorFn

// 获取同步验证器

get validator(): ValidatorFn {

return composeValidators(this._rawValidators);

}

export interface ValidatorFn { (c: AbstractControl): ValidationErrors|null; }

get asyncValidator(): AsyncValidatorFn

// 获取异步验证器

get asyncValidator(): AsyncValidatorFn {

return composeAsyncValidators(this._rawAsyncValidators);

}

export interface AsyncValidatorFn {

(c: AbstractControl): Promise|Observable;

}

viewToModelUpdate(newValue: any): void

// 触发ngModelChange事件

viewToModelUpdate(newValue: any): void {

this.viewModel = newValue;

// @Output('ngModelChange') update = new EventEmitter();

this.update.emit(newValue);

}

NgControl 抽象类

// angular2/packages/forms/src/directives/ng_control.ts

// 所有控件指令都需继承的基类,绑定FormControl对象至DOM元素

export abstract class NgControl extends AbstractControlDirective {

/** @internal */

_parent: ControlContainer = null;

name: string = null;

valueAccessor: ControlValueAccessor = null;

/** @internal */

_rawValidators: Array = [];

/** @internal */

_rawAsyncValidators: Array = [];

get validator(): ValidatorFn { return unimplemented(); }

get asyncValidator(): AsyncValidatorFn { return unimplemented(); }

abstract viewToModelUpdate(newValue: any): void;

}

AbstractControlDirective 抽象类

// angular2/packages/forms/src/directives/abstract_control_directive.ts

export abstract class AbstractControlDirective {

// 获取控件

get control(): AbstractControl { throw new Error('unimplemented'); }

// 获取控件的值

get value(): any { return this.control ? this.control.value : null; }

// 控件控件的验证状态 - valid、invalid、pending

get valid(): boolean { return this.control ? this.control.valid : null; }

get invalid(): boolean { return this.control ? this.control.invalid : null; }

get pending(): boolean { return this.control ? this.control.pending : null; }

get pristine(): boolean { return this.control ? this.control.pristine : null; }

get dirty(): boolean { return this.control ? this.control.dirty : null; }

get touched(): boolean { return this.control ? this.control.touched : null; }

get untouched(): boolean { return this.control ? this.control.untouched : null; }

get disabled(): boolean { return this.control ? this.control.disabled : null; }

get enabled(): boolean { return this.control ? this.control.enabled : null; }

// 获取控件验证异常对象

get errors(): ValidationErrors|null {

return this.control ? this.control.errors : null;

}

// 获取statusChanges对象

get statusChanges(): Observable {

return this.control ? this.control.statusChanges : null;

}

// 获取valueChanges对象

get valueChanges(): Observable {

return this.control ? this.control.valueChanges : null;

}

// 获取控件路径

get path(): string[] { return null; }

// 重设控件的值

reset(value: any = undefined): void {

if (this.control) this.control.reset(value);

}

// 判断是否path路径对应的控件,是否存在errorCode对应的错误

hasError(errorCode: string, path: string[] = null): boolean {

return this.control ? this.control.hasError(errorCode, path) : false;

}

// 获取path路径对应的控件,参数errorCode对应的错误

getError(errorCode: string, path: string[] = null): any {

return this.control ? this.control.getError(errorCode, path) : null;

}

}

input 指令

input 指令定义

@Directive({

selector:`

input:not([type=checkbox])[formControlName],textarea[formControlName],

input:not([type=checkbox])[formControl],textarea[formControl],

input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]

`,

host: {

'(input)': '_handleInput($event.target.value)',

'(blur)': 'onTouched()',

'(compositionstart)': '_compositionStart()',

'(compositionend)': '_compositionEnd($event.target.value)'

},

providers: [DEFAULT_VALUE_ACCESSOR]

})

相关说明

compositionstart - 事件触发于一段文字的输入之前 (类似于 keydown 事件,但是该事件仅在若干可见字符的输入之前,而这些可见字符的输入可能需要一连串的键盘操作、语音识别或者点击输入法的备选词)。

compositionend - 事件触发于完成文本段落输入或取消输入

compositionstart、compositionend 的实际应用,请参考 - 应对中文输入法的字符串截断方案

DEFAULT_VALUE_ACCESSOR

export const DEFAULT_VALUE_ACCESSOR: any = {

provide: NG_VALUE_ACCESSOR,

useExisting: forwardRef(() => DefaultValueAccessor),

multi: true

};

DefaultValueAccessor

export class DefaultValueAccessor implements ControlValueAccessor {

onChange = (_: any) => {};

onTouched = () => {};

/** Whether the user is creating a composition string (IME events). */

private _composing = false;

constructor(

private _renderer: Renderer, // 注入Renderer对象

private _elementRef: ElementRef,

@Optional() @Inject(COMPOSITION_BUFFER_MODE)

private _compositionMode: boolean) {

if (this._compositionMode == null) {

this._compositionMode = !_isAndroid();

}

}

// 将模型中的新值写入视图或DOM元素属性中

writeValue(value: any): void {

const normalizedValue = value == null ? '' : value;

this._renderer.setElementProperty(this._elementRef.nativeElement,

'value', normalizedValue);

}

// 设置当控件接收到change事件后,调用的函数

registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }

// 设置当控件接收到touched事件后,调用的函数

registerOnTouched(fn: () => void): void { this.onTouched = fn; }

// 设置控件的Disabled状态

setDisabledState(isDisabled: boolean): void {

this._renderer.setElementProperty(this._elementRef.nativeElement,

'disabled', isDisabled);

}

// 处理input事件

_handleInput(value: any): void {

if (!this._compositionMode || (this._compositionMode && !this._composing)) {

this.onChange(value);

}

}

// 处理compositionstart事件

_compositionStart(): void { this._composing = true; }

// 处理compositionend事件

_compositionEnd(value: any): void {

this._composing = false;

this._compositionMode && this.onChange(value);

}

}

export const COMPOSITION_BUFFER_MODE = new InjectionToken

('CompositionEventMode');

// 用于判断是否处于安卓平台,composition事件在iOS和Android存在兼容性

function _isAndroid(): boolean {

const userAgent = getDOM() ? getDOM().getUserAgent() : '';

return /android (\d+)/.test(userAgent.toLowerCase());

}

相关说明

为了能够支持跨平台,Angular 通过抽象层封装了不同平台的差异,统一了 API 接口。如定义了抽象类 Renderer 、抽象类 RootRenderer 等。此外还定义了以下引用类型:ElementRef、TemplateRef、ViewRef 、ComponentRef 和 ViewContainerRef 等。

另外看完上面的代码,不知道读者有没有以下的疑问:

writeValue() 方法什么时候调用?

registerOnChange() 什么时候调用?

registerOnTouched() 什么时候调用?

为了解开这些疑惑我们就需要分析一下,一个很重要的方法 - setUpControl()。我们先来看一下 setUpControl() 的调用的时机点:

NgModel ngOnChanges 生命周期钩子

ngOnChanges(changes: SimpleChanges) {

...

if (!this._registered) this._setUpControl();

...

}

_setUpControl() 方法

private _setUpControl(): void {

this._isStandalone() ? this._setUpStandalone() :

// 在ControlContainer所属的form中注册该控件

this.formDirective.addControl(this);

this._registered = true; // 标识已注册

}

_setUpControl() 方法内部,先判断控件有设置 standalone 属性,如果有的话,则调用 _setUpStandalone() 方法:

// 若设置standalone属性,则初始化该控件,并更新控件的值和验证状态

private _setUpStandalone(): void {

setUpControl(this._control, this); // 调用时机点一

this._control.updateValueAndValidity({emitEvent: false});

}

如果没有设置 standalone 属性,则调用 this.formDirective.addControl(this),这个方法存在于我们的 form 指令中,我们直接看一下具体实现:

addControl(dir: NgModel): void {

resolvedPromise.then(() => {

const container = this._findContainer(dir.path);

dir._control = container.registerControl(dir.name, dir.control);

setUpControl(dir.control, dir); // 调用时机点二

dir.control.updateValueAndValidity({emitEvent: false});

});

}

搞清楚 setUpControl() 调用的时机点,是时候分析一下 setUpControl() 方法的具体实现了。

setUpControl()

// angular2/packages/forms/src/directives/shared.ts

export function setUpControl(control: FormControl, dir: NgControl): void {

if (!control) _throwError(dir, 'Cannot find control with');

/**

* NgModel构造函数

* @Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]

* this.valueAccessor = selectValueAccessor(this, valueAccessors);

*/

// 判断控件是否实现ControlValueAccessor接口

if (!dir.valueAccessor) _throwError(dir, 'No value accessor for form control with');

// 组合同步验证器

control.validator = Validators.compose([control.validator, dir.validator]);

// 组合异步验证器

control.asyncValidator = Validators.composeAsync([control.asyncValidator,

dir.asyncValidator]);

// 该方法用于将模型中的新值写入视图或 DOM 属性中

dir.valueAccessor.writeValue(control.value);

// view -> model

/**

* @Directive({

* selector: 'input:not([type=checkbox])[formControlName],...',

* host: {

* '(input)': '_handleInput($event.target.value)'

* },

* providers: [DEFAULT_VALUE_ACCESSOR]

* })

* export class DefaultValueAccessor implements ControlValueAccessor {

* // 下面就是调用该方法

* registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }

*

* // input事件触发后,调用该方法

* _handleInput(value: any): void {

* if (!this._compositionMode || (this._compositionMode && !this._composing)) {

* this.onChange(value); //调用下面注册的onChange函数

* }

* }

* }

*

*/

dir.valueAccessor.registerOnChange((newValue: any) => {

/**

* ngModel指令 - viewToModelUpdate() 方法

*

* viewToModelUpdate(newValue: any): void {

* this.viewModel = newValue; // 更新viewModel

* // @Output('ngModelChange') update = new EventEmitter();

* this.update.emit(newValue); // 触发ngModelChange事件

* }

*/

dir.viewToModelUpdate(newValue);

control.markAsDirty();

/*

* setValue(value: any, {onlySelf, emitEvent, emitModelToViewChange,

* emitViewToModelChange}: {

* onlySelf?: boolean,

* emitEvent?: boolean,

* emitModelToViewChange?: boolean,

* emitViewToModelChange?: boolean

* } = {}): void {

* this._value = value;

* if (this._onChange.length && emitModelToViewChange !== false) {

* this._onChange.forEach((changeFn) => changeFn(this._value,

* emitViewToModelChange !== false));

* }

* this.updateValueAndValidity({onlySelf, emitEvent});

* }

*/

control.setValue(newValue, {emitModelToViewChange: false}); // 更新控件的值

});

// touched

dir.valueAccessor.registerOnTouched(() => control.markAsTouched());

/**

* control = new FormControl();

*

* control - _onChange 属性

* _onChange: Function[] = [];

*

* control - registerOnChange() 方法

* registerOnChange(fn: Function): void { this._onChange.push(fn); }

*/

control.registerOnChange((newValue: any, emitModelEvent: boolean) => {

// control -> view

/*

* writeValue(value: any): void {

* const normalizedValue = value == null ? '' : value;

* this._renderer.setElementProperty(this._elementRef.nativeElement, 'value',

* normalizedValue);

* }

*/

dir.valueAccessor.writeValue(newValue);

// control -> ngModel

/**

* ngModel指令 - viewToModelUpdate() 方法

*

* viewToModelUpdate(newValue: any): void {

* this.viewModel = newValue; // 更新viewModel

* // @Output('ngModelChange') update = new EventEmitter();

* this.update.emit(newValue); // 触发ngModelChange事件

* }

*/

if (emitModelEvent) dir.viewToModelUpdate(newValue);

});

// 当控件状态变成 DISABLED 或从 DISABLED 状态变化成 ENABLE 状态时,会调用该函数。该函数会根据参数

// 值,启用或禁用指定的 DOM 元素

if (dir.valueAccessor.setDisabledState) {

control.registerOnDisabledChange(

(isDisabled: boolean) => { dir.valueAccessor.setDisabledState(isDisabled); });

}

// re-run validation when validator binding changes, e.g. minlength=3 -> minlength=4

dir._rawValidators.forEach((validator: Validator | ValidatorFn) => {

if ((validator).registerOnValidatorChange)

(validator).registerOnValidatorChange(() =>

control.updateValueAndValidity());

});

dir._rawAsyncValidators.forEach((validator: AsyncValidator | AsyncValidatorFn) => {

if ((validator).registerOnValidatorChange)

(validator).registerOnValidatorChange(() =>

control.updateValueAndValidity());

});

}

最后我们再看一下 ControlValueAccessor 接口:

ControlValueAccessor

// angular2/packages/forms/src/directives/control_value_accessor.ts

export interface ControlValueAccessor {

writeValue(obj: any): void;

registerOnChange(fn: any): void;

registerOnTouched(fn: any): void;

setDisabledState?(isDisabled: boolean): void;

}

writeValue(obj: any):该方法用于将模型中的新值写入视图或 DOM 属性中

registerOnChange(fn: any):设置当控件接收到 change 事件后,调用的函数

registerOnTouched(fn: any):设置当控件接收到 touched 事件后,调用的函数

setDisabledState?(isDisabled: boolean):当控件状态变成 DISABLED 或从 DISABLED 状态变化成 ENABLE 状态时,会调用该函数。该函数会根据参数值,启用或禁用指定的 DOM 元素

了解 ControlValueAccessor 的详细信息,可以参考 - Understanding ControlValueAccessor

明天补充图示说明哈,能够理解的同学请直接略过。

参考资源

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

angular html原理,Angular 4.x ngModel 双向绑定原理揭秘 的相关文章

  • sql注入之报错注入

    报错注入 报错注入在没法用union联合查询时用 但前提还是不能过滤一些关键的函数 报错注入就是利用了数据库的某些机制 人为地制造错误条件 使得查询结果能够出现在错误信息中 这里主要记录一下xpath语法错误和concat rand gro
  • 某**集团夺旗赛的一道隐写题

    解压压缩包 解压出来一个文件file 使用file命令进行查看 发现是data 010查看也无果 看到标题是logistic联想到是否与xor文件有关 遂使用工具xortool 工具在此 xortool file 选概率最大的那个 13 x
  • HDFS API操作的访问方式及JUnit测试类的使用

    HDFS API操作的访问方式 主要分为使用文件系统访问方式和URL访问方式 package com wyg hdfs import java io File import java io FileOutputStream import j
  • hive 高级分组聚合(grouping sets cube和rollup)

    1 grouping sets 1 1 select a b sum c from tbl group by a b grouping sets a b 相当于 select a b sum c from tbl group by a b
  • Python求1-100所有奇数和的方法!

    在之前的文章中 老男孩IT教育小编为大家介绍过Python的特点 优势 用途以及薪资待遇等知识 而为了帮助大家更好的掌握Python 小编将为大家讲解一些实战案例 比如 Python中如何求1 100的奇数和 接下来我们来看看吧 Pytho
  • Stable Diffusion安装教程、model导入教程以及精品promt指令

    文章目录 引言 原理 图片感知压缩 潜在扩散模型 安装 插件 插件与模型下载 常用promt关键字 交流讨论 引言 最近大火的AI作画吸引了很多人的目光 AI作画近期取得如此巨大进展的原因个人认为有很大的功劳归属于Stable Diffus
  • 读取sftp服务器上的文件内容到指定的数据库表内

    引入sftp jar依赖
  • 一些常用的公共js方法

    读者可能会觉得节流与防抖有点像 其实仔细斟酌就能发现他们的不同 节流是指对于连续触发的事件 每隔一段固定时间执行一次 只要事件持续出发就可以执行很多次 在节流里涉及的时间主要是指事件执行的间隔时间 防抖则是对连续触发的事件 只会执行一次 不
  • 从操作系统层面理解同步、异步、阻塞、非阻塞

    同步和异步描述调用者会不会主动等待函数的返回值 举个例子 public void method int result otherMethod 像上面这种形式就叫同步 result 会一直等待 otherMethod 方法执行完毕并拿到返回值
  • BMVC2022

    原文标题 Hierarchical Residual Learning Based Vector Quantized Variational Autoencoder for Image Reconstruction and Generati
  • 运行 Triton 示例

    安装 Triton Docker 镜像 在拉取镜像前 需要安装 Docker 和 NVIDIA Container Toolkit 用以下命令拉取镜像 docker pull nvcr io nvidia tritonserver
  • VTK编译方法

    VTK编译方法 VTK Group Imaging ON VTK Group MPI ON VTK Group QT ON VTK Group TK ON VTK Group Views ON VTK RENDERING BACKEND O
  • 使用layui/layuiAdmin的总结

    layui是一个前端UI框架 主要是配合JQuery使用 开始使用 首先是下载文件 然后引入css和js文件 引入之后就需要在
  • 以太坊开发入门,完整入门

    翻译自 https medium com mattcondon getting up to speed on ethereum 63ed28821bbe 从入门到精通 干货篇 必读 如果你 是一个专业的程序员 如果你想了解以太坊当前可以做到
  • QT QTabWidget 、布局控件 动态添加窗口(控件)、删除窗口(控件)方案

    new 一个窗口或者控件 QTabWidget addTab 将新建的控件放到一个容器中 比如 QMap
  • mybatis查询

    以后返回统一用对象 resultMap 查询 基本查询 select from person where person id id 条件查询 分页 select from cobra apply store yjs user id yjsU
  • 递归的一种应用

    有些问题 涉及两个对象 比如两个数 像个长度不同的数组 链表之类的 必须考虑是前者大还是后者大的情况 分别处理 其实可以只处理一种情况 比如前者小 后者大的情况 另一种情况 前者大后者小 可以通过交换参数 递归调用本函数来处理
  • ArrayList与顺序表

    文章目录 一 顺序表是什么 二 ArrayList是什么 三 ArrayList的构造方法 四 ArrayList的常见方法 4 1 add 4 2 size 4 3 remove 4 4 get 4 5 set 4 6 contains

随机推荐

  • kali linux中如何安装中文输入法

    前言 在使用kali linux中 我们可能用到中文输入法 那么我们该如何安装中文输入法呢 正文 一 首先 我们需要检查更新源是否可用 如果可用我们就进行第二步 如果不可用 我们则需要手动添加更新源 手动添加更新源 我们需要到网上找到最新的
  • 云原生安全性:构建可信任的云应用的最佳实践

    文章目录 云原生安全性的重要性 1 数据隐私 2 恶意攻击 3 合规性要求 4 业务连续性 构建可信任的云应用的最佳实践 1 安全开发 2 身份验证与授权 3 容器安全性 4 监控与审计 5 持续集成与持续交付 CI CD 6 安全培训和教
  • 制作树莓派img镜像文件

    想做个树莓派的img镜像 然而对SD卡进行全盘复制很浪费空间 且不能恢复到比现有SD卡容量小的卡上 因此探索制作小img的方法 网上看了大神制作的脚本 比如https github com conanwhf RaspberryPi scri
  • MySQL索引原理详解

    目录 一 数据结构 1 1 二叉树 为什么索引的数据结构不用二叉树 1 2 红黑树 自平衡二叉查找树 为什么索引的数据结构不用红黑树 1 3 B树 多路平衡搜索树 为什么索引的数据结构不用B树 1 4 B 树 1 5 MySQL B 树 1
  • QML的Label实现Tooltip提示效果

    在用QML进行界面设计时 往往需要用到Label 但是由于界面宽度的限制 Label会显示不全 需要进行Tooltip进行提示 而QML中的Label本身还不支持Tooltip的提示功能 所以给开发带来了一定的困难 那么 遇到这种问题 该怎
  • SpringBoot中使用ThreadPoolExecutor和ThreadPoolTaskExecutor线程池的方法和区别

    Java中经常用到多线程来处理业务 在多线程的使用中 非常的不建议使用单纯的Thread或者实现Runnable接口的方式来创建线程 因为这样的线程创建及销毁势必会造成耗费资源 线程上下文切换问题 同时创建过多的线程也可能会引发资源耗尽的风
  • 【计算机操作系统】第二章 进程管理

    1 进程的基本概念 1 1 程序的顺序执行和特征 程序顺序执行时的特征 顺序性 处理机的操作严格按照程序所规定的顺序执行 即每一操作必须在上一个操作结束之后开始 封闭性 程序是在封闭的环境下执行的 即程序运行时独占全机资源 资源的状态 除初
  • 大数据课程C5——ZooKeeper的应用组件

    文章作者邮箱 yugongshiye sina cn 地址 广东惠州 本章节目的 掌握Zookeeper的Canal消费组件 掌握Zookeeper的Dubbo分布式服务框架 掌握Zookeeper的Metamorphosis消息中间件 掌
  • 快速性分析 一阶、二阶系统响应

    自控笔记 3 3一阶系统的时间响应及动态性能 一 一阶系统的数学模型 凡是以一阶微分方程作为运动方程的控制系统 称为一阶系统 其数学模型为 时间常数T是表征系统响应的唯一参数 T越小 输出响应上升得越快 调节时间越小 二 一阶系统的典型响应
  • 华为OD机试 - 符合要求的元组的个数(Java & JS & Python)

    题目描述 给定一个整数数组 nums 一个数字k 一个整数目标值 target 请问nums中是否存在k个元素使得其相加结果为target 请输出所有符合条件且不重复的k元组的个数 数据范围 2 nums length 200 10 9 n
  • Java Thread.currentThread()方法具有什么功能呢?

    转自 Java Thread currentThread 方法具有什么功能呢 下文讲述Thread currentThread 方法的功能简介说明 如下所示 Thread currentThread 方法的功能 返回当前线程 注意事项 Th
  • Java实现CSV读写

    在开发过程中经常需要处理csv文件 我一般是实现一个CSVHelper 封装一些对csv文件的基本操作 代码中直接使用封装好的CSVHelper来读写csv文件就可以了 今天就来记录一下如何通过Java实现封装的csv文件的读写 对于C 实
  • 弱网测试

    来自 https www cnblogs com linxiu 0925 p 9412190 html 什么是弱网测试 弱网测试主要在宽带 丢包 延时的弱网环境中 验证客户端的展示 以及丢包 延时的处理机制 属于健壮性测试的内容 比如弱网下
  • STM32移植U8g2图形库——玩转OLED显示

    之前的文章 介绍过ESP8266在Arduino IDE环境中使用U8g2库 实现OLED上的各种图形显示 本篇 介绍一下U8g2库如何移植到STM32上 进行OLED的图形显示 本次的实验硬件为 STM32 型号为最常见的STM32F10
  • 前事不忘,后事之师——基于相似性进行剩余有效寿命预测的案例讲解

    在上一篇文章中我们讲到了三种机电产品算命方法 相似模型法 退化模型法和生存模型法 这一篇我们将使用相似模型法构建完整的剩余使用寿命 RUL 估计工作流程 该案例来自MATLAB的Similarity Based Remaining Usef
  • JavaScript设计模式(三)——单例模式、装饰器模式、适配器模式

    个人简介 个人主页 前端杂货铺 学习方向 主攻前端方向 正逐渐往全干发展 个人状态 研发工程师 现效力于中国工业软件事业 人生格言 积跬步至千里 积小流成江海 推荐学习 前端面试宝典 Vue2 Vue3 Vue2 3项目实战 Node js
  • SpringBoot快速入门

    SpringBoot快速入门 1 SpringBoot简介 SpringBoot概述 SpringBoot起步依赖 SpringBoot程序启动 入门案例 SpringBoot项目快速启动 2 基础配置 自动提示功能消失解决方案 yaml语
  • docker创建多个mysql集群_Docker在一台服务器上安装和配置Mysql集群

    1 从docker hub下载mysql5 6的镜像 docker pull mysql 5 6 2 使用mysql5 6镜像运行4台mysql服务 用端口号区分 前期准备工作 在本机创建四个目录 分别用了存储4台mysql服务的数据 日志
  • Windows服务器管理(运维)——cmd命令大全

    1 文件和目录操作命令 cd 更改当前目录 dir 列出当前目录中的文件和文件夹 mkdir 创建一个新的文件夹 rmdir 删除一个空的文件夹 copy 复制文件或文件夹 del 删除文件 ren 重命名文件或文件夹 move 移动文件或
  • angular html原理,Angular 4.x ngModel 双向绑定原理揭秘

    在 Angular 4 x 中对于使用 Template Driven 表单场景 如果需要实现表单数据绑定 我们就需要引入 ngModel 指令 该指令用于基于 domain 模型 创建 FormControl 实例 并将创建的实例绑定到表