
Angular
汪子熙
JerryWang,2007年从电子科技大学计算机专业硕士毕业后加入SAP成都研究院工作至今。Jerry是SAP社区导师,SAP中国技术大使。2020年5月下旬,Jerry做了脑部肿瘤的开颅切除手术,对编程和人生又有了新的感悟。
-
原创 SAP Spartacus OrganizationState
OrganizationState作为createSelector的第二个参数,即projector,箭头函数的输入参数。左边的大写字符串是常量:b2bUser是在Management数据类型的基础上,增添了三个新字段:approverspermissionsuserGroupsB2BUser的五个字段:Management:1export interface B2BUserManagement extends Managementexport interfa2021-03-04 22:18:194
0
-
原创 SAP Spartacus B2BUserService的一个问题
This issue is for discussion in this StackOverflow question:https://stackoverflow.com/questions/66458798/how-can-i-detect-current-users-user-group-in-spartacusIn order to test it, I add the following source code in AppModule’s constructor:constructor(b22021-03-04 20:14:243
0
-
原创 SAP Spartacus CMS Component的lazy loading懒加载方式
代码如下: ConfigModule.withConfig({ cmsComponents: { SimpleResponsiveBannerComponent: { component: () => import('./lazy/lazy-banner.component').then( (m) => m.LazyBanner ), } }2021-03-04 16:21:283
0
-
原创 Angular Lazy load学习笔记
Lazy loading, also known as code splitting, lets you divide your JavaScript code into multiple chunks. The result is that you do not have to load all the JavaScript of the full application when a user accesses the first page. Instead, only the chunks that.2021-03-04 16:12:006
0
-
原创 从一个实际的例子出发,谈谈SAP Commerce Cloud电商云的UI自定义开发
昨天 Jerry 读到了 SAP天天事 公众号上发布的一篇文章:多家知名企业选择 SAP Commerce Cloud 为客户提供卓越购物体验。SAP 日前宣布,锦江国际旗下的全球酒店供应链平台锦江联采、摩托车制造商新大洲本田和娱乐零售商 Virgin Megastore 已选择采用 SAP Commerce Cloud(电商云)解决方案,为客户提供卓越的电商体验。既然选择了 SAP Commerce Cloud 并进行实施,那么对其前台界面的定制化必不可少。面向终端消费者的 SAP Comme2021-03-04 14:15:076
0
-
原创 从ngrx store里selector出来的Observable,执行subscribe的单步调试
源代码: getNextPageContext(): Observable<PageContext> { const a = this.store.pipe(select(RoutingSelector.getNextPageContext)); console.log('Jerry next page context: ' + a); a.subscribe((b) =>{ console.log(b); }); return a;2021-03-01 22:54:2227
0
-
原创 Angular routerLink指令的href属性生成逻辑
一个例子:运行时,触发change detection,调用refreshView方法:RouterLinkWithHref这个class实现了ngOnChanges hook方法:在updateTargetUrlAndHref方法里,生成href:this.commands就是我们传入到routerLink Directive里的输入:createUrlTree:将JavaScript对象this.urlTree, 序列化成字符串url:更多Jerry的原创文章,尽在:“汪2021-03-01 11:31:4470
0
-
原创 如何通过ActivationStart监控 Angular的路由激活事件
看个具体的例子:在app.component.ts里注入Router:export class AppComponent { constructor(router:Router){ console.log('Checking router'); router.events.pipe( filter(e => e instanceof ActivationStart) ).subscribe(e =>{ console.log('路由开始2021-02-28 14:44:4528
0
-
原创 Angular 路由的wild匹配
我如果在Angular Component里输入一个并不存在path配置的url,会遇到如下错误消息:ERROR Error: Uncaught (in promise): Error: Cannot match any routes. URL Segment: ‘custom/any/2’解决方案:再增添一个Angular wild 路由匹配,路径如下: { path: '**', component: WildComponent }之后就不会有错误消息了:更多J2021-02-27 22:18:3328
0
-
原创 Angular路由里的resolve接口的使用方法
https://angular.io/api/router/ResolveInterface that classes can implement to be a data provider. A data provider class can be used with the router to resolve data during navigation. The interface defines a resolve() method that is invoked when the naviga2021-02-27 21:39:0843
0
-
原创 Angular路由里的canActivate用法
看个具体的例子。const CUSTOM_ROUTES: Routes = [ { path: "custom/:id", component: RouteDemoComponent, data:{ name: 'jerry'}, canActivate: [CanActivateTeam] }];canActivate的类型是数组,而不是单个元素。在app.module.ts的providers里,导入CanActivateTeam:canActivate2021-02-26 21:42:2137
0
-
原创 TypeScript里的Index signature
参考链接:https://basarat.gitbook.io/typescript/type-system/index-signatures看一个例子:class Foo { constructor(public message: string){}; log(){ console.log(this.message) } } let foo: any = {}; foo['Hello'] = new Foo('World')2021-02-26 19:36:2829
0
-
原创 如何从Angular的ActivatedRoute数据结构中获得运行时路由信息
看个具体的例子:路由配置信息:const CUSTOM_ROUTES: Routes = [ { path: "custom/:id", component: RouteDemoComponent }];RouteDemoComponent的实现:import { Component, OnInit } from '@angular/core';import { ActivatedRoute } from '@angular/router';import { Observable } f2021-02-26 17:09:5725
0
-
原创 Angular Route数据结构里常用字段使用方法一览
本文介绍Route interface里常用字段的使用方法。https://angular.io/api/router/Route#descriptionpathCan be a wild card (**) that matches any URL (see Usage Notes below). Default is “/” (the root path).两个星号:代表任意匹配。pathMatchThe path-matching strategy, one of ‘pref2021-02-26 16:37:5624
0
-
原创 为什么Angular的路由执行离不开router-outlet标签
做一个实验,如果把第53行包含router-outlet的代码注释掉:则在运行时,context.outlet为null:如此一来,没有机会执行第4107行的代码了:context.outlet.activatedWith(future, cmpFactoryResolver);RouterOutlet里包含的有用信息:整个这段代码都是在router-outlet的实现上下文里执行的。更多Jerry的原创文章,尽在:“汪子熙”:...2021-02-26 13:52:1437
0
-
原创 Angular router-outlet占位符层级结构的子节点,运行时是如何插入的
我有一个简单的Component:@Component({ selector: "app-root", template: ` <a href="/custom2">Click me</a> <div class="container"> <router-outlet></router-outlet> </div> `})export class AppComponent {运行时,router2021-02-26 11:57:0628
0
-
原创 最简单的Angular Route hello world场景都跑不通的一个原因
我有一个最简单的app Component:@Component({ selector: "app-root", template: ` <div cxFocuses>Painful</div> <a href="/custom2">Click me</a> `})export class AppComponent {}我期望点击了Click Me这个标签之后,能跳转到相对路径/custom2.在app.module.js里2021-02-26 11:33:1240
0
-
原创 Angular路由错误消息 - router-outlet is not a known element
我在application Component的HTML里定义了一个router-outlet元素:遇到错误消息:router-outlet is not a known element查看其定义,源代码比文档更有说服力:/** * @description * * Acts as a placeholder that Angular dynamically fills based on the current router state. * * Each outlet can have2021-02-25 22:52:4335
0
-
原创 一个关于Angular Directive selector里的中括号使用问题
其实对于Angular指令的selector,我一直搞得不是太清楚,看下面的例子:selector的定义里,包含了中括号。在消费该Directive的HTML页面里,不用中括号:则Directive正常工作,我在Directive的ngOnInit里打印了一些调试语句。如果消费Directive的时候,用了中括号,反而报错:selector定义中去掉中括号,反而不工作了:正确的打开方式:在Directive selector里使用中括号,消费Directive的时候去掉。更多Jerr2021-02-20 17:21:4525
0
-
原创 关于Angular里给Component protected方法写单元测试的技巧
本文写作背景是,我需要开发一个单元测试,能够测试到protected convertListItem方法。我直接敲service., Visual Studio Code的代码自动完成列表里,没有出现convertListItem,因为它是protected方法。但我们要知道,因为Angular TypeScript编译后生成的是JavaScript代码,而JavaScript里是不存在protected方法概念的,因此我们可以使用第244行这种动态调用的方式,实现在运行时仍然能够执行convertLi2021-02-20 17:16:5515
0
-
原创 Not using the local TSLint version found for XXX
今天我打开Visual Studio Code准备进行Angular开发时,看到Angular TSLint报了这个警告信息:Not using the local TSLint version found for ‘c:/Code/SPA/spartacus/feature-libs/organization/administration/components/unit/services/unit-list.service.spec.ts’To enable code execution from2021-02-20 17:02:32134
0
-
原创 Angular应用里setTimeout被如何被monkey patched的
monkey patched: monkey patch指的是在运行时动态替换,一般是在startup的时候.下面的测试代码可以让我们弄清楚,浏览器支持的原生函数setTimeout,是如何在Angular应用启动时被monkey patched的:将setTimeout保存到一个Angular变量里。在zone-evergreen.js的patchMethod方法里:下图地2719行,能看到timers组下面的setTimeout,setInterval被monkey patched的入口:2021-02-17 11:47:1630
0
-
原创 Angular 默认的Change detection策略及缺陷
默认策略下,用户时间,计时器,XHR,promise等事件触发,所有的组件都会执行变更检测。看一个实际例子:import { Component, OnInit, Input } from '@angular/core';@Component({ selector: 'appparentchild', template: `<h1>title</h1> {{ JerryHelloChange }} <child [childContent] = "pa2021-02-15 12:33:3436
0
-
原创 Angular NgModule中的declarations和exports定义
我在app.component.ts的template文件里试图使用另一个Component时,遇到如下错误消息:app-parent-child is not a known element:在parent.module.ts定义里,检查exports区域里是否包含了selector app-parent-child对应的Angular Component:ParentChildComponent:declarations: 定义了该NgModule的组成部分:Components, direc2021-02-13 21:24:24110
5
-
原创 Could not find an NgModule. Use the skip-import option to skip importing in
该错误消息意思是创建Component时未指定其属于哪一个module,因此先用命令行ng g m parent创建一个名为parent的module:默认生成的代码如下图所示:然后使用ng g c 创建一个名为parent-child的Component,使用参数 -m parent挂接到刚才新建的名为parent的module即可。更多Jerry的原创文章,尽在:“汪子熙”:...2021-02-13 19:59:4030
0
-
原创 如何在Angular单元测试里,对class protected方法进行测试
例子:我的service class里有一个protected方法,我想在单元测试里对其进行测试:一种思路是,可以沿用Java里测试protected方法的变通方式,即创建一个新的sub class,继承包含该protected方法的class,然后在子类里新建一个公有方法作为wrapper之用,实现逻辑只有一行,就是调用父类的protected方法。例子如下:这样,我只需要对子类的公有方法进行单元测试即可测试到父类的protected方法。完整源代码:import { Injectable2021-02-10 14:33:2141
0
-
原创 fixture.detectChange开始单步调试,如何执行到Directive的ngAfterViewInit钩子
同ngOnChanges hook的调用入口一致,请查看这篇文章:fixture.detectChange开始单步调试,如何执行到Directive的ngOnChange钩子.refreshView是一个逻辑很多的函数:/** * Processes a view in update mode. This includes a number of steps in a specific order: * - executing a template function in update mode;2021-02-02 16:23:0828
0
-
原创 fixture.detectChange开始单步调试,如何执行到Directive的ngOnChange钩子
this._tick里调用this.changeDetectorRef.detectChanges():首先从RootViewRef开始检测:context里能看到Component的数据:renderComponentOrTemplate:refreshView里会调用init和check hook:/** * Executes post-order init and check hooks (one of AfterContentInit, AfterContentChecked2021-02-02 16:18:0426
0
-
原创 fixture.detectChange如何通过Angular zone执行其异步逻辑的
调试出发点:在detectChange内部本身有一个ngZone:接下来会在this.ngZone里执行箭头函数的逻辑,即this._tick():this.ngZone.run里面调用_inner的run方法:this._zoneDelegate.invoke:ZoneDelegate.invoke方法内部, 调用this._invokeZS.onInvoke:onInvoke:首先onEnter进入Zone:onEnter方法,修改zone._nesting的层数:p2021-02-02 15:45:5422
0
-
原创 Angular zone学习笔记
https://blog.thoughtram.io/angular/2016/01/22/understanding-zones.html假设有这三个函数:foo();bar();baz();function foo() {...}function bar() {...}function baz() {...}要度量其运行时间:var start, time = 0; timer = performance ? performance.now : Date.now;2021-02-02 13:36:5926
0
-
原创 Angular单元测试遇到的错误消息:Uncaught Error - Cannot find module tslib
我的单元测试源代码:@Component({ selector: 'cx-host', template: ` <div id="a" [cxFocus]="{ autofocus: true, refreshFocus: model.uid }"></div>`, changeDetection: ChangeDetectionStrategy.Default,})class MockComponent { model = "";}将下图import的2021-02-01 22:27:2355
0
-
原创 在Angular单元测试代码的it方法里连续调用两次detectChange方法,会触发两次ngAfterViewInit吗
做一个测试:测试结果:第67行执行完毕之后,ngOnChange和ngAfterViewInit均未触发。即使手动修改Component的属性也没用了:单元测试里修改的属性已经生效了:还是解析到了我的Directive啊:整个current都为空,执行不进去了:current字段是在2130行被清空的:更多Jerry的原创文章,尽在:“汪子熙”:...2021-02-01 22:11:1221
0
-
原创 Angular单元测试框架beforeEach和it的执行顺序
AsyncTestZoneSpec:然后执行第一个it:再执行beforeEach的callback:然后执行第二个it:由此可见,beforeEach相当于ABAP单元测试的setup方法。更多Jerry的原创文章,尽在:“汪子熙”:2021-02-01 22:09:2933
0
-
原创 关于Angular使用http发送请求后的响应处理
源代码:import { Component, OnInit } from '@angular/core';import { FormControl } from '@angular/forms';import { HttpClient } from '@angular/common/http';const ENDPOINT = "http://localhost:3000/echo?data=";@Component({ selector: 'jerryform', template2021-01-20 13:00:2146
0
-
原创 使用Angular的http client发送请求,请求response总是被当成json类型处理
奇怪的问题,我的req.responseType字段没有显式赋值,而默认值为json:这个默认值是在哪里填充的呢?调试代码,http.js的第1669行有这个默认的逻辑:解决方案:下图第26行responseType: 'text' as 'json'import { Component, OnInit } from '@angular/core';import { FormControl } from '@angular/forms';import { HttpClient, Ht2021-01-20 11:16:3034
0
-
原创 SAP UI5和Angular的函数防抖(Debounce)和函数节流(Throttle)实现原理介绍
这是Jerry 2021年的第 11 篇文章,也是汪子熙公众号总共第 282 篇原创文章。Jerry之前的文章 SAP UI5 OData谣言粉碎机:极短时间内发送两个Odata request, 前一个会自动被cancel掉吗,介绍过SAP成都研究院CRM Fiori开发团队开发过的一个Live Search的场景。用户创建Opportunity,维护Account字段,每输入一个字符,都会触发SAP UI5 Input控件的liveChange事件。在该事件的onAccountInputFieldC2021-01-19 19:48:4133
0
-
原创 使用Angular reactive form发送HTTP请求的一个简单例子
form源代码:import { Component, OnInit } from '@angular/core';import { FormControl } from '@angular/forms';@Component({ selector: 'jerryform', template: ` Favorite Color: <input type="text" [formControl]="favoriteColorControl"> `})export2021-01-14 14:00:2039
0
-
原创 Property ngOnInit does not exist on type VisibleFocusDirective
原因是我把Base focus Directive的OnInit函数删除了:添加上之后问题消失:更多Jerry的原创文章,尽在:“汪子熙”:2021-01-13 14:55:5525
0
-
原创 Angular里ngModel双向绑定的一个使用例子
源代码:@Component({ selector: "app-root", template: ` <input [(ngModel)] = "jerry"> `})export class AppComponent implements AfterViewInit { _jerry = "Hello"; ngAfterViewInit(): void { setTimeout(() => { this._jerry = "chan2021-01-13 13:49:0546
0
-
原创 Angular里ngClass的一个使用例子
ngClass定义的位置:export declare class NgClass implements DoCheck { private _iterableDiffers; private _keyValueDiffers; private _ngEl; private _renderer; private _iterableDiffer; private _keyValueDiffer; private _initialClasses;2021-01-13 13:42:2348
0