开篇三问:

为何要进行单元测试?

单元测试有什么好处?

如何编写angular单元测试?

没有单元测试会如何?

或者换句话说,为何要开发编写单元测试?
在业务开发紧张的情况下,往往会忽略单元测试,直接采用,然后开启下方的难忘人生回忆~

单元测试有啥好处?

我们在开发完毕,加入单元测试环节,下划线部分可能就不存在了~

如何进行angular单元测试?

angular前提背景知识

  • 构建angular框架,angular-cli命令可以,在创建service、pipe、component时候,同时创建对应的测试用例**.spec.ts文件
  • 运行单元测试
 ng test --no-watch --code-coverage //根目录下会生成coverage目录,其中index.html记录组件覆盖率
  • 查看

编写angular8 单元测试

测试service-无依赖

框架new实例测试

代码如下:

@Injectable() //交给angular管理,帮忙注入依赖
export class ValueService {value:string;constructor() { }getValue() { return this.value}
}

测试用例如下:

# 1.直接new service 实例
let service: ValueService;beforeEach(() => { service = new ValueService(); });it('#getValue should return real value', () => {expect(service.getValue()).toBe('real value');});# or # 2.直接获取服务实例进行测试,通过调用服务,校验逻辑
let service: ValueService;
beforeEach(() => {TestBed.configureTestingModule({ providers: [ValueService] }); //等效于useClass
});
it('should use ValueService', () => {service = TestBed.get(ValueService);expect(service.getValue()).toBe('real value');
});

测试service - 有依赖

利用spyOn mock

代码如下:

@Injectable()
export class MasterService {constructor(private valueService: ValueService) { }getValue() { return this.valueService.getValue(); }
}

获取真实的依赖服务,常因为服务中依赖原因,难以顺利创建。此时spy,跳过真正的服务业务逻辑,进行单独测试,是最简单的方法。** 不跳过依赖,则属于集成测试范畴。**

测试如下:

let masterService: MasterService;
let valueServiceSpy: jasmine.SpyObj<ValueService>;beforeEach(() => {const spy = jasmine.createSpyObj('ValueService', ['getValue']);//需要注意位置,在beforeEachTestBed.configureTestingModule({// Provide both the service-to-test and its (spy) dependencyproviders: [MasterService,//注入服务,mock提供依赖服务的支持,完成MasterService实例创建{ provide: ValueService, useValue: spy } ]});// Inject both the service-to-test and its (spy) dependencymasterService = TestBed.get(MasterService);valueServiceSpy = TestBed.get(ValueService);
});
it('#getValue should return stubbed value from a spy', () => {const stubValue = 'stub value';# mock 返回值 valueServiceSpy.getValue.and.returnValue(stubValue);expect(masterService.getValue()).toBe(stubValue, 'service returned stub value'); //利用mock依赖返回的值,进行期望判断业务逻辑
});

测试组件-无依赖

代码如下:

@Component({selector: 'lightswitch-comp',template: `<button (click)="clicked()">Click me!</button><span>{{message}}</span>`
})
export class LightswitchComponent {isOn = false;clicked() { this.isOn = !this.isOn; }get message() { return `The light is ${this.isOn ? 'On' : 'Off'}`; }
}

测试代码如下:

//直接new 
it('#clicked() should set #message to "is on"', () => {const comp = new LightswitchComponent();expect(comp.message).toMatch(/is off/i, 'off at first');comp.clicked();expect(comp.message).toMatch(/is on/i, 'on after clicked');});//or 获取组件实例,交给框架创建newlet comp:LightswitchComponent;beforeEach(() => {TestBed.configureTestingModule({// provide the component-under-test and dependent serviceproviders: [LightswitchComponent,]});// inject both the component and the dependent service.comp = TestBed.get(LightswitchComponent);
});it('#clicked() should set #message to "is on"', () => {expect(comp.message).toMatch(/is off/i, 'off at first');comp.clicked();expect(comp.message).toMatch(/is on/i, 'on after clicked');});

测试组件-有input、output

export class DashboardHeroComponent {@Input() hero: Hero;@Output() selected = new EventEmitter<Hero>();click() { this.selected.emit(this.hero); }
}

测试代码如下:

let comp:DashboardHeroComponent;beforeEach(() => {TestBed.configureTestingModule({// provide the component-under-test and dependent serviceproviders: [DashboardHeroComponent,]});// inject both the component and the dependent service.comp = TestBed.get(DashboardHeroComponent);
});
it('raises the selected event when clicked', () => {const hero: Hero = { id: 42, name: 'Test' };comp.hero = hero;comp.selected.subscribe((selectedHero: Hero) => expect(selectedHero).toBe(hero));comp.click();
});

测试组件 - 有依赖

WelcomeComponent 依赖于 UserService

export class WelcomeComponent  implements OnInit {welcome: string;constructor(private userService: UserService) { }ngOnInit(): void {this.welcome = this.userService.isLoggedIn ?'Welcome, ' + this.userService.user.name : 'Please log in.';}
}

测试代码

# spect.ts
class MockUserService {isLoggedIn = true;user = { name: 'Test User'};
};beforeEach(() => {TestBed.configureTestingModule({// provide the component-under-test and dependent serviceproviders: [WelcomeComponent,{ provide: UserService, useClass: MockUserService } // {provide: UserService, useVale: userServiceSpy} # 两者都可以,不同方式而已]});// inject both the component and the dependent service.comp = TestBed.get(WelcomeComponent);//容易记住,也不太冗长。但是,只有当Angular在测试的根注入器中将带有服务实例的组件注入组件时,它才起作用。userService = TestBed.get(UserService); //userService = fixture.debugElement.injector.get(UserService);
});
it('should not have welcome message after construction', () => {expect(comp.welcome).toBeUndefined();
});
it('should welcome logged in user after Angular calls ngOnInit', () => {comp.ngOnInit();expect(comp.welcome).toContain(userService.user.name);
});
it('should ask user to log in if not logged in after ngOnInit', () => {userService.isLoggedIn = false;comp.ngOnInit();expect(comp.welcome).not.toContain(userService.user.name);expect(comp.welcome).toContain('log in');
});

组件中dom元素测试

组件创建测试

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BannerComponent } from './banner.component';
describe('BannerComponent', () => {let component: BannerComponent;let fixture: ComponentFixture<BannerComponent>;beforeEach(async(() => {TestBed.configureTestingModule({declarations: [ BannerComponent ]}).compileComponents();}));beforeEach(() => {fixture = TestBed.createComponent(BannerComponent);component = fixture.componentInstance;fixture.detectChanges();});# 只有在组件创建初期有意义,后面添加业务单元测试,推荐删除的it('should create', () => {expect(component).toBeDefined();});});

页面元素固定

 it('should contain "banner works!"', () => {const bannerElement: HTMLElement = fixture.nativeElement;expect(bannerElement.textContent).toContain('banner works!');});it('should have <p> with "banner works!"', () => {const bannerElement: HTMLElement = fixture.nativeElement;const p = bannerElement.querySelector('p');expect(p.textContent).toEqual('banner works!');});it('should find the <p> with fixture.debugElement.nativeElement)', () => {const bannerDe: DebugElement = fixture.debugElement;const bannerEl: HTMLElement = bannerDe.nativeElement;const p = bannerEl.querySelector('p');expect(p.textContent).toEqual('banner works!');
});

如果querySelector不能使用,

import { By } from '@angular/platform-browser';
it('should find the <p> with fixture.debugElement.query(By.css)', () => {const bannerDe: DebugElement = fixture.debugElement;const paragraphDe = bannerDe.query(By.css('p'));const p: HTMLElement = paragraphDe.nativeElement;expect(p.textContent).toEqual('banner works!');
});

页面元素动态修改

页面元素动态修改,测试

it('should display a different test title', () => {component.title = 'Test Title';fixture.detectChanges(); //显示的进行修改检测expect(h1.textContent).toContain('Test Title');
});

除去上述显示声明detectChanges,使用自动检测也可以实现

import { ComponentFixtureAutoDetect } from '@angular/core/testing';
TestBed.configureTestingModule({declarations: [ BannerComponent ],providers: [{ provide: ComponentFixtureAutoDetect, useValue: true }]
});

Render2 样式测试

import {Type ,Render2 } from 'angular/core';let renderer2: Renderer2;
...
beforeEach(async( () => {TestBed.configureTestingModule({...providers: [Renderer2]}).compileComponents();
}));beforeEach(() => {fixture = TestBed.createComponent(BannerComponent);renderer2 = fixture.componentRef.injector.get<Renderer2>(Renderer2 as Type<Renderer2>);// and spy on itspyOn(renderer2, 'addClass').and.callThrough();// or replace// spyOn(renderer2, 'addClass').and.callFake(..);// etc
});it('should call renderer', () => {expect(renderer2.addClass).toHaveBeenCalledWith(jasmine.any(Object), 'css-class');
});

Observable测试

代码如下

getQuote() {this.errorMessage = '';this.quote = this.twainService.getQuote().pipe(startWith('...'),catchError( (err: any) => {// Wait a turn because errorMessage already set once this turn() => this.errorMessage = err.message || err.toString()return of('...'); // reset message to placeholder}));

正常返回

beforeEach(() => {testQuote = 'Test Quote';const twainServiceSpy = jasmine.createSpyObj('TwainService', ['getQuote']);getQuoteSpy = twainServiceSpy.getQuote.and.returnValue( of(testQuote) ); //关键在此TestBed.configureTestingModule({declarations: [ TwainComponent ],providers:    [{ provide: TwainService, useValue: twainServiceSpy }]});fixture = TestBed.createComponent(TwainComponent);component = fixture.componentInstance;quoteEl = fixture.nativeElement.querySelector('.twain');
});
it('should show quote after component initialized', () => {fixture.detectChanges(); // onInit()expect(quoteEl.textContent).toBe(testQuote);expect(getQuoteSpy.calls.any()).toBe(true, 'getQuote called');
});

返回异常

beforeEach(() => {const twainService = jasmine.createSpyObj('TwainService', ['getQuote']);getQuoteSpy = twainService.getQuote.and.returnValue( throwError('ops') ); //关键在此TestBed.configureTestingModule({declarations: [ TwainComponent ],providers:    [{ provide: TwainService, useValue: twainService }]});fixture = TestBed.createComponent(TwainComponent);component = fixture.componentInstance;quoteEl = fixture.nativeElement.querySelector('.twain');
});
it('should show quote after component initialized', () => {fixture.detectChanges(); // onInit()expect(errorMessage()).toMatch(/test failure/, 'should display error');expect(quoteEl.textContent).toBe('...', 'should show placeholder');
});

返回异常,但异步处理

getQuote() {this.errorMessage = '';this.quote = this.twainService.getQuote().pipe(startWith('...'),catchError( (err: any) => {setTimeout(() => this.errorMessage = err.message || err.toString());return of('...');}));beforeEach(() => {const twainService = jasmine.createSpyObj('TwainService', ['getQuote']);getQuoteSpy = twainService.getQuote.and.returnValue( throwError('ops') ); //关键在此TestBed.configureTestingModule({declarations: [ TwainComponent ],providers:    [{ provide: TwainService, useValue: twainService }]});fixture = TestBed.createComponent(TwainComponent);component = fixture.componentInstance;quoteEl = fixture.nativeElement.querySelector('.twain');
});it('should display error when TwainService fails', fakeAsync(() => { //fakeAsync不适用与ajaxgetQuoteSpy.and.returnValue(throwError('TwainService test failure'));fixture.detectChanges(); // onInit()tick(); // flush the component's setTimeout()fixture.detectChanges(); // update errorMessage within setTimeout()expect(errorMessage()).toMatch(/test failure/, 'should display error');expect(quoteEl.textContent).toBe('...', 'should show placeholder');
}));

异步代码测试

使用fakeAsync

it('should get Date diff correctly in fakeAsync', fakeAsync(() => {const start = Date.now();tick(100);const end = Date.now();expect(end - start).toBe(100);}));

fakeAsync支持以下异步任务:

  • setTimeout
  • setInterval
  • requestAnimationFrame
  • webkitRequestAnimationFrame
  • mozRequestAnimationFrame
  • rxjs - delay、interval等

ajax请求测试

it('should show quote after getQuote (async)', async(() => {fixture.detectChanges(); // ngOnInit()expect(quoteEl.textContent).toBe('...', 'should show placeholder');fixture.whenStable().then(() => { // wait for async getQuotefixture.detectChanges();        // update view with quoteexpect(quoteEl.textContent).toBe(testQuote);expect(errorMessage()).toBeNull('should not show error');});
}));

jasmine done

it('should show quote after getQuote (spy done)', (done: DoneFn) => {fixture.detectChanges();// the spy's most recent call returns the observable with the test quotegetQuoteSpy.calls.mostRecent().returnValue.subscribe(() => {fixture.detectChanges(); // update view with quoteexpect(quoteEl.textContent).toBe(testQuote);expect(errorMessage()).toBeNull('should not show error');done();});
});

组件嵌套测试

服务依赖错误

TypeError: ctor is not a constructor

问题原因:provide中错误的配置

//错误的
providers: [{provide: OrderService, useClass: new OrderServiceMock()}]
//正确的
providers: [{provide: OrderService, useValue: new OrderServiceMock()}]

HTTP service测试

类似service测试,使用Spy

使用HttpTestingController

配置

  let service: BlogPostsService;let backend: HttpTestingController;beforeEach(() => {TestBed.configureTestingModule({providers: [BlogPostsService],imports: [HttpClientTestingModule]});});beforeEach(() => {service = TestBed.get(BlogPostsService);backend = TestBed.get(HttpTestingController);});

expectOne判定url

it('should expectOne url', () => {service.getAll().subscribe();backend.expectOne(`https://rails-rest.herokuapp.com/posts`);backend.verify();
});

method判定

it('should expectOne url and method', () => {service.getAll().subscribe();backend.expectOne({url: `https://rails-rest.herokuapp.com/posts`});service.getAll().subscribe();backend.expectOne({url: `https://rails-rest.herokuapp.com/posts`, method: 'GET'});backend.verify();
});

none判定

 it('should not expect one when not subscribed', () => {service.getAll()// .subscribe();backend.expectNone(`https://rails-rest.herokuapp.com/posts`);backend.verify();});

match 正则判定

 it('should match two requests', () => {service.getAll().subscribe();service.get(1).subscribe();const calls = backend.match((request) => {return request.url.match(/posts/) && # url正则匹配request.method === 'GET';});expect(calls.length).toEqual(2);expect(calls[0].request.url).toEqual(`https://rails-rest.herokuapp.com/posts`);expect(calls[1].request.url).toEqual(`https://rails-rest.herokuapp.com/posts/1.json`);backend.verify();});

match 不同url

it('should match different requests', () => {service.getAll().subscribe();service.get(1).subscribe();const otherCalls = backend.match((request) => {return request.url == `https://rails-rest.herokuapp.com/posts/1.json` &&request.method === 'GET';});const calls = backend.match((request) => {return request.url == `https://rails-rest.herokuapp.com/posts` &&request.method === 'GET';});expect(calls.length).toEqual(1);expect(otherCalls.length).toEqual(1);expect(calls[0].request.url).toEqual(`https://rails-rest.herokuapp.com/posts`);expect(otherCalls[0].request.url).toEqual(`https://rails-rest.herokuapp.com/posts/1.json`);backend.verify();});

match 判定urlWithParams

it('should have url and urlWithParams', () => {service.getAll({page: 1}).subscribe();const calls = backend.match((request) => {return request.url == `https://rails-rest.herokuapp.com/posts` &&request.urlWithParams == `https://rails-rest.herokuapp.com/posts?page=1` &&request.method === 'GET';});backend.expectNone(`https://rails-rest.herokuapp.com/posts`); // If url with params, use `.match`backend.verify();});

match 其余request参数

it('should have a few more attributes on request that are useful', () => {service.getAll({page: 1}).subscribe();const calls = backend.match((request: HttpRequest<any>) => {return request.url == `https://rails-rest.herokuapp.com/posts` &&request.urlWithParams == `https://rails-rest.herokuapp.com/posts?page=1` &&request.method === 'GET' &&request.params.get('page') == '1' &&request.body == null &&request.headers instanceof HttpHeaders &&request.responseType == 'json' &&request.withCredentials == false;});backend.expectNone(`https://rails-rest.herokuapp.com/posts`); // If url with params, use `.match`backend.verify();});

subscribe 结果验证

it('should create post', () => {service.save({title: 'Creating a post',content: 'Another long description...'}).subscribe((response) => {expect(response).toEqual(jasmine.objectContaining({id: 2,title: 'Creating a post',content: jasmine.any(String),created_at: new Date('2017-12-07T04:39:49.447Z'),updated_at: jasmine.any(Date)}));});const response = {'id': 2,'title': 'Creating a post','content': 'Another long description...','created_at': '2017-12-07T04:39:49.447Z','updated_at': '2017-12-07T04:39:49.447Z'};const call = backend.expectOne(`https://rails-rest.herokuapp.com/posts`);expect(call.request.method).toEqual('POST');call.flush(response); # 返回结果backend.verify();});

个人心得

  • 测试用例的编写,应该尽可能的简化测试对象逻辑,分而测之,
  • 避免一次调用,敲定全部测试,这属于集成测试范畴
  • 编写代码时候,需要有意识的拆分代码,便于单元测试,不要一个方法一大屏看不到低

更多推荐

Angular开发提效vscode插件

angular开发需要了解的rxjs操作符实践

Angular8 日常开发避坑指南

参考文献

window 变量

Angular

d3 测试

HttpTestingContrller

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

相关文章

  1. Angular Ionic概述

    Angular&#xff1a; SPAS&#xff1a;单页应用程序 MVC&#xff1a; M(model)&#xff1a;数据模型 V(view)&#xff1a;视图 C(controller)&#xff1a;控制器 控制器是用于协调数据模型和视图之间的关系&#xff0c;视图和模型都是相互独立的 为什么要使用MVC&#x…...

    2024/3/14 22:41:43
  2. angular第二天

    一、自定义指令 语法&#xff1a; var app angular.module(myModule,[ng]); app.directive(name,fn); 自定义指令&#xff1a; restrict:EACM E-->Element 元素 A-->Attribute 属性 C-->Class M-->Comment 注释&#xff08;在指令中添加一个属性&#xff1a;…...

    2024/3/14 22:41:41
  3. angular5项目笔记汇总

    很久之前用angular4写过demo&#xff0c;最近用V5来做个小项目&#xff0c;碰到了些问题。在这儿做个笔记。 发布到服务上出现404错误 解决方案1 用锚点实现路由&#xff1a; // app.module.ts import { LocationStrategy, HashLocationStrategy} from angular/common; NgM…...

    2024/3/25 22:29:13
  4. Angular2 (One framework 概要)

    Angular1.x 版本的火爆&#xff0c;我们不用多说&#xff0c;是很多前端工作者不错的选择&#xff0c;速度快&#xff0c;方便快捷&#xff0c;可拓展性强&#xff0c;我做过很多项目都是基于Anguar 1.x架构的。 但是Goolge公司在2.x版本&#xff0c;确做了新的设计&#xff0…...

    2024/3/14 22:41:41
  5. angular自定义点击组件防止多次请求

    第一次写博客不知道写什么&#xff0c;写个前段时间解决的一个问题作为第一篇文吧&#xff08;哈哈&#xff09;。 做项目的时候&#xff0c;经常碰到网不好请求慢的时候用户就会多次进行点击&#xff0c;导致提交多次请求&#xff0c;为了阻止这种情况&#xff0c;自己写了一个…...

    2024/3/14 22:41:38
  6. 如何衡量angular工程师水平

    1. ng-if跟ng-show/hide的区别有哪些&#xff1f;2. ng-repeat迭代数组的时候&#xff0c;如果数组中有相同值&#xff0c;会有什么问题&#xff0c;如何解决&#xff1f;3. ng-click中写的表达式&#xff0c;能使用JS原生对象上的方法&#xff0c;比如Math.max之类的吗&#x…...

    2024/3/21 22:20:58
  7. ANGULAR JS WATCH监听使用(详)

    ANGULAR 监听使用&#xff1a; 当angular数据模型发生变化时&#xff0c;我们需要如果需要根据他的变化触发其他的事件。 $watch是一个scope函数&#xff0c;用于监听模型变化&#xff0c;当你的模型部分发生变化时它会通知你。 $watch(watchExpression, listener, objectEqual…...

    2024/3/14 22:41:37
  8. angular整合editor.md

    下载editor的文件 进入官网进行下载https://pandao.github.io/editor.md/,解压后放入assets目录下 全局加载editormd资源文件 全局加载js文件 在angular.json中引入editormd.min.js,在引入editormd.min.js文件前需要先引入jQuery 安装jQuery npm install jquery --save在…...

    2024/3/14 22:41:36
  9. Angular ng命令

    1、根目录下执行ng命令Microsoft Windows [版本 10.0.16299.492](c) 2017 Microsoft Corporation。保留所有权利。C:\Users\dqgzs>ng Available Commands:add Add support for a library to your project.new Creates a new directory and a new Angular app.generate Gener…...

    2024/3/14 22:41:34
  10. Angular权威指南学习笔记

    第一章&#xff0e; 初识Angular——Angular是MVW的Js框架。 第二章&#xff0e; 数据绑定——ViewModel中不仅可以含有变量&#xff0c;还可以还有事件。可以通过事件来控制变量的值改变。视图绑定着VM中的变量和事件。 第三章&#xff0e; 模块——可以…...

    2024/3/14 22:41:33
  11. Angular 2快速开始

    使用typescript和gulp快速开始Angular 2开发 Angular2出来了一段时间&#xff0c;通过更简单和更简洁的概念&#xff0c;如基于组件的架构&#xff0c;新的依赖注入或内置模块化&#xff0c;新版本的框架要学习得简单得多。在这个分步教程中&#xff0c;您将了解如何使用TypeS…...

    2024/3/13 20:02:42
  12. Gulp构建Angular项目实践

    Gulp是近年来很火的一款前端自动化构建工具&#xff0c;本人之前做的项目采用的是Grunt&#xff0c;感觉其配置较为繁琐&#xff0c;不是特别友好&#xff0c;故尝试用Gulp来构建一个Angular项目。 在实践之前我们先了解一下对比Grunt&#xff0c;Gulp有哪些优点&#xff1a; …...

    2024/3/13 20:02:39
  13. angular directive详解

    在前端开发的过程中&#xff0c;很多时候我们都希望能够重复的使用某一个模块&#xff0c;比如说文件上传、组织架构树等&#xff0c;angular的directive可以帮助我们轻松的实现组件的重复使用。 首先先让我们解释一个问题&#xff1a;什么是directive? directive是DOM元素的…...

    2024/3/29 2:34:09
  14. 关于angular js 监听

    1.监听变量的变化&#xff1a; $scope.$watch( 变量名 , function(newValue, oldValue, scope){ //。。。 }); 2.监听页面路由变化&#xff1a; $scope.$on( $destroy , function(){ //路由变化时的操作 });...

    2024/3/13 20:02:37
  15. Angular JS 入门学习

    1、内置指令 ng-app 制定控制域&#xff0c;模型的意思。ng-model 定义模型&#xff0c;与HTML元素绑定——-配合使用ng-init 数据模型初始化。ng-bind 绑定ng-model&#xff0c;可用“&#xff5b;&#xff5b;&#xff5d;&#xff5d;”来代替。ng-click 点击事件。 ng-dbl…...

    2024/3/14 22:41:34
  16. angular1+,性能优化

    u75601 单次绑定这样就没有watch。数据只绑定一次。{{::name}}2使用track by1是可以解决重复的问题&#xff0c; 2是解决dom元素重复创建&#xff0c; 删除的。3 数据扁平化。减少watch 4页面跳转传参$state.go (state param)$stateParam利用服务 创建空对象 set get$rootScop…...

    2024/3/26 8:25:37
  17. Angular之作用域

    1. 作用域的特点 1.1 具体来说&#xff0c;作用域包括以下3个比较显著特点&#xff1a; 提供了一个$watch 方法来监听数据模型的变化&#xff0c;ng-model的双向绑定&#xff0c;就是由其支撑。提供了$apply 方法&#xff0c;为各种类型的数据模型改变提供支撑&#xff0c;例如…...

    2024/3/14 22:41:31
  18. angular判断用户设备为移动设备还是PC

    $window[‘navigator’][‘userAgent’]&#xff1a; 获取当前浏览器的用户代理&#xff08;user agent&#xff09;字符串&#xff0c;userAgent 属性是一个只读的字符串&#xff0c;声明了浏览器用于 HTTP 请求的用户代理头的值。 $window[‘navigator’][‘vendor’]&#x…...

    2024/3/14 22:41:28
  19. Angular开发技巧

    由于之前有幸去参加了ngChina2018开发者大会&#xff0c;听了will保哥分享了Angular开发技巧&#xff0c;自己接触Angular也有差不多快一年的时间了&#xff0c;所以打算对Angular开发中的一些技巧做一个整理 工具篇 所谓 “工欲善其事&#xff0c;必先利其器”&#xff0c;下…...

    2024/3/14 22:41:29
  20. Angular2表单1模板驱动的表单(Template-Driven Forms)

    http://codin.im/2016/09/26/angular2-form-template-driven/ 在网页开发中&#xff0c;表单估计是最常用的一个&#xff0c;同时也是最麻烦、最容易出问题的。在一个稍微复杂一点的应用中&#xff0c;我们除了用表单元素收集数据&#xff0c;还需要验证&#xff0c;几个数据之…...

    2024/3/14 22:41:28

最新文章

  1. go实现链表

    package main import "fmt" // Node 定义链表节点 type Node struct { data int next *Node } // LinkedList 定义链表 type LinkedList struct { head *Node } // Append 在链表末尾添加新节点 func (l *LinkedList) Append(data int) { ne…...

    2024/3/29 4:15:05
  2. 梯度消失和梯度爆炸的一些处理方法

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

    2024/3/20 10:50:27
  3. js设计模式

    单例模式 它确保一个类只有一个实例&#xff0c;并提供了一个全局访问点来访问该实例。 class User {static #instance: User;#name: string;constructor(name: string) {this.#name nameif (new.target ! User) {return}if (!User.#instance) {User.#instance thisreturn U…...

    2024/3/28 14:52:21
  4. 一、初识 Web3

    瑾以此系列文章&#xff0c;献给那些出于好奇并且想要学习这方面知识的开发者们 在多数时间里&#xff0c;我们对 web3 的理解是非常模糊的 就好比提及什么是 web1 以及 web2&#xff0c;相关概念的解释是&#xff1a; 1. 从 Web3 的开始 Web3&#xff0c;也被称为Web3.0&…...

    2024/3/27 10:47:57
  5. 【外汇早评】美通胀数据走低,美元调整

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

    2024/3/27 10:21:24
  6. 【原油贵金属周评】原油多头拥挤,价格调整

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

    2024/3/24 20:11:25
  7. 【外汇周评】靓丽非农不及疲软通胀影响

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

    2024/3/29 2:45:46
  8. 【原油贵金属早评】库存继续增加,油价收跌

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

    2024/3/24 20:11:23
  9. 【外汇早评】日本央行会议纪要不改日元强势

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

    2024/3/26 20:58:42
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

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

    2024/3/28 17:01:12
  11. 【外汇早评】美欲与伊朗重谈协议

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

    2024/3/24 5:55:47
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

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

    2024/3/29 1:13:26
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

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

    2024/3/26 23:04:51
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

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

    2024/3/26 11:20:25
  15. 【外汇早评】美伊僵持,风险情绪继续升温

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

    2024/3/24 20:11:18
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

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

    2024/3/28 9:10:53
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

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

    2024/3/29 0:49:46
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

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

    2024/3/24 20:11:15
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

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

    2024/3/27 7:12:50
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

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

    2024/3/24 20:11:13
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

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

    2024/3/26 11:21:23
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

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

    2024/3/28 18:26:34
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

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

    2024/3/28 12:42:28
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

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

    2024/3/28 20:09:10
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

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

    2022/11/19 21:17:18
  26. 错误使用 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
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

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

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

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

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

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

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

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

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

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

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

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

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,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
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2022/11/19 21:16:58
  44. 如何在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