Fundebug前端JavaScript插件更新至180兼容低版本的Android浏览器

摘要: 兼容低版本Android浏览器,请大家及时更新。 Fundebug前端BUG监控服务Fundebug是专业的程序BUG监控平台,我们JavaScript插件可以提供全方位的BUG监控,可以帮助开发者第一时间定位JavaScript执行错误、HTTP请求错误以及资源加载错误。并且,我们可以记录用户行为、“录制”用户操作视频,帮助开发者快速复现BUG,提高Debug效率。 1.8.0兼容低版本Android浏览器用户和我们反馈,Fundebug在Android 4.4与 5.1.1浏览器上会报错,我们在1.8.0修复了这个问题,请大家及时更新插件。 通过优化Babel配置,我们兼容了一些低版本的浏览器: { "presets": [ [ "@babel/preset-env", { "targets": { "ie": 6, "android": 4, "ios": 8 } } ] ]}可知,Fundebug插件最低兼容IE 6,Android 4以及iOS 8。注意,我们保证Fundebug插件在这些浏览器下不会出错,但是并无意于为低版本浏览器提供全面的BUG监控服务。例如,我们的录屏功能仅支持一些高版本的浏览器,IE 6 ~ IE 10均不支持。 最后,感谢Fundebug用户闁鑅与疯狂紫萧的反馈。 参考Fundebug录屏插件更新至0.4.0,修复BUG,优化性能Fundebug文档 - JavaScript插件版本关于FundebugFundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有Google、360、金山软件、百姓网等众多品牌企业。欢迎大家免费试用! 版权声明转载时请注明作者Fundebug以及本文地址:https://blog.fundebug.com/2019/06/03/fundebug-javascript-1-8-0/

June 3, 2019 · 1 min · jiezi

BehaviorSubject与-Subject-不同之处

需求保持一个ShareMoudel不变,实现前台器具用户和计量机构注销功能 开始思路1.增加一个实体2.判断是器具用户登录,如果是则调用器具用户的注销,否则调用计量机构的注销(分别显示对应的菜单) 实现创建一个对象,订阅之前的$isLogin 如果是true表示已登录,然后调用对应注销方法,实现代码如下: // tobar数组的第一对象的位置,改变位置时需改变profileMenuIndex private profileMenuIndex = 0; tobars: Tobar[] = [ { title: 'Profile', class: 'fa fa-fw fa-user', state: false, onclickFn: () => { this.router.navigateByUrl('/main/personal'); } }, { title: 'Privacy', class: 'fa fa-fw fa-user-secret', state: true, onclickFn: () => { } }, { title: 'Settings', state: true, class: 'fa fa-fw fa-cog', onclickFn: () => { } }, { title: 'Logout', state: true, class: 'fa fa-fw fa-sign-out', onclickFn: () => { // 器具用户注销 this.unsubscribeMain = this.departmentService.$isLogin.subscribe((isInstrumentUserLogin) => { if (isInstrumentUserLogin) { this.departmentLogout(); } }); this.unsubscribeMain.unsubscribe(); // 计量机构注销 this.unsubscribeAdmin = this.systemService.$isLogin.subscribe((isAdminLogin) => { if (isAdminLogin) { this.logout(); } }); this.unsubscribeAdmin.unsubscribe(); } }, ]; ngOnInit(): void { this.departmentService.$isLogin.subscribe((isLogin) => { if (isLogin) { this.showProfileMenu(); } }); } // 改变state,true显示,false不显示 showProfileMenu(): void { this.tobars[this.profileMenuIndex].state = true; }然而并没有实现问题: 计量机构的注销并不起作用发现:我订阅的计量机构$isLogin不执行,所以说计量机构不能实现注销 ...

April 25, 2019 · 2 min · jiezi

在 Angular 8 中,我们可以期待些什么

转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。本文由葡萄城翻译并发布Angular 作为一款优秀的前端框架,自诞生之日起,就致力于面向前端开发者提供一整套全功能解决方案。与其他流行框架所追求的理念不同,Angular并非作为一款可以并入 Web 应用程序的轻量级框架而生,而是设计为:包含了一个完整的工作流,用于从项目创建开始,持续地维护并更新你的应用程序。2019对于 Angular来说意义非凡,因为其包含了Ivy 等功能的全新版Angular 8 将于本年内正式发布。尽管 Angular 技术团队仍没有对外公布 Angular 8 正式版发布日期,但其beta版在本月上线已几乎没有悬念。Angular 8的发布近在咫尺!那么,在 Angular 8 中,我们可以期待些什么?Angular 8 中有什么新功能尽管大多数开发者都在关注Ivy,但其实 Angular 8 中还是有很多值得称道的亮点:JavaScript中的差异化加载作为构建过程的一部分,Angular CLI生成的新应用程序现在将包含用于旧版JavaScript(ES5)和现代JavaScript(ES2015 +)的单独软件包。该包在客户端中实现差异化加载,以提高浏览器的加载速度和传输间隔时间(TTI)。这是一个好消息,意味着支持ES2015的浏览器将能够下载更小、更高效的应用程序包,而这些应用程序包的加载速度和渲染速度都比以往更快。Ivy (预览版)视图引擎抢先试用您可在应用程序中开始使用Ivy渲染器,并向Angular团队提供使用反馈,以便其及时做出优化和修复。Angular Router的向后兼容模式Angular 添加了向后兼容模式,以降低大型项目的升级成本。Angular Router将可以使用 $ route APIs 在现有的Angular应用程序中,选择延迟加载部分 Angular 1.x应用程序。在理想的情况下,我们甚至可以立即将Angular 1.x应用程序升级到Angular 2+。然而,现实并非如此。直到今天,还有大量传统的Angular应用程序仍在为企业提供服务。而这些企业没有选择升级的原因很简单:它们运行良好,并且在完成重写时无需太多投入。Angular 1.x的时代已经结束,之后不会有任何新的更新,直到2021年6月30日。因此,对于从事大型Angular 1.x应用程序开发的团队,现在正是选择升级的最佳时间。改良的Web Worker绑定方式Web Worker是编写主线程运行代码的好方法,可用于提高应用程序的速度和并发性。为了达到这一目标,Angular团队在 CLI 中添加了改良的 Web Worker绑定支持。可选的信息共享机制为了有效收集您的反馈,更好地建设 Angular社区,Angular团队在CLI中添加了可选的信息共享机制。在您同意的情况下,将开始收集您的命令行和构建速度等匿名信息。依赖关系更新与往常一样,Angular团队正在更新对TypeScript、RxJS和Node等工具的依赖关系,以便与 Angular 生态系统的其余部分保持同步。Angular Ivy 是什么?作为下一代 Angular 的视图引擎,Ivy的出现旨在彻底缩减代码尺寸并增强系统灵活性。与目前的Angular View Engine相比,Ivy具有以下优势:通过 Angular 编译器生成的代码现在将更容易让开发者阅读和理解项目重建时间将明显加快有效减少了负载大小,浏览器用于下载和解析应用程序的时间将更短更好的模板类型检查,以便您在项目构建初期,就可捕获更多BUG,以防止用户在运行时遇到它们除此之外,Angular Ivy与现有的Angular应用程序广泛兼容。因此,在理想的情况下,您将无需更改应用程序即可获得Ivy的全部支持。这也是为什么使用Angular 8和Ivy构建当前的Angular应用程序会更加便利。Ivy(预览版)可能存在的问题目前已知的是,Ivy 的国际版和Angular Universal版尚未完全兼容。当然,如果您在开发过程中遇到任何使用BUG,请立即向Angular技术团队反馈,以便他们能够及时解决问题并将您的建议作为 Ivy 正式版的一部分。如果您的Angular应用程序支持多语言或使用服务器端呈现数据,请不要指望它能与Ivy完美结合。除此之外,用户可能遇到的另一个问题就是Angular Material,目前来看,使用Angular Material的应用似乎与即将发布的Angular 8预览版Ivy不太匹配。Angular 8 对 Web Worker 的支持对于那些对Web Worker 还不熟悉的开发者来说,Web Worker标志着前端开发中的一项重要创新,在此之前,所有 Web 应用程序仅限于使用单线程。而随着 Web Worker 的出现,可以将CPU分配到单独的硬件线程中,使浏览器环境拥有多线程,从而提升项目开发效率。在Angular 8更新之前,使用 Web Worker需要注意的问题是:在worker中运行的代码不能与应用程序的其余部分位于同一JavaScript脚本文件中。它必须是分开的。因此,对于曾经希望借助Angular CLI等工具,自动将JavaScript文件拆分、绑定到更少文件夹下的效果往往不佳。而Angular 8的新特性之一便是改进了使用Angular CLI捆绑WebWorker的支持,这项改进意味着您将走向多并发、自动化的Web Worker之路。Angular 8 对 TypeScript 的支持关于 Angular 8 中的依赖更新,包括了对Angular依赖项和最新版本的更新,如RxJS和TypeScript等框架。这看起来似乎是一个微小的改进,但却同样受欢迎,特别是TypeScript部分。Angular 8 的性能提升虽然 Angular 8 带来了很多令人称赞的功能,但是真正促使我们升级的很大一部分原因取决于其性能的提升!为证实这一点,我们将对 Angular 7.2 和Angular 8.0.0-beta.7 进行全面对比。该性能测试是基于ng new创建的新应用程序运行,并使用ng build –prod构建的。测试本身使用了Chrome的审核标签完成,通过 “Applied Fast 3G,4X CPU Slowdown” 的设置来模拟在移动设备上运行。Angular 7.2使用Angular 7.2的正式版本生成main.js的大小为240KB。具体数据如下图:Angular 8.0.0-beta.7Angular 8 beta版本的main.js文件大小与Angular 7.2相同:240KB。由此可见,文件大小没有任何改变,但让我们对比一下性能数据:Angular 8 看起来更棒!相对于Angular 7.2,获得了相当不错的性能提升。于是,在 Angular 8 中,我们可以得到些什么正如我们所看到的,Angular 8的新增特性除 Ivy 之外并不是很亮眼,尽管这些特性非常好用,但对于大多数应用程序来说并不重要。基于这一点,您应该将应用程序升级到Angular 8,还是坚持使用Angular 7?毫无疑问,你应该升级它们。即便功能上没有任何大的重大更新,但通过 Angular 8 新增的差异化加载,您将获得显著的性能提升。更重要的是,升级到Angular 8将确保您的应用程序为Ivy做好准备,即便目前 Ivy只是Angular 8提供的一个可选预览。如果您的应用程序需要兼容 Ivy,那么最好从现在开始尝试。或者,您也可以选择一条更加快捷且简便的方式,比如使用一款相当成熟的商业化开发工具——WijmoJS。这样,您就不必考虑项目中前端框架的兼容性和版本更新问题,因为它不但同时兼容了Angular、React、Vue、TypeScript和Ionic 等框架,还时刻紧随技术潮流,第一时间保持对框架最新版本的全面支持。本文是由葡萄城技术开发团队发布,转载请注明出处:葡萄城官网了解可嵌入您系统的在线 Excel,请前往SpreadJS纯前端表格控件与开发人员分享前端技术趋势、交流心得技巧,请加入葡萄城“前端技术交流群”(QQ群:720389894) ...

April 15, 2019 · 1 min · jiezi

angular 实现同步验证器跨字段验证

需求:设置成绩占比时,如果总占比不是100%,则无法通过验证。分析:需求很简单,只需要写一个验证器即可,由于不需要访问后台,且验证器与三个字段有关,所以是同步跨字段验证。实现验证器首先,去官网上找示例代码:export const identityRevealedValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => { const name = control.get(’name’); const alterEgo = control.get(‘alterEgo’); return name && alterEgo && name.value === alterEgo.value ? { ‘identityRevealed’: true } : null;};解释:这个身份验证器实现了 ValidatorFn 接口。它接收一个 Angular 表单控件对象作为参数,当表单有效时,它返回一个 null,否则返回 ValidationErrors 对象。从上可知,所谓跨字段,就是从验证表单单个控件formControl变成了验证整个表单formGroup了,而formGroup的每个字段就是formControl。明白了这个原理,就是根据需求进行改写: // 判断总占比是否等于100 export const scoreWeightSumValidator: ValidatorFn = (formGroup: FormGroup): ValidationErrors | null => { const sumLegal = formGroup.get(‘finalScoreWeight’) .value + formGroup.get(‘middleScoreWeight’) .value + formGroup.get(‘usualScoreWeight’) .value === 100; // 如果是,返回一个 null,否则返回 ValidationErrors 对象。 return sumLegal ? null : {‘scoreWeightSum’: true};};到此,验证器已经写完。添加到响应式表单给要验证的 FormGroup 添加验证器,就要在创建时把一个新的验证器传给它的第二个参数。 ngOnInit(): void { this.scoreSettingAddFrom = this.fb.group({ finalScoreWeight: [null, [Validators.required, scoreWeightValidator]], fullScore: [null, [Validators.required]], middleScoreWeight: [null, [Validators.required, scoreWeightValidator]], name: [null, [Validators.required]], passScore: [null, [Validators.required]], priority: [null, [Validators.required]], usualScoreWeight: [null, [Validators.required, scoreWeightValidator]], }, {validators: scoreWeightSumValidator});}添加错误提示信息我使用的是ng-zorro框架,当三个成绩占比均输入时,触发验证 <nz-form-item nz-row> <nz-form-control nzValidateStatus=“error” nzSpan=“12” nzOffset=“6”> <nz-form-explain *ngIf=“scoreSettingAddFrom.errors?.scoreWeightSum && scoreSettingAddFrom.get(‘middleScoreWeight’).dirty && scoreSettingAddFrom.get(‘finalScoreWeight’).dirty && scoreSettingAddFrom.get(‘usualScoreWeight’).dirty”>成绩总占比需等于100%! </nz-form-explain> </nz-form-control></nz-form-item>效果:总结总的来说这个验证器实现起来不算很难,就是读懂官方文档,然后根据自己的需求进行改写。参考文档:angular表单验证 跨字段验证 ...

April 10, 2019 · 1 min · jiezi

angualr2中管道@pipe的简单使用

需求使用过滤,实现前台查询。在Angular2中有各种各样的类修饰器,比如:@App,@Component,@input,@output等等,最近需要用到数据过滤用到了管道@Pipe,下面记录简单@Pipe的简单用法。什么是pipe?就是管道,简单来说,管道的作用就是传输。并且不同的管道具有不同的作用。(其实就是处理数据)。angualr中自带的pipe函数管道功能DatePipe日期管道,格式化日期JsonPipe将输入数据对象经过JSON.stringify()方法转换后输出对象的字符串UpperCasePipe将文本所有小写字母转换成大写字母LowerCasePipe将文本所有大写字母转换成小写字母DecimalPipe将数值按照特定的格式显示文本CurrentcyPipe将数值进行货币格式化处理SlicePipe将数组或者字符串裁剪成新子集PercentPipe将数值转百分比格式pipe用法{{ 输入数据 | 管道 : 管道参数}} (其中‘|’是管道操作符)链式管道 {{ 输入数据 | date | uppercase}}详细内容请参考angualr官方文档Angular-pipepipe的基本语法import { Pipe, PipeTransform } from ‘@angular/core’;@Pipe({ name: ‘pipe’ // 使用时的名称 pure: Boolean // 纯管道与非纯管道(默认为纯管道,false时为非纯管道)})export class TestPipe implements PipeTransform { /** * @param value 待处理的数据 * @param args 附加参数 * @return 处理完成的数据 / transform(value: any, …args: any[]): any { return value; }}我们大多数使用的是纯管道,对与非纯管道也不是太理解,可能以后用到了就明白了;官方是这样解释非纯管道的 Angular executes an impure pipe during every component change detection cycle. An impure pipe is called often, as often as every keystroke or mouse-move.实现代码import { Pipe, PipeTransform } from ‘@angular/core’;import { Host } from ‘../../entity/Host’;/* * 过滤:按计算机名称进行查询 */@Pipe({ name: ‘hostPipe’ })export class HostPipe implements PipeTransform { hostList: Array<Host>; transform(value: Host[], hostName: string): any { // 如果Value为null,则不执行 if (!value) { return; } // 如果hostName为undefined,则返回value if (!hostName) { return value; } this.hostList = []; value.forEach((host) => { // 查询与hostName相同的 const index = host.name.indexOf(hostName); // 如果不是-1,则相同 if (index !== -1) { this.hostList.push(host); } }); return this.hostList; }}效果 ...

March 15, 2019 · 1 min · jiezi

前台Ng-Alain

对与Ng-Alain这个前段框架,用了也有一短时间了,对于框架而言确实时很强大,很好,但对于我们而言文档少,就寸步难行,边研究编写,时不时的就在坑里爬半天,还不一定爬出来。问题描述点击多选框时,选择是正确的,但是点击表格的其他地方值就变成了null,感到很奇怪,发现官方的就正常,不会出现我这种情况。想法首先想到的是去看文档,但是很遗憾,没有找到文档还好有github可以看到示例代码,比较哪里与官方的不一样。解决发现官方上的 (change)事件是这样写的:stChange(e: STChange) { switch (e.type) { case ‘checkbox’: this.selectedRows = e.checkbox; this.totalCallNo = this.selectedRows.reduce((total, cv) => total + cv.callNo, 0); this.cdr.detectChanges(); break; case ‘filter’: this.getData(); break; } }思考:为什么要这样写,这样写的原因是什么?在控制台打印了一下e,发现原来鼠标点击checkbox和点击表格还有在表格中双击所触发的事件不同:修改后: public selectHost(e: STChange) { switch (e.type) { // 如果是多选框事件,选中主机 case ‘checkbox’: this.selectedHosts = e.checkbox; } }完美,没有问题!!!总结在需要这个样式的时候就Ctrl+C、Ctrl+V过来了也没有多看,没用的代码就直接删掉了,但是在发现问题是往往可能是你感觉没用的哪些代码,所有在使用样式的时候大概看一下,这次没用到下次就可能用到,要多看,多总结,到在用的时候就会顺手许多。

March 8, 2019 · 1 min · jiezi

【Angular】Angula6中的组件通信

Angula6_组件通信本文主要介绍 Angular6 中的组件通信一、父子组件通信1.1 父组件向子组件传递信息方法一 在父组件上设置子组件的属性父组件绑定信息<app-child childTitle=“可设置子组件标题”></app-child>子组件接收消息import { Component, OnInit, Input } from ‘@angular/core’;@Input childTitle: string;方法二 父组件调用子组件的方法父组件触发消息<app-child #child></app-child> <button (click)=“child.childPrint()"></button>子组件接收消息childPrint() { alert(“来自子组件的打印”);}1.2 子组件向父组件传递信息方法一 使用 EventEmitter子组件使用 EventEmitter 传递消息import { Component, OnInit, Output, EventEmitter } from ‘@angular/core’;…@Output() initEmit = new EventEmitter<string>();ngOnInit() { this.initEmit.emit(“子组件初始化成功”);}…父组件接收消息<app-child (initEmit)=“accept($event)"></app-child>accept(msg:string) { alert(msg);}方法二 使用 ViewChild子组件提供传递参数的函数sendInfo() { return ‘Message from child 1.’;}父组件使用 ViewChild 触发并接收信息<button (click)=“getInfo()">获取子组件1号的信息</button><h2>{{ info }}</h2>import { Component, OnInit, ViewChild } from ‘@angular/core’;…@ViewChild(ChildFirstComponent) private childcomponent: ChildFirstComponent;getInfo() { this.info = this.childcomponent.sendInfo();}二、非父子组件通信方法一 service缺点:需要双向的触发(发送信息 / 接收信息)service.tsimport { Component, Injectable, EventEmitter } from “@angular/core”;@Injectable()export class myService { public info: string = “”; constructor() {}}组件 1 向 service 传递信息import { myService } from ‘../../service/myService.service’;…constructor( public service: myService) { }changeInfo() { this.service.info = this.service.info + “1234”;}…组件 2 从 service 获取信息import { myService } from ‘../../service/myService.service’;…constructor( public service: myService) { }showInfo() { alert(this.service.info);}…方法二 使用 BehaviorSubject优点:真正的发布订阅模式,当数据改变时,订阅者也能得到响应serviceimport { BehaviorSubject } from ‘rxjs’;…public messageSource = new BehaviorSubject<string>(‘Start’);changemessage(message: string): void { this.messageSource.next(message);}组件调用 service 的方法传信息和接收信息changeInfo() { this.communication.changemessage(‘Message from child 1.’);}ngOnInit() { this.communication.messageSource.subscribe(Message => { window.alert(Message); this.info = Message; });}三、其他的通信方式路由传值cookie、session、storage参考文献《Angular6.x 学习笔记——组件详解之组件通讯》《angular6 组件间的交流方式》 ...

January 31, 2019 · 1 min · jiezi

【Angular6+】属性及样式绑定

Angular6_属性及样式绑定Angular 通过 [] 来绑定数值、变量或者表达式,这种绑定是单向数据绑定。属性绑定属性绑定分为两种Property元素的常规属性,比如 src、disabled 等<img [src]=“heroImageUrl” /><button [disabled]=“isUnchanged”>Cancel is disabled</button><div [ngClass]=“classes”>[ngClass] binding to the classes property</div><app-hero-detail [hero]=“currentHero”></app-hero-detail>Attribute元素的非常规属性,比如 colspan 等<tr> <td [attr.colspan]=“1 + 1”>One-Two</td></tr>CSS 类绑定借助 CSS 类绑定,可以从元素的 class attribute 上添加和移除 CSS 类名。<!– 这是一个或者全有或者全无的替换型绑定。即当 badCurly 有值时 class 这个 attribute 设置的内容会被完全覆盖 –><div class=“bad curly special” [class]=“badCurly”>Bad curly</div><!– toggle the “special” class on/off with a property –><div [class.special]=“isSpecial”>The class binding is special</div>样式绑定样式绑定的语法与属性绑定类似。 但方括号中的部分不是元素的属性名,而由 style 前缀,一个点 (.)和 CSS 样式的属性名组成。 形如:[style.style-property]。<button [style.color]=“isSpecial ? ‘red’: ‘green’">Red</button><button [style.background-color]=“canSave ? ‘cyan’: ‘grey’">Save</button><button [style.font-size.em]=“isSpecial ? 3 : 1”>Big</button><button [style.font-size.%]="!isSpecial ? 150 : 50”>Small</button> ...

January 2, 2019 · 1 min · jiezi

ngrx

ngrx/store本文档会持续更新。StoreStrore是Angular基于Rxjs的状态管理,保存了Redux的核心概念,并使用RxJs扩展的Redux实现。使用Observable来简化监听事件和订阅等操作。在看这篇文章之前,已经假设你已了解rxjs和redux。官方文档 有条件的话,请查看官方文档进行学习理解。安装npm install @ngrx/storeTutorial下面这个Tutorial将会像你展示如何管理一个计数器的状态和如何查询以及将它显示在Angular的Component上。你可以通过StackBlitz来在线测试。1.创建actionssrc/app/counter.actions.tsimport {Action} from ‘@ngrx/store’;export enum ActionTypes { Increment = ‘[Counter Component] Increment’, Decrement = ‘[Counter Component] Decrement’, Reset = ‘[Counter Component] Reset’,}export class Increment implements Action { readonly type = ActionTyoes.Increment;}export class Decrement implements Action { readonly type = ActionTypes.Decrement;}export class Reset implements Action { readonly tyoe = Actiontypes.Reset;}2.定义一个reducer通过所提供的action来处理计数器state的变化。src/app/counter.reducer.tsimport {Action} from ‘@ngrx/store’;import {ActionTypes} from ‘./conter.actions’;export const initailState = 0;export function conterReducer(state = initialState, action: Action) { switch(action.type) { case ActionTypes.Increment: return state + 1; case ActionTypes.Decrement: return state - 1; case ActionTypes.Reset: return 0; default: return state; }}3.在src/app/app.module.ts中导入 StoreModule from @ngrx/store 和 counter.reducerimport {StroeModule} from ‘@ngrx/store’;import {counterReducer} from ‘./counter.reducer’;4.在你的AppModule的imports array添加StoreModule.forRoot,并在StoreModule.forRoot中添加count 和 countReducer对象。StoreModule.forRoot()函数会注册一个用于访问store的全局变量。scr/app/app.module.tsimport { BrowserModule } from ‘@angular/platform-browser’;import { NgModule } from ‘@angular/core’; import { AppComponent } from ‘./app.component’; import { StoreModule } from ‘@ngrx/store’;import { counterReducer } from ‘./counter.reducer’;@NgModule({ declaration: [AppComponent], imports: [ BrowserModule, StoreModule.forRoot({count: countReducer}) ], provoders: [], bootstrap: [AppComponent]})export class AppModule {}5.在app文件夹下新创建一个叫my-counter的Component,注入Store service到你的component的constructor函数中,并使用select操作符在state查询数据。更新MyCounterComponent template,添加添加、减少和重设操作,分别调用increment,decrement,reset方法。并使用async管道来订阅count$ Observable。src/app/my-counter/my-counter.component.html<button (click)=“increment()">Increment</button> <div>Current Count: {{ count$ | async }}</div><button (click)=“decrement()">Decrement</button><button (click)=“reset()">Reset Counter</button>更新MyCounterComponent类,创建函数并分发(dispatch)Increment,Decrement和Reset actions.import { Component } from ‘@angular/core’;import { Store, select } from ‘@ngrx/store’;import { Observable } from ‘rxjs’;import { Increment, Decrement, Reset } from ‘../counter.actions’;@Component({ selector: ‘app-my-counter’, templateUrl: ‘./my-counter.component.html’, styleUrls: [’./my-counter.component.css’],})export class MyCounterComponent ( count$: Observable<number>; constructor(private store: Stare<{count: number}>) { this.count$ = store.pipe(select(‘count’)); } increment() { this.store.dispatch(new Increment()); } decrement() { this.store.dispatch(new Decrement()); } reset() { this.store.dispatch(new Reset()); })6.添加MyCounter component到AppComponent template中<app-my-counter></app-my-counter>ActionsActions是NgRx的核心模块之一。Action表示在整个应用中发生的独特的事件。从用户与页面的交互,与外部的网络请求的交互和直接与设备的api交互,这些和更多的事件通过actions来描述。介绍在NgRx的许多地方都使用了actions。Actions是NgRx许多系统的输入和输出。Action帮助你理解如何在你的应用中处理事件。Action接口(Action interface)NgRx通过简单的interface来组成Action:interface Action { type: string;}这个interface只有一个属性:type,string类型。这个type属性将描述你的应用调度的action。这个类型的值以[Source]的形式出现和使用,用于提供它是什么类型的操作的上下文和action在哪里被调度(dispatched)。您可以向actions添加属性,以便为操作提供其他上下文或元数据。最常见的属性就是payload,它会添加action所需的所有数据。下面列出的是作为普通javascript对象编写的操作的示例:{ type: ‘[Auth API] Login Success’}这个action描述了调用后端API成功认证的时间触发。{ type: ‘[Login Page]’, payload: { username: string; password: string; }}这个action描述了用户在登录页面点击登录按钮尝试认证用户的时间触发。payload包含了登录页面提供的用户名和密码。编写 actions有一些编写actions的好习惯:前期——在开始开发功能之前编写编写action,以便理解功能和知识点分类——基于事件资源对actions进行分类编写更多——action的编写容易,所以你可以编写更多的actions,来更好的表达应用流程事件-驱动——捕获事件而不是命令,因为你要分离事件的描述和事件的处理描述——提供针对唯一事件的上下文,其中包含可用于帮助开发人员进行调试的更详细信息遵循这些指南可帮助您了解这些actions在整个应用程序中的流程。下面是一个启动登陆请求的action示例:import {} from ‘@ngrx/store’;export class Login Implements Action { readonly type = ‘[Login Page] Login’ constructor(public: payload: {username: string, password: string}){}}action编写成类,以便在dispatched操作时提供类型安全的方法来构造action。Login action 实现(implements) Action interface。在示例中,payload是一个包含username和password的object,这是处理action所需的其他元数据.在dispatch时,新实例化一个实例。login-page.component.tsclick(username: string, password: string) { store.dispatch(new Login({username:username, password: password}))}Login action 有关于action来自于哪里和事件发生了什么的独特上线文。action的类型包含在[]内类别用于对形状区域的action进行分组,无论他是组件页面,后端api或浏览器api类别后面的Login文本是关于action发生了什么的描述。在这个例子中,用户点击登录页面上的登录按钮来通过用户名密码来尝试认证。创建action unionsactions的消费者,无论是reducers(纯函数)或是effects(带副作用的函数)都使用actions的type来确定是否要执行这个action。在feature区域,多个actions组合在一起,但是每个action都需要提供自己的type信息。看上一个Login action 例子,你将为action定义一些额外的信息。import {Action} from ‘@ngrx/store’;export enum ActionTypes { Login = ‘[Login Page] Login’;}export class Login Implememts Action { readonly type = ActionTypes.Login; constructor(public paylad: {username: string, password: string})}export type Union = Login;将action type string放在enum中而不是直接放在class内。此外,还会使用Union类去导出Loginclass.ReducersNgRx中的Reducers负责处理应用程序中从一个状态到下一个状态的转换。Reducer函数从action的类型来确定如何处理状态。介绍Reducer函数是一个纯函数,函数为相同的输入返回相同的输出。它们没有副作用,可以同步处理每个状态转化。每个reducer都会调用最新的action,当前状态(state)和确定是返回最新修改的state还是原始state。这个指南将会向你展示如何去编写一个reducer函数,并在你的store中注册它,并组成独特的state。关于reducer函数每一个由state管理的reducer都有一些共同点:接口和类型定义了state的形状参数包含了初始state或是当前state、当前actionswitch语句下面这个例子是state的一组action,和相对应的reducer函数。首先,定义一些与state交互的actions。scoreboard-page.actions.tsimport {Action} from ‘@ngrx/store’;export enum Actiontypes { IncrementHome = ‘[Scoreboard Page] Home Score’, IncrementAway = ‘[Scoreboard Page] Away Score’, Reset = ‘[Scoreboard Page] Score Reset’,}export class IncrementHome implements Action { readonly type = ActionTypes.IncrementHome;}export class IncrementAway implements Action { readonly type = ActionTypes.IncrementAway;}export class Reset implements Action { readonly type = ActionTypes.Reset; constructor(public payload: {home: number, away: number}) {}}export type ActionsUnion = IncrementHome | IncrementAway | Reset;接下来,创建reducer文件,导入actions,并定义这个state的形状。定义state的形状每个reducer函数都会监听actions,上面定义的scorebnoard actions描述了reducer处理的可能转化。导入多组actions以处理reducer其他的state转化。scoreboard.reducer.tsimport * as Scoreboard from ‘../actions/scoreboard-page.actions’;export interface State { home: number; away: number;}根据你捕获的内容来定义state的形状,它是单一的类型,如number,还是一个含有多个属性的object。设置初始state初始state给state提供了初始值,或是在当前state是undefined时提供值。您可以使用所需state属性的默认值设置初始state。创建并导出变量以使用一个或多个默认值捕获初始state。scoreboard.reducer.tsexport const initialState: Satate = { home: 0, away: 0,};创建reducer函数reducer函数的职责是以不可变的方式处理state的更变。定义reducer函数来处理actions来管理state。scoreboard.reducer.tsexport function reducer { satate = initialState, action: Scoreboard.ActionsUnion}: State { switch(action.type) { case Scoreboard.ActionTypes.IncrementHome: { return { …state, home: state.home + 1, } } case Scoreboard.ActionTypes.IncrementAway: { return { …state, away: state.away + 1, } } case Scoreboard.ActionTypes.Reset: { return action.payload; } default: { return state; } }}Reducers将switch语句与TypeScript在您的actions中定义的区分联合组合使用,以便在reducer中提供类型安全的操作处理。Switch语句使用type union来确定每种情况下正在使用的actions的正确形状。action的types定在你的action在你的reducer函数的case语句。type union 也约束你的reducer的可用操作。在这个例子中,reducer函数处理3个actions:IncrementHome,IncrementAway,Reset。每个action都有一个基于ActionUnion提供的强类型。每个action都可以不可逆的处理state。这意味着state更变不会修改源state,而是使用spread操作返回一个更变后的新的state。spread语法从当前state拷贝属性,并创建一个新的返回。这确保每次更变都会有新的state,保证了函数的纯度。这也促进了引用完整性,保证在发生状态更改时丢弃旧引用注意:spread操作只执行浅复制,不处理深层嵌套对象。您需要复制对象中的每个级别以确保不变性。有些库可以处理深度复制,包括lodash和immer。当action被调度时,所有注册过的reducers都会接收到这个action。通过switch语句确定是否处理这个action。因为这个原因,每个switch语句中总是包含default case,当这个reducer不处理action时,返回提供的state。注册root statestate在你的应用中定义为一个large object。注册reducer函数。注册reducer函数来管理state的各个部分中具有关联值的键。使用StoreModule.forRoot()函数和键值对来定义你的state,来在你的应用中注册一个全局的Store。StoreModule.forRoot()在你的应用中注册一个全局的providers,将包含这个调度state的action和select的Store服务注入到你的component和service中。app.module.tsimport {NgModule} from ‘@angular/core’;import {StoreModule} form ‘@ngrx/store’;import {scoreboardReducer} from ‘./reducers/scoreboard.resucer’;@NgModule({ imports: [StoreModule.forRoot({game: scoreboardReducer})],})export class AppModule {}使用StoreModule.forRoot()注册states可以在应用启动时定义状态。通常,您注册的state始终需要立即用于应用的所有区域。注册形状state形状states的行为和root state相同,但是你在你的应用中需要定义具体的形状区域。你的state是一个large object,形状state会在这个object中以键值对的形式注册。下面这个state object的例子,你将看到形状state如何以递增的方式构建你的state。让我们从一个空的state开始。app.module.ts@NgModule({ imports: [StoreModule.forRoot({})],})export class AppModule {}这里在你的应用中创建了一个空的state{}现在使用scoreboardreducer和名称为ScoreboarModule的形状NgModule注册一个额外的state。scoreboard.module.tsimport { NgModule } from ‘@angular/core’;import { StoreModule } from ‘@ngrx/store’;import { scoreboardReducer } from ‘./reducers/scoreboard.reducer’;@NgModule({ imports: [StoreModule.forFeature(‘game’, scoreboardReducer)],})export class ScoreboardModule {}添加ScoreboardModule到APPModule。app.module.tsimport { NgModule } from ‘@angular/core’;import { StoreModule } from ‘@ngrx/store’;import { ScoreboardModule } from ‘./scoreboard/scoreboard.module’;@NgModule({ imports: [StoreModule.forRoot({}), ScoreboardModule],})export class AppModule {}每一次ScoreboardModule被加载,这个game将会变为这个object的一个属性,并被管理在state中。{ game: { home: 0, away: 0}}形状state的加载是eagerly还是lazlly的,取决于你的应用。可以使用形状状态随时间和不同形状区域构建状态对象。selectSelector是一个获得store state的切片的纯函数。@ngrx/store提供了一些辅助函数来简化selection。selector提供了很多对state的切片功能。轻便的记忆化组成的可测试的类型安全的当使用createSelector和createFeatureSelector函数时,@ngrx/store会跟踪调用选择器函数的最新参数。因为选择器是纯函数,所以当参数匹配时可以返回最后的结果而不重新调用选择器函数。这可以提供性能优势,特别是对于执行昂贵计算的选择器。这种做法称为memoization。使用selector切片stateindex.tsimport {createSelector} from ‘@ngrx/store’;export interface FeatureState { counter: number;}export interface AppSatte { feature: FeatureState;}export const selectFeature = (state: AppState) => state.feature;export const selectFeatureCount = createSelector( selectFeature, (state: FeatrureState) => state.counter)使用selectors处理多切片createSelector能够从基于同样一个state的几个切片state中获取一些数据。createSelector最多能够接受8个selector函数,以获得更加完整的state selections。在下面这个例子中,想象一下你有selectUser object 在你的state中,你还有book object的allBooks数组。你想要显示你当前用户的所有书。你能够使用createSelector来实现这些。如果你在allBooks中更新他们,你的可见的书将永远是最新的。如果选择了一本书,它们将始终显示属于您用户的书籍,并且在没有选择用户时显示所有书籍。结果将会是你从你的state中过滤一部分,并且他永远是最新的。import {createSelecotr} from ‘@ngrx/store’;export interface User { id: number; name: string;}export interface Book { id: number; userId: number; name: string;}export interface AppState { selectoredUser: User; allBooks: Book[];}export const selectUser = (state: AppSate) => state.selectedUser;export const SelectAllBooks = (state: AppState) => state.allBooks;export const SelectVisibleBooks = createSelector( selectUser, selectAllBooks, (selectedUser: User, allBooks: Books[]) => { if(selectedUser && allBooks) { return allBooks.filter((book: Book) => book.UserId === selectedUser.id); }else { return allBooks; } })使用selecotr props当store中没有一个适合的select来获取切片state,你可以通过selector函数的props。在下面的例子中,我们有计数器,并希望他乘以一个值,我们可以添加乘数并命名为prop:index.tsexport const getCount = createSelector( getCounterValue, (counter, props) => counter * props.multiply);在这个组件内部,我们定义了一个props。ngOnInit() { this.counter = this.store.pipe(select(formRoot.getCount, {multiply: 2}));}记住,selector只将之前的输入参数保存在了缓存中,如果你用另一个乘数来重新使用这个selector,selector总会去重新计算它的值,这是因为他在接收两个乘数。为了正确地记忆selector,将selector包装在工厂函数中以创建选择器的不同实例index.tsexport const getCount = () => { createSelector( (state, props) => state.counter[props.id], (counter, props) => counter * props* multiply );}组件的selector现在调用工厂函数来创建不同的选择器实例: ngOnInit() { this.counter2 = this.store.pipe(select(fromRoot.getCount(), { id: ‘counter2’, multiply: 2 })); this.counter4 = this.store.pipe(select(fromRoot.getCount(), { id: ‘counter4’, multiply: 4 })); this.counter6 = this.store.pipe(select(fromRoot.getCount(), { id: ‘counter6’, multiply: 6 })); } ...

December 29, 2018 · 4 min · jiezi

ionic echarts引入和使用及报错解决

1.项目中安装echartscnpm install echarts –save但是ionic项目依赖于angular和typeScript,所以再安装ts支持包cnpm install @types/echarts –save官网给出的一段建议:在 3.1.1 版本之前 ECharts 在 npm 上的 package 是非官方维护的,从 3.1.1 开始由官方 EFE 维护 npm 上 ECharts 和 zrender 的 package。所以还得安装zrendercnpm install zrender2.引入使用在所需页面的name.ts引入如下import echarts from ’echarts’;使用如下initCharts(){ echarts.init(this.myCharts.nativeElement).setOption({ xAxis: { type: ‘category’, data: [‘Mon’, ‘Tue’, ‘Wed’, ‘Thu’, ‘Fri’, ‘Sat’, ‘Sun’] }, yAxis: { type: ‘value’ }, series: [{ data: [820, 932, 901, 934, 1290, 1330, 1320], type: ’line’ }] },true);}调用constructor(public navCtrl: NavController, public navParams: NavParams) { console.log(echarts); this.initCharts();}页面显示找到name.html定义一个呈现图表的div如下<ion-header> <ion-navbar> <ion-title>charts</ion-title> </ion-navbar></ion-header><ion-content padding>// #charts通过ViewChild获取dom节点 <div class=“charts” #charts> </div></ion-content>在name.ts文件一如dom节点 @ViewChild(‘charts’) myCharts:ElementRef运行ionic serve报错如下图大概意思是说获取不到dom节点,也就是我们展示地图的那个div,打印了之后发现是null或者undefine。后面就打印了一下ionic页面的生命周期,测试了一下ionic页面的生命周期函数。生命周期函数 触发时刻 顺序constructor() 创建页面时触发 1ionViewDidLoad() 当页面加载时触发 2ionViewWillEnter() 当将要进入页面时触发 3ionViewDidEnter() 进入页面时触发 4在constructor() 和 ionViewDidLoad()initCharts()函数中是获取不到dom节点的,因为页面还没有加载完成,在ionViewWillEnter()和ionViewDidEnter()是能获取到dom节点的,顾名思义只有当页面加载完成时才能获取到dom节点,所以再在页面加载完成后的生命周期函数里可以获取到改为ionViewWillEnter(){ }或ionViewDidEnter(){ this.initCharts();}运行ionic serve 完美解决3.完整代码示例Charts.ts如下import {Component, ElementRef, ViewChild} from ‘@angular/core’;import { IonicPage, NavController, NavParams } from ‘ionic-angular’;import echarts from ’echarts’;/** * Generated class for the ChartsPage page. * * See https://ionicframework.com/docs/components/#navigation for more info on * Ionic pages and navigation. */@IonicPage()@Component({ selector: ‘page-charts’, templateUrl: ‘charts.html’,})export class ChartsPage { @ViewChild(‘charts’) myCharts:ElementRef constructor(public navCtrl: NavController, public navParams: NavParams) { console.log(echarts); } ionViewDidLoad() { // this.initCharts(); console.log(‘ionViewDidLoad ChartsPage’); } // ionViewDidEnter(){ // this.initCharts(); // } ionViewWillEnter(){ this.initCharts(); } initCharts(){ console.log(document.getElementById(‘charts’)); echarts.init(this.myCharts.nativeElement).setOption({ xAxis: { type: ‘category’, data: [‘Mon’, ‘Tue’, ‘Wed’, ‘Thu’, ‘Fri’, ‘Sat’, ‘Sun’] }, yAxis: { type: ‘value’ }, series: [{ data: [820, 932, 901, 934, 1290, 1330, 1320], type: ’line’ }] },true); }}charts.html<!– Generated template for the ChartsPage page. See http://ionicframework.com/docs/components/#navigation for more info on Ionic pages and navigation.–><ion-header> <ion-navbar> <ion-title>charts</ion-title> </ion-navbar></ion-header><ion-content padding> <div class=“charts” #charts> </div></ion-content>charts.scsspage-charts { .charts{ height: 500px; }} ...

December 29, 2018 · 2 min · jiezi

教你如何在@ViewChild查询之前获取ViewContainerRef

原文:https://blog.angularindepth.c…作者:Max Koretskyi译者:而井【翻译】教你如何在@ViewChild查询之前获取ViewContainerRef在我最新的一篇关于动态组件实例化的文章《在Angular中关于动态组件你所需要知道的》中,我已经展示了如何将一个子组件动态地添加到父组件中的方法。所有动态的组件通过使用ViewContainerRef的引用被插入到指定的位置。这个引用通过指定一些模版引用变量来获得,然后在组件中使用类似ViewChild的查询来获取它(模版引用变量)。在此快速的复习一下。假设我们有一个父组件App,并且我们需要将子组件A插入到(父组件)模版的指定位置。在此我们会这么干。组件A我们来创建组件A@Component({ selector: ‘a-comp’, template: &lt;span&gt;I am A component&lt;/span&gt; ,})export class AComponent {}App根模块然后将(组件A)它在declarations和entryComponents中进行注册:@NgModule({ imports: [BrowserModule], declarations: [AppComponent, AComponent], entryComponents: [AComponent], bootstrap: [AppComponent]})export class AppModule {}组件App然后在父组件App中,我们添加创建组件A实例和插入它(到指定位置)的代码。@Component({ moduleId: module.id, selector: ‘my-app’, template: &lt;h1&gt;I am parent App component&lt;/h1&gt; &lt;div class="insert-a-component-inside"&gt; &lt;ng-container #vc&gt;&lt;/ng-container&gt; &lt;/div&gt; ,})export class AppComponent { @ViewChild(‘vc’, {read: ViewContainerRef}) vc: ViewContainerRef; constructor(private r: ComponentFactoryResolver) {} ngAfterViewInit() { const factory = this.r.resolveComponentFactory(AComponent); this.vc.createComponent(factory); }}在plunker中有可以运行例子(译者注:这个链接中的代码已经无法运行,所以译者把代码整理了一下,放到了stackblitz上了,可以点击查看预览)。如果有什么你不能理解的,我建议你阅读我一开始提到过的文章。使用上述的方法是正确的,也可以运行,但是有一个限制:我们不得不等到ViewChild查询执行后,那时正处于变更检测期间。我们只能在ngAfterViewInit生命周期之后来访问(ViewContainerRef的)引用。如果我们不想等到Angular运行完变更检测之后,而是想在变更检测之前拥有一个完整的组件视图呢?我们唯一可以做到这一步的就是:用directive指令来代替模版引用和ViewChild查询。使用directive指令代替ViewChild查询每一个指令都可以在它的构造器中注入ViewContainerRef引用。这个将是与一个视图容器相关的引用,而且是指令的宿主元素的一个锚地。让我们声明这样一个指令:import { Directive, Inject, ViewContainerRef } from ‘@angular/core’;@Directive({ selector: ‘[app-component-container]’,})export class AppComponentContainer { constructor(vc: ViewContainerRef) { vc.constructor.name === “ViewContainerRef_”; // true }}我已经在构造器中添加了检查(代码)来保证视图容器在指令实例化的时候是可用的。现在我们需要在组件App的模版中使用它(指令)来代替#vc模版引用:<div class=“insert-a-component-inside”> <ng-container app-component-container></ng-container></div>如果你运行它,你会看到它是可以运行的。好的,我们现在知道在变更检查之前,指令是如何访问视图容器的了。现在我们需要做的就是把组件传递给它(指令)。我们要怎么做呢?一个指令可以注入一个父组件,并且直接调用(父)组件的方法。然而,这里有一个限制,就是组件不得不要知道父组件的名称。或者使用这里描述的方法。一个更好的选择就是:用一个在组件及其子指令之间共享服务,并通过它来沟通!我们可以直接在组件中实现这个服务并将其本地化。为了简化(这一操作),我也将使用定制的字符串token:const AppComponentService= { createListeners: [], destroyListeners: [], onContainerCreated(fn) { this.createListeners.push(fn); }, onContainerDestroyed(fn) { this.destroyListeners.push(fn); }, registerContainer(container) { this.createListeners.forEach((fn) => { fn(container); }) }, destroyContainer(container) { this.destroyListeners.forEach((fn) => { fn(container); }) }};@Component({ providers: [ { provide: ‘app-component-service’, useValue: AppComponentService } ], …})export class AppComponent {}这个服务简单地实现了原始的发布/订阅模式,并且当容器注册后会通知订阅者们。现在我们可以将这个服务注入AppComponentContainer指令之中,并且注册(指令相关的)视图容器了:export class AppComponentContainer { constructor(vc: ViewContainerRef, @Inject(‘app-component-service’) shared) { shared.registerContainer(vc); }}剩下唯一要做的事情就是当容器注册时,在组件App中进行监听,并且动态地创建一个组件了:export class AppComponent { vc: ViewContainerRef; constructor(private r: ComponentFactoryResolver, @Inject(‘app-component-service’) shared) { shared.onContainerCreated((container) => { this.vc = container; const factory = this.r.resolveComponentFactory(AComponent); this.vc.createComponent(factory); }); shared.onContainerDestroyed(() => { this.vc = undefined; }) }}在plunker中有可以运行例子(译者注:这个链接中的代码已经无法运行,所以译者把代码整理了一下,放到了stackblitz上了,可以点击查看预览)。你可以看到,已经没有ViewChild查询(的代码)了。如果你新增一个ngOnInit生命周期,你将看到组件A在它(ngOnInit生命周期)触发前就已经渲染好了。RouterOutlet也许你觉得这个办法十分骇人听闻,其实不是的,我们只需看看Angular中router-outlet指令的源代码就好了。这个指令在构造器中注入了viewContainerRef,并且使用了一个叫parentContexts的共享服务在路由器配置中注册自身(即:指令)和视图容器:export class RouterOutlet implements OnDestroy, OnInit { … private name: string; constructor(parentContexts, private location: ViewContainerRef) { this.name = name || PRIMARY_OUTLET; parentContexts.onChildOutletCreated(this.name, this); … } ...

December 26, 2018 · 1 min · jiezi

【教程】(Angular)模版引用变量的魔法

【翻译】【教程】模版引用变量的魔法原文链接:https://blog.angulartraining…. 作者:Alain Chautard译者:而井模版引用变量是个好东西,它允许Angular完成许多有用的事情。我经常称这个功能为“井号语法”,因为在模版中它依赖一个简单的井号来创建对一个元素(译者注:元素包括HTML元素和组件元素)的引用:<input #phone placeholder=“phone number”>上述的语法是如此的简洁:它创建了一个指向input元素的引用,这个引用稍后可以在我的模版中使用。需要注意的是,这个(引用)变量的作用域是它所定义的整个HTML模版(的范围)(译者注:即在定义这个引用变量的HTML模版中都可以访问这个变量)。例如,这里就是我如何用这个引用来获取输入框的值(的例子):<!– phone指向输入框元素 –> <button (click)=“callPhone(phone.value)">Call</button>注意那个phone(变量)指向了input的HTMLElement对象实例。所以phone(变量)持有了(相应)HTMLElement(实例对象)的任何属性和方法,如id、name、innerHTML、value等。上述是一种避免使用ngModel或其他数据绑定的好方法,(因为)这种方法在校验方面上不需要写太多代码。在组件上也奏效吗?答案就是可以奏效!假设我们有HelloWorldComponent如下:@Component({ selector: ‘app-hello’, template: &lt;div&gt; &lt;h2&gt;Hello {{name}}&lt;/h2&gt; &lt;/div&gt; })export class HelloComponent { name = ‘Angular’;}现在按照如下代码,我们使用了“井号语法”得到了组件的引用:<app-hello #helloComp></app-hello>它(模版引用变量)一个最好的地方就是我们可以获取实际上的组件实例对象HelloWorldComponent。所以我们可以访问这个组件的任何方法或属性,即使他们(的权限)是声明为私有或保护的,多么令人惊喜:<app-hello #helloComp></app-hello><!– 下面这个表达式将会显示(文本)“Angular” –>{{helloComp.name}}我们不仅可以通过这种语法来读取一个组件的数据,而且也能修改它。对指令也奏效吗?当然(可以),不过这里需要进一步了解它(模版引用变量)。大部分的指令将会被作为(译者注:HTML或组件标签)的属性来使用,这意味着我们无法在那里真正应用“井号语法”,除非我们使用相同的语法进行扭转:<form (ngSubmit)=“onSubmit(myForm)” #myForm=“ngForm”>在上面的例子里,myForm是一个指向(应用于表单的)ngForm指令的引用。现在如果你细看上面的HTML元素,你可能会想:“等一下,那里并没有ngForm指令!我没有见过任何属性叫ngForm的!”,你(如果)这样想就对了。答案就在ngForm指令的源代码中:@Directive({ selector: ‘form:not([ngNoForm]):not([formGroup]),ngForm,[ngForm]’, … exportAs: ’ngForm’})看到那个指令的选择器的了没?它(指令)将应用于任何没有ngNoForm和formGroup属性的form表单元素之上。因此,ngForm指令将自动应用于我的form元素之上。第二个被注意到的趣事就是装饰器中的exportAs属性。它告诉Angular:“嘿,如果有人想用模版引用变量来指向这个指令,(那么指令的)名字就叫做ngForm”。现在我们已经知道它是如何运作的了。我们可以创建定制指令,并通过一个叫exportAs的来暴露该指令。译者附为了方便大家理解模版引用变量对指令的操控,我把相关链接的核心演示代码附在本文最后面。import {Component} from ‘@angular/core’;import {NgForm} from ‘@angular/forms’; @Component({ selector: ’example-app’, template: &lt;form #f="ngForm" (ngSubmit)="onSubmit(f)" novalidate&gt; &lt;input name="first" ngModel required #first="ngModel"&gt; &lt;input name="last" ngModel&gt; &lt;button&gt;Submit&lt;/button&gt; &lt;/form&gt; &lt;p&gt;First name value: {{ first.value }}&lt;/p&gt; &lt;p&gt;First name valid: {{ first.valid }}&lt;/p&gt; &lt;p&gt;Form value: {{ f.value | json }}&lt;/p&gt; &lt;p&gt;Form valid: {{ f.valid }}&lt;/p&gt; ,})export class SimpleFormComp { onSubmit(f: NgForm) { console.log(f.value); // { first: ‘’, last: ’’ } console.log(f.valid); // false }} ...

December 25, 2018 · 1 min · jiezi

问道Angular——Angular刷新当前页面

onSameUrlNavigation 从angular5.1起提供onSameUrlNavigation来支持路由重新加载。、 有两个值’reload’和’ignore’。默认为’ignore’ 定义当路由器收到一个导航到当前 URL 的请求时应该怎么做。 默认情况下,路由器将会忽略这次导航。但这样会阻止类似于 “刷新” 按钮的特性。 使用该选项可以配置导航到当前 URL 时的行为。使用配置onSameUrlNavigation@NgModule({ imports: [RouterModule.forRoot( routes, { onSameUrlNavigation: ‘reload’ } )], exports: [RouterModule]}) reload实际上不会重新加载路由,只是重新出发挂载在路由器上的事件。配置runGuardsAndResolvers runGuardsAndResolvers有三个值:paramsChange: 仅在路由参数更改时触发。如/reports/:id 中id更改paramsOrQueryParamsChange: 当路由参数更改或参训参数更改时触发。如/reports/:id/list?page=23中的id或page属性更改always :始终触发const routes: Routes = [ { path: ‘’, children: [ { path: ‘report-list’, component: ReportListComponent }, { path: ‘detail/:id’, component: ReportDetailComponent, runGuardsAndResolvers: ‘always’ }, { path: ‘’, redirectTo: ‘report-list’, pathMatch: ‘full’ } ] }];组件监听router.eventsimport {Component, OnDestroy, OnInit} from ‘@angular/core’;import {Observable} from ‘rxjs’;import {Report} from ‘@models/report’;import {ReportService} from ‘@services/report.service’;import {ActivatedRoute, NavigationEnd, Router} from ‘@angular/router’;@Component({ selector: ‘app-report-detail’, templateUrl: ‘./report-detail.component.html’, styleUrls: [’./report-detail.component.scss’]})export class ReportDetailComponent implements OnInit, OnDestroy { report$: Observable<Report>; navigationSubscription; constructor( private reportService: ReportService, private router: Router, private route: ActivatedRoute ) { this.navigationSubscription = this.router.events.subscribe((event: any) => { if (event instanceof NavigationEnd) { this.initLoad(event); } }); } ngOnInit() { const id = +this.route.snapshot.paramMap.get(‘id’); this.report$ = this.reportService.getReport(id); } ngOnDestroy(): void { // 销毁navigationSubscription,避免内存泄漏 if (this.navigationSubscription) { this.navigationSubscription.unsubscribe(); } } initLoad(e) { window.scrollTo(0, 0); console.log(e); }}☞☞☞问道Angular系列☜☜☜ ...

November 20, 2018 · 1 min · jiezi