关于angular:angular动态组件的使用demo

什么是动静组件?动静组件是指在运行时依据条件或用户操作动静加载并渲染的组件。这种技术容许应用程序依据须要动静地切换和加载不同的组件。举例来说,当用户在应用程序中进行了特定的操作,比方点击按钮或者抉择了某个选项,你能够依据这些操作加载不同的组件,而不用在应用程序初始化时将所有组件都加载进来。也就是说组件的模板不会永远是固定的。利用可能会须要在运行期间加载一些新的组件 实现动静组件在 Angular 中实现动静组件通常波及以下几个步骤: 创立动静组件:须要创立要动静加载的组件。这些组件与一般组件一样,但通常会用于展现特定的内容或性能。 创立容器组件:接下来,你须要创立一个容器组件,用于动静加载其余组件。这个容器组件应该蕴含一个指令或组件,用来标记动静加载组件的地位。 通过组件工厂加载组件:应用 Angular 提供的ComponentFactoryResolver 和 ViewContainerRef,你能够在容器组件中动静加载组件。首先,解析要加载的组件的工厂,而后应用工厂创立组件并将其增加到视图容器中。 Angular中应用动静组件时罕用的API1.ViewContainerRef:用于治理动静加载的组件的容器。它提供了一种将动态创建的组件插入到特定地位的办法,并提供了对插入组件的拜访和管制。 容器治理:ViewContainerRef 能够视作一个容器,用于动静加载和治理组件。 动静组件加载:通过 ViewContainerRef,能够在容器中动静地创立组件实例,并将其增加到容器中。 清空容器:ViewContainerRef 还提供了一个 clear() 办法,用于清空容器中的所有内容,能够在须要时用来移除已加载的组件。 2.ComponentFactoryResolver:容许在运行时动静加载组件,而不是在编译时将所有组件都蕴含在模板中。 @ViewChild('dynamicComponentContainer', { read: ViewContainerRef }) dynamicComponentContainer: ViewContainerRef;// 解析 MyComponent 的工厂const componentFactory = this.componentFactoryResolver.resolveComponentFactory(MyComponent); // 创立 MyComponent 的实例,并将其增加到视图容器中const componentRef = this.dynamicComponentContainer.createComponent(componentFactory);应用示例 创立两个不同的动态组件: import { Component } from '@angular/core';@Component({ selector: 'app-dynamic', template: '<p>组件一</p>',})export class DynamicComponent {}import { Component } from '@angular/core';@Component({ selector: 'app-dynamic-two', template: '<p>动静加载组件二</p>',})export class DynamicTwoComponent {}创立容器组件 export class AppComponent { @ViewChild('dynamicComponentContainer', { read: ViewContainerRef }) dynamicComponentContainer: ViewContainerRef; constructor(private componentFactoryResolver: ComponentFactoryResolver) {} loadComponent1() { this.loadComponent(DynamicComponent); } loadComponent2() { this.loadComponent(DynamicTwoComponent); } loadComponent(component: any) { this.dynamicComponentContainer.clear(); const componentFactory = this.componentFactoryResolver.resolveComponentFactory(component); this.dynamicComponentContainer.createComponent(componentFactory); }}<!--app.component.html--><div #dynamicComponentContainer></div><button (click)="loadComponent1()">加载组件一</button><button (click)="loadComponent2()">加载组件二</button>动静切换 ...

March 2, 2024 · 1 min · jiezi

关于angular:Angular-应用开发中-Injection-Token-的使用方法介绍

Angular是一个风行的前端JavaScript框架,它提供了一种弱小的形式来构建单页应用程序(SPA)。在Angular中,依赖注入(Dependency Injection,DI)是一项要害的性能,它容许咱们无效地管理应用程序中的依赖关系。Angular的依赖注入零碎应用InjectionToken来实现某些非凡的依赖注入需要。在本文中,我将具体解释InjectionToken的作用,并提供示例以阐明其在Angular利用中的理论用处。 什么是依赖注入?在深刻理解InjectionToken之前,让咱们首先理解什么是依赖注入。依赖注入是一种设计模式,它容许咱们将一个对象的依赖关系(例如,服务或配置)注入到另一个对象中,而不须要硬编码这些依赖关系。这样做的益处包含: 可维护性:通过将依赖关系注入到组件中,咱们能够轻松更改这些依赖项,而不用批改大量的代码。可测试性:咱们能够轻松地为组件提供模仿的依赖项,以进行单元测试,而无需理论创立这些依赖项的实例。松耦合:依赖注入帮忙咱们实现松耦合,使各个组件之间的关系更加灵便。在Angular中,依赖注入是内置的,Angular的依赖注入容器负责管理依赖项的创立和生命周期。 为什么须要InjectionToken?通常状况下,Angular的依赖注入零碎能够主动解析依赖项的类型并为其创立实例。例如,如果您须要在组件中应用一个服务,只需将该服务的类型申明为该组件的结构函数参数,Angular将会主动创立该服务的实例并注入到组件中,如下所示: import { Component } from '@angular/core';import { MyService } from './my-service';@Component({ selector: 'app-my-component', templateUrl: './my-component.html',})export class MyComponent { constructor(private myService: MyService) { // 应用myService }}然而,有时候咱们须要注入的依赖项不是一个类的实例,而是一个配置项、字符串、或其余非类的值。这就是InjectionToken的用武之地,它容许咱们将非类的值作为依赖项注入到组件或服务中。 InjectionToken的作用InjectionToken的作用是定义一个标识符,用于标识依赖项。它容许咱们将任何值注入到Angular组件或服务中,而不仅仅是类的实例。通常状况下,咱们会在应用程序中的某个中央创立InjectionToken,而后在须要注入该值的中央应用它。 以下是InjectionToken的次要作用: 唯一性标识:InjectionToken是一个惟一的标识符,确保依赖项的唯一性。这对于避免依赖项的混同或抵触十分重要。非类依赖注入:InjectionToken容许咱们注入任何值,而不仅仅是类的实例。这在配置、常量、字符串等场景中十分有用。提供器配置:通过提供器配置,咱们能够通知Angular如何为InjectionToken提供依赖项的实例。这使得咱们能够在不同的上下文中为InjectionToken提供不同的值。当初,让咱们通过一些示例具体阐明InjectionToken的用法和作用。 示例一:注入配置假如咱们有一个应用程序,它须要依据用户的首选语言加载不同的国际化配置。咱们能够应用InjectionToken来注入以后用户的首选语言配置。首先,咱们须要创立一个InjectionToken来示意这个配置: import { InjectionToken } from '@angular/core';export const LANG_CONFIG = new InjectionToken<string>('langConfig');下面的代码创立了一个名为LANG_CONFIG的InjectionToken,它示意一个字符串类型的依赖项,用于存储语言配置。 接下来,咱们能够在Angular模块中配置如何提供这个依赖项的实例: import { NgModule } from '@angular/core';import { LANG_CONFIG } from './config.tokens';@NgModule({ providers: [ { provide: LANG_CONFIG, useValue: 'en-US' // 默认语言配置 } ]})export class AppModule { }在下面的代码中,咱们在模块的提供器中配置了LANG_CONFIG的默认值为en-US。这意味着如果没有其余中央提供LANG_CONFIG的理论值,它将默认为en-US。 ...

September 24, 2023 · 2 min · jiezi

关于angular:关于-Angular-testing-开发包里-fakeAsync-测试工具的用法

@angular/core/testing 是 Angular 框架提供的一个测试模块,用于帮忙开发者编写单元测试和集成测试。其中的 fakeAsync 是一个十分有用的测试工具,它容许咱们编写异步代码的测试,同时在测试中模仿工夫的后退,使得测试用例的执行更加可控和可预测。 fakeAsync 函数是一个测试辅助函数,它能够包装一个测试函数,容许在其中应用虚构的时间轴来模仿异步操作。通过 tick 函数,咱们能够手动后退虚构工夫,触发异步操作的执行。这样,咱们能够在测试用例中编写看起来同步的代码,同时也可能测试异步操作的行为,例如期待一个 Promise 或 setTimeout 实现。 接下来,让咱们通过一个具体的示例来阐明 fakeAsync 的用法。 假如咱们有一个简略的 Angular 组件,其中有一个异步办法 fetchData,它通过 setTimeout 模仿了一个异步的数据获取操作。咱们心愿编写一个测试来确保 fetchData 办法在异步操作实现后可能正确更新组件的数据。 首先,让咱们创立这个组件: import { Component } from '@angular/core';@Component({ selector: 'app-data', template: '{{ data }}'})export class DataComponent { data: string = ''; fetchData(): void { setTimeout(() => { this.data = 'Fetched data'; }, 1000); }}当初,咱们须要编写测试用例来测试这个组件的异步操作。咱们将应用 fakeAsync 来模仿工夫的后退,并应用 tick 来手动触发异步操作的执行。 import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';import { DataComponent } from './data.component';describe('DataComponent', () => { let component: DataComponent; let fixture: ComponentFixture<DataComponent>; beforeEach(() => { TestBed.configureTestingModule({ declarations: [DataComponent] }); fixture = TestBed.createComponent(DataComponent); component = fixture.componentInstance; }); it('should update data after fetching', fakeAsync(() => { expect(component.data).toBe(''); component.fetchData(); expect(component.data).toBe(''); // 手动后退虚构工夫 tick(1000); expect(component.data).toBe('Fetched data'); }));});在这个测试用例中,咱们应用 fakeAsync 包装了一个测试函数。在这个测试函数中,咱们首先调用了 component.fetchData() 来触发异步操作的开始。而后,应用 expect 断言来验证 data 是否依然为空,因为异步操作尚未实现。接着,应用 tick(1000) 来后退虚构工夫,期待异步操作实现。最初,再次应用 expect 断言来验证 data 是否曾经被更新为 'Fetched data'。 ...

September 9, 2023 · 1 min · jiezi

关于angular:关于-Angular-eslintdisablenextline-注释的使用

在 Angular 开发中,开发者常常会应用 Lint 工具来放弃代码品质的一致性和规范性。其中,ESLint 是一个用于辨认和修复 JavaScript 代码问题的风行 Lint 工具,而 @typescript-eslint 则是一个专为 TypeScript 设计的插件,它为 ESLint 提供了在 TypeScript 代码中进行动态剖析和规定校验的性能。eslint-disable-next-line 是 ESLint 提供的一种正文模式,用于长期禁用特定行的代码查看。 首先,让咱们来解释这行正文的具体含意。// eslint-disable-next-line @typescript-eslint/unified-signatures 这行正文的作用是禁用下一行特定规定(@typescript-eslint/unified-signatures)的代码查看。通常状况下,ESLint 会依据预约义的规定对代码进行动态剖析,以确保代码的品质和一致性。但在某些状况下,可能须要临时敞开某些规定,以防止不必要的正告或谬误。这时,能够应用 eslint-disable-next-line 正文来达到这个目标。 接下来,让咱们通过一个例子具体阐明这行正文的作用。 假如咱们正在开发一个 Angular 利用,其中有一个名为 userService 的服务。在该服务中,咱们有一个办法 getUserInfo,用于获取用户的详细信息。咱们应用 TypeScript 为这个办法增加了重载,以反对不同的参数类型。 class UserService { // ... /** * 获取用户信息 * @param userId 用户ID */ getUserInfo(userId: string): UserInfo; getUserInfo(userEmail: string): UserInfo; getUserInfo(identifier: string): UserInfo { // 理论获取用户信息的逻辑 }}而后,咱们应用 ESLint 进行代码查看,以确保代码的品质。然而,ESLint 的某些规定可能会对咱们的代码产生误报或正告,尤其是在应用重载时。假如 ESLint 中有一个规定 @typescript-eslint/unified-signatures,该规定查看是否所有重载签名的返回类型都是统一的。 ...

September 9, 2023 · 1 min · jiezi

关于angular:实力入选移动安全产品及服务购买决策参考星阑守护数字生活

近期,国内权威平安调研机构GoUpSec公布《挪动平安产品及服务购买决策参考》(下称“报告”),报告通过深刻调研了国内出名挪动平安业余厂商,从产品性能、利用行业、胜利案例、安全策略等维度对各厂商挪动平安产品及服务进行调研理解,以期帮忙企业拨开挪动平安营销迷雾,进步市场能见度,全面理解潜在挪动平安策略合作伙伴。星阑科技凭借在挪动数据流动平安畛域翻新的产品及业余的服务,入选本次报告。 时代改革下的威逼降级报告指出,企业数字化转型、劳动力去中心化、挪动办公以及近程办公已成为常态。然而,随之而来的是越来越猖狂的网络立功,企业员工的隐衷、个人身份和特权拜访凭证面临着前所未有的危险。特地是挪动攻打变得更加致命和长于窃取特权拜访凭证,使得企业的网络安全领导者不得不面对这个残暴的事实:一旦挪动设施被侵入,整个企业的基础设施就会受到毁坏。在挪动平安畛域,只有100%的胜利或100%的失败两个选项,因而挪动平安危险正成为企业数字化转型中头等威逼。挪动设施作为个人隐私、身份/拜访权限和企业数据的交汇点,其所面临的攻击面十分宽泛。威逼来自于挪动利用/API、挪动硬件、云端和挪动/无线网络等多个维度。挪动利用/API,作为挪动设施的外围组成部分,常常成为黑客入侵的指标。通过歹意利用或者破绽利用,黑客能够获取到用户的敏感信息、个人隐私甚至特权拜访凭证。此外,挪动硬件也是一个容易被攻打的环节。黑客能够通过物理攻击或固件破绽入侵设施,窃取企业数据或篡改设施性能;或者通过无线劫持、中间人攻打或钓鱼等伎俩窃取用户信息或篡改通信内容。 解码威逼背地的明码AIGC时代利用都要构建于AI模型层和数据流动根底,大模型API变革商业逻辑,企业须要从新扫视数据智能化以及业务视角的平安运维,智能利用的迭代形式必须适应高速变动的市场环境,这意味着在DevOps的根底上技术团队须要进一步与业务团队的工作流进行联合、整体提效。随着BizDevOps方法论的逐渐遍及和实际,越来越多的企业运维和平安人员正踊跃转变他们在组织中的角色,从单纯的老本核心走向具备弱小能力的外围驱动力。他们以赋能业务为导向,从新布局数据管理策略,并利用智能化基础设施来提供全面的业务反对和增长能源。星阑科技作为一家以平安攻防技术为外围、数据智能为驱动的网络安全科技公司,领有一支深耕于信息安全钻研多年的技术团队,粗浅理解信息安全畛域的各种技术和产品,为解决各种场景下的数据流动平安问题,公司从攻防能力、大数据分析能力及云原生技术体系登程,提供全景化智能资产辨认、高级威逼检测、简单行为剖析等能力,构建数据流动Runtime Protection体系。星阑科技长期投入打造数据基座与智能化基座,以应用层根底数据为外围帮忙运维与平安团队构建多场景一体化监测计划。其中蕴含了先进的数据采集、大数据分析平台、AI技术,通过高度自动化的形式实现对海量数据的高效解决和智能化治理,不仅能够帮忙企业构建欠缺的数据流程和数据资产管理体系,还能通过深度开掘数据的潜在价值,为业务提供全新的商业洞察和决策反对。 萤火翻新智能,守护数字生存星阑科技自主翻新研发的数据安全产品“萤火”有不同利用场景的解决方案,适配服务器、容器集群、微服务架构以及云平台等多种架构。从资产-危险-事件-数据等多个维度实现对资产应用状况的实时监控、异样拜访的可视化。对流量中的数据接口主动聚合、分类、自定义标签化治理,并以可视化的形式便于管理员进行摸索和考察,从而实现业务数据安全对立监测。星阑科技的智能挪动数据安全解决方案精准符合客户的业务场景,通过构建平面纵深的平安防护体系,为挪动设施和挪动业务提供综合爱护及治理运维反对,实现卓越的挪动终端业务交付速度,始终保持全程平安可控。同时,通过深度交融零碎与业务,在云端、网络层和终端的数据整合和采集方面展现出卓越的能力,不仅收集用户所需的终端零碎数据、业务数据和平安数据,更将借助大数据技术与深度学习相结合,构建平安态势感知平台,帮忙用户进行深入分析和经营,以疏导安全策略和业务应用的继续优化和拓展。“萤火”能基于企业级大数据分析平台以及机器学习数据实体辨认算法,提供合乎ISO PI PII规范以及金融、政府、通信行业细分规范的敏感数据实体辨认与分类分级能力,并在此基础上针对歹意攻打事件、数据泄露事件、撞库攻打进行实时告警,使企业可能从容应对破绽攻打、黑客入侵、数据泄露以及账号滥用危险。通过AI驱动的数据模型剖析实现智能化的决策反对,辨别失常拜访与歹意攻打,自动更新进攻逻辑,实现毫秒级攻打拦挡,在不影响业务的可用性与性能的前提下,被动发现数据流攻打向量,解决用户业务资产梳理、破绽入侵、数据泄露问题。目前,星阑科技已为政府、金融、互联网、运营商等多个行业用户提供数据安全技术产品、解决方案和服务反对,取得市场与客户的高度认可。数字时代,平安为本。将来,星阑科技将持续以“技术研发能力”为后盾,保持“以用户需要为基本”,不断创新与迭代,推出适应新场景的平安产品和解决方案,切实解决用户在企业倒退过程中呈现的平安问题,为企业数字化转型保驾护航。 对于星阑星阑科技基于大数据分析及AI智能化技术体系,助力企业应答数字世界的平安危险。凭借继续翻新的平安理念和成果导向的攻防能力,星阑科技倒退成为国内数据智能、信息安全畛域的双料科技公司。为解决流动数据安全问题,星阑科技从数据攻防、数据分析、数据治理等不同场景登程,提供全景化数据流转监测、利用数据分析、API平安治理、高级威逼检测等解决方案,构建全链路流动数据保护体系。星阑科技外围产品——萤火流动数据分析平台,可针对用户异构、多模态数据提供危险监测、合规性治理、数据建模、用户追踪、行为关联、AI解释与推理等一体化数据分析能力,为企业的数据可观测性、数据合规、威逼监测、利用治理、业务洞察等场景提供全面反对。

August 31, 2023 · 1 min · jiezi

关于angular:Angular4学习笔记

内容投影ng-content ng-content是一个占位符,有些相似于router-outlet。 以前举の例子,父组件蕴含子组件都是间接指明子组件的selector,比方子组件的selector叫app-child,那么嵌入父组件时间接指明即可: <app-child></app-child> 这是很硬性的编码,而ng-content就是用来代替这种硬性编码的。 比方有个父组件这样定义: @Component({ selector: 'app-parent', template: ` <p>Parent Component</p><div style="background: cyan"> <ng-content></ng-content></div>`}) 它示意被放进的内容的背景色对立设置为cyan。 接下来就要将子组件放入父组件中,搁置的形式很简略,在根组件中将app-child插入到app-parent中即可: <app-parent> <app-child></app-child></app-parent> 多个投影 上例中只有一个投影,那么多个投影怎么办呢?<ng-content>反对一个select属性,能够让你在特定的中央投射具体的内容。该属性反对 CSS 选择器(my-element,.my-class,[my-attribute],...)来匹配你想要的内容。如果ng-content上没有设置select属性,它将接管全部内容,或接管不匹配任何其余 ng-content 元素的内容。 示例: http://www.developcls.com/qa/aa47c62122cd4fb7af18b0dc2131a3be.html比方父组件上有两个可投影的地位,一个背景为浅绿,一个为粉红: <div style="background: cyan"> <ng-content select="[name=child]"></ng-content></div><div style="background: pink"> <ng-content select=".red"></ng-content></div>此时能够在根组件上定义如下: <app-parent> <app-child class="red"></app-child> <app-child name="child"></app-child></app-parent>这样就能够对号入座了!ContentChild 了解了ng-content就能够应用@ContentChild装璜器来调用投影内容了,它和@ViewChild十分相似,就不多做介绍了,其异同点列举如下: 相同点 都是属性装璜器都有对应的复数模式装璜器:ContentChildren、ViewChildren都反对 Type|Function|string 类型的选择器不同点 ContentChild 用来从通过 Content Projection 形式 (ng-content) 设置的视图中获取匹配的元素ViewChild 用来从模板视图中获取匹配的元素在父组件的 ngAfterContentInit 生命周期钩子中能力胜利获取通过 ContentChild 查问的元素在父组件的 ngAfterViewInit 生命周期钩子中能力胜利获取通过 ViewChild 查问的元素参考 ng-content 中暗藏的内容Angular 2 ContentChild & ContentChildren

July 4, 2023 · 1 min · jiezi

关于angular:记录开发微信扫码登录过程遇到的问题功能尚未完成

前言尝试开发微信扫码性能,参考了两位学长的文章:https://segmentfault.com/a/1190000043395324https://segmentfault.com/a/1190000043413908?utm_source=sf-similar-article性能尚未实现,不过记录一下目前为止在此实现形式上遇到的一些问题;记录一下相干变量含意: wsAuthToken:前台生成的uuid。xAuthToken: 用户身份验证令牌map(WebSocketService): 绑定wsAuthToken和xAuthTokenmap(UserServiceImpl): 绑定loginUid和usernameopenid: 微信向开发者提供的用户惟一标识符微信对接微信对接的根本流程是:微信服务器向开发者在微信公众平台提供的服务器地址发送申请,而后服务器返回原样给微信服务器申请内容,微信服务器验证通过后对接胜利。因为这个过程中波及到了微信服务器向服务器发动申请,如果服务器位于内网,那么外界就无法访问到,所以须要通过内网穿透将内网的服务器映射到外网。首先尝试应用了ngrok,尝试对接时却发现调用不到后盾的办法,然而在本机上应用浏览器拜访外网地址就能够登程对应的办法。发现呈现以上状况是因为在拜访ngrok提供的外网地址时,会先弹出一个界面所以微信服务器接管到的返回值实际上是这个界面,最终也就配置失败了。 ngrok没有胜利之后切换应用frp工具来内网穿透,不过应用frp须要有一台公网的服务器。 Invalid Host Header微信对接实现之后通过外网地址拜访服务呈现了Invalid Host Header的谬误如果在本机拜访服务能够失常拜访。 呈现起因如下: "Invalid Host Header" 是由 Web 服务器的平安机制所触发的谬误,通常是为了避免 HTTP Host 坑骗攻打。当 Web 服务器收到一个申请时,它会查看申请头中的 Host 字段是否与服务器上的域名匹配。如果不匹配,则服务器会回绝该申请并返回 "Invalid Host Header" 谬误。通过浏览器拜访服务器时,申请的Host为abc.com:7005,而后通过nginx将申请转发到服务器上4200端口的web服务上。在这个过程中,如果nginx设置了proxy_set_header Host $host,nginx在转发时就会将host批改为客户端申请的主机名,也就是abc.com:7005;如果不设置,发送到web服务的host的值就会是nginx所在服务器的域名或IP地址。因为我这里曾经配置该项,所以我的web服务接管到的申请的host的值是abc.com:7005。而进行测验之后发现申请头中的Host字段与服务器的域名不匹配最终呈现了invalid host header的谬误。解决该问题只须要在启动开发服务器时候禁用hostCheck即可。 解决问题时发现在浏览器的开发者工具中查看网络申请没有host字段。应该是因为浏览器通常会将申请头分组并暗藏许多头信息,包含 Host 头。Chrome 浏览器的开发者工具文档 未获取到sessionId(xAuthToken)这里获取不到sessionId,打印后发现map的内容为空,阐明webSocket没有向map中存值,map的值是通过webSocket来获取的。不过这个办法并没有被调用,所以webSocket连贯并没有胜利。查看后发现我的我的项目中建设连贯的代码没有执行所以前台无奈通过webSocket连贯向map存值。 建设webSocket连贯。在开发扫码登录时,一方面因为代码逻辑问题导致连贯建设失败之外。还有就是没有引入相干的js文件。在参考文章中,SockJS,Stomp两个类须要独自引入文件来提供,别离是sockjs.min.js,stomp.min.js(见参考文章源码的assets)。连贯办法: 前台WebsocketService->onInit() => 后盾 WebSocketController->bindToXAuthToken() 能够扫码绑定,无奈扫码登录通过排查之后,发现是因为在登录界面时,webSocket连贯还未建设,所以扫码之后,后盾服务器无奈向前台推送用于登录的loginUid。前台也就无奈发送登录申请进行登录。问题解决只须要实现在登录界面就建设连贯即可。学长的示例中连贯建设如下: private currentLoginUser$ = new ReplaySubject<User>(1);WebsocketService private onInit(): void { this.userService?.getCurrentLoginUser$().pipe(first()) .subscribe(() => { // 连贯代码 });}订阅用户,用户变动时从新建设连贯。着陆组件中初始化用户 this.userService.initCurrentLoginUser();执行error代码,设置用户为undefined,并且前台此时开始尝试建设websocket连贯。 this.setCurrentLoginUser(undefined);接管到的appId与本人的不符用户扫码后微信服务器向开发服务器推送音讯时蕴含的信息波及appid。在后盾配置实现之后进行扫码操作时,后盾接管到的信息: 而toUser这个字段值之后被作为WechatUser的appid属性的值。而收到值的始终是如图的gh_d7exxx与微信公众平台测试号理论的appid不雷同。 可能是因为应用的测试号,不过还没有验证,之后再进行测试。 Subject示例Subject订阅之后有新值再公布 ...

May 17, 2023 · 1 min · jiezi

关于angular:ThingsBoard-前端项目内置部件开发

前言ThingsBoard 是目前 Github 上最风行的开源物联网平台(12.8k Star),能够实现物联网我的项目的疾速开发、治理和扩大, 是中小微企业物联网平台的不二之选。 本文介绍如何在 ThingsBoard 前端我的项目中开发内置的菜单导航部件。 内置相干部件TB(ThingsBoard简称,下同)零碎前端内置了三十多种部件,如日期范畴抉择部件: 对应代码寄存在前端我的项目文件中:thingsboard\ui-ngx\src\app\modules\home\components\widget\lib\date-range-navigator。 TB 后盾指定了十四种部件包(一类部件合集,不能再增加部件),由后盾接口返回代码存储地位:thingsboard\application\src\main\data\json\system\widget_bundles。 对应 TB 前端部件包界面列表: 在仪表板中的部件,编辑中的高级选项会对应部件的 setting 文件,如日期范畴抉择组件的高级内容对应文件:thingsboard\ui-ngx\src\app\modules\home\components\widget\lib\settings\date\date-range-navigator-widget-settings.component.ts。 当然本篇不会波及部件高级设置性能,后续可能会独自出一篇相干文章。 部件实现计划咱们先察看下日期范畴部件。 能够看到 TB 前端内置了 tb-date-range-navigator-widget 组件,间接通过调用组件选择器实现了部件的性能,所以咱们开发菜单导航部件也须要先开发一个对应的组件。 菜单导航组件在抉择菜单导航组件的的抉择上我掉坑里了- -,选用的 layui 的菜单导航,引入到 TB 前端我的项目中发现款式没问题,然而点击没有开展收起成果,只能放弃。当然这不代表 layui 不好,相同 layui 是很用心的一款前端组件,我正在开发的博客主题用的就是 layui。 再起初抉择了 Ant Design 组件库,对应 Angular 实现的版本叫 ng-zorro-antd,其中的菜单导航组件链接:https://ng.ant.design/version/12.1.x/components/menu/zh。 Angular 引入 Ant因 TB 前端我的项目应用的是 Angular v12 的版本,所以对应的 ng-zorro-antd 要抉择 v12.1.1 版本,过高的版本装置后会报错,装置命令: yarn add ng-zorro-antd@12.1.1 --save在 angular.json 文件中引入 css 款式和 icons 资源: ...

April 27, 2023 · 2 min · jiezi

关于angular:Angular-应用里-serverts-文件的-APPBASEHREF-token-的用法

Angular 利用里 server.ts 文件,上面这段代码的含意? server.get('*', (req, res) => { res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }], }); }); 在 Angular 利用中,server.ts 文件是用于构建服务器端渲染(Server-side rendering,简称 SSR)的入口文件。该文件中的代码通常应用 Node.js 的 http 模块创立一个 HTTP 服务器,用于解决客户端申请。 在这段代码中,server.get('*', ...) 示意当 HTTP 服务器收到任何申请时都会执行上面的回调函数。其中,res.render() 办法用于渲染 HTML 页面,并将其作为响应发送给客户端。这里的 indexHtml 是一个字符串,它是应用程序的 HTML 入口文件,在服务端渲染时须要将其填充为残缺的 HTML 页面。能够通过 Node.js 的文件系统模块或者其余形式加载 HTML 入口文件。 res.render() 办法的第二个参数是一个对象,用于向 HTML 页面注入数据。在这个例子中,咱们向 indexHtml 中注入了两个属性: req:示意以后申请的上下文信息,包含申请的 URL、HTTP 头、HTTP 办法等。providers:是一个数组,用于向 Angular 应用程序的 DI(Dependency Injection,依赖注入)零碎中注入数据。在这个例子中,咱们向 DI 零碎中注入了一个 APP_BASE_HREF 的提供者,用于指定应用程序的根门路。req.baseUrl 示意以后申请的门路,它会作为 APP_BASE_HREF 的值被注入到 DI 零碎中,从而在应用程序中能够应用 APP_BASE_HREF 依赖注入来获取以后申请的根门路。须要留神的是,这段代码只是 server.ts 文件中的一部分,还须要在其余中央(例如在 main.ts 中)定义 APP_BASE_HREF 的提供者。这样能力在客户端和服务端都正确地应用 APP_BASE_HREF 依赖注入。APP_BASE_HREF 是一个 Angular 利用中用于指定应用程序根门路的常量。它通常用于在应用程序中构建 URL,例如生成导航链接或者加载资源文件。 ...

April 4, 2023 · 1 min · jiezi

关于angular:Angular-动态表单与数据库结合后的通过模拟数据的实现

前言之前曾经介绍过Angular中动静表单的根本实现思路,可参考Angular动静表单构建的简略实现,其中简略的介绍了前端的代码。这篇文章在之前的实现形式上做了局部批改,比方没有再应用[ngSwitch]而是建设了一个自定义组件配置类,而后应用动静组件的形式来渲染组件,向对批改敞开,对扩大凋谢的开发准则聚拢。具体能够查看源码,已在文末给出 本文将在此基础上联合数据表构造通过模仿数据来简略演示下以老师新增申请和编辑申请作为场景时,如何将数据库中的数据转换成为v层渲染出的表单。最终成果外观和一般表单相似,实际上每个表单都为动静渲染失去。 一、 批改阐明这篇文章中应用的类名与属性名与Angular动静表单构建的简略实现中的有所区别,先在此进行简略阐明。本文中的表单对象模型将应用FormInfo为类名,动静表单模板应用FormComponent为类名,动静表单内容即表单项应用FieldComponent为类名。 变动名称Angular动静表单构建的简略实现本文表单对象模型FormFormInfo动静表单模板(表单组件)DynamicFormComponentFormComponent动静表单内容(表单项组件)DynamicFormUnitComponentFieldComponent二、问题之前的文章是间接模仿现成的FormInfo(即之前文章中的Form);这篇文章演示的是从后盾获取原始数据,而后将原始数据转换为FormInfo,再通过FormInfos失去相应的表单。所以咱们要解决的次要问题是: 了解ER图如何将原始数据转换成FormInfo对象模型三、ER图模型实现基本功能的动静表单的实体图如下,请对该实体图有个大略的理解。 四、ER图含意4.1、 表单的构建首先咱们来讨论一下ER图的构造,此ER图中有8个表,咱们临时先只探讨申请类型ApplyType, 字段Field, 字段类型FieldType三个表。 申请类型:对申请进行分类,比方评奖申请,入职申请。字段:记录在数据库的表单字段,比方一个表单中的姓名输出,地址输出。字段类型:字段对应的自定义类型,比方姓名应该是输出文本,那么就能够用textbox作为其中type属性的值;地址能够是抉择类型,即以dropdown为值(下面篇文章中定义的两个类型)。每个申请类型对应多个字段,每个字段和一个字段类型相关联。 它们之间的关系就是一个申请类型就是一个表单,对应多个表单项(即字段),通过表单类型来规定表单项的模式。 4.2、 表单值记录再探讨申请表Apply和字段记录表FieldRecord。下面的是针对新增的,新增实现之后须要对数据进行保留,而申请表Apply对应的就是新增实现之后保留的申请,通过字段记录表FieldRecord来记录它的值。如同申请类型表ApplyType和字段表Field的关系,申请表Apply和字段表Field的关系也是这样,只是在之间加上了一个用来记录值的两头表。 4.3、 表单验证规定持续探讨字段校验器FieldValidator,它记录了每个字段的校验规定,每个字段可能关联多个校验规定,比方required,unique。因为须要思考到字段对于验证器的要求以及用户的输出,所以在定制表单的时候须要对验证器进行范畴要求,不能让用户随便抉择验证器。所以须要先通过字段类型表ApplyType到字段校验器表FieldValidator获取到可选验证器,而后通过用户的进一步抉择,最初实现不同的字段有不同的验证规定。 4.4、字段选项最初再加上数据集和数据项两个表,两表之间关系很简略,每个数据集对应多个数据项,次要的是在于为什么会有这两个表的存在。有一些字段类型须要进行抉择,所以要给出选项,比方select以及radio等,这时候两个表就能够发挥作用了。对于一些静态数据,比方性别(男、女),评级(A、B、C)等能够应用数据集来提供。对于须要实时渲染的动态数据(比方学生抉择,班级抉择),可能会须要自定义相应的组件。对于为什么将数据集DataSet和字段表Field建设关联而不是和字段类型表FieldType建设关系,是想到如果须要增加的数据集多的话和后者建设关系须要创立更多的自定义组件,而如果和前者建设关系无需补充组件。 五、将原始数据转化为FormInfo本文应用的数据都是通过事后定义好的模仿数据。 新增演示首先筹备相应的模仿数据 import {Field} from '../entity/field';import {Apply} from '../entity/apply';import {FieldRecord} from '../entity/field-record';import {DataSet} from '../entity/data-set';import {DataItem} from '../entity/data-item';import {ApplyType} from '../entity/apply-type';import {FieldValidator} from '../entity/field-validator';const MockDataItems = [ {id: 1, name: '男', weight: 2, dataSet: {id: 1} as DataSet}, {id: 2, name: '女', weight: 1, dataSet: {id: 1} as DataSet},] as DataItem[];const MockDataSets = [ {id: 1, name: '性别', dataItems: MockDataItems}] as DataSet[];const MockFieldValidators = [ {id: 1, name: '是否必须', key: 'required', value: true}] as FieldValidator[];const MockFields = [ {id: 1, fieldType: {id: 1, type: 'textbox'}, fieldValidators: MockFieldValidators, key: 'name', label: '名称', weight: 4, type: 'text'}, {id: 2, fieldType: {id: 2, type: 'textbox'}, fieldValidators: MockFieldValidators, key: 'username', label: '用户名', weight: 3, type: 'text'}, {id: 3, fieldType: {id: 3, type: 'textbox'}, fieldValidators: MockFieldValidators, key: 'email', label: '邮箱', weight: 2, type: 'email'}, {id: 4, fieldType: {id: 4, type: 'textbox'}, fieldValidators: MockFieldValidators, key: 'jobNumber', label: '工号', weight: 1, type: 'number'}, {id: 5, fieldType: {id: 5, type: 'radio'}, fieldValidators: MockFieldValidators, key: 'sex', label: '性别', dataSet: MockDataSets[0], weight: 0, type: ''},] as Field[];export const MockApplyType = { id: 1, name: '老师新增', fields: MockFields} as ApplyType;上述示例定义了1个申请类型,5个字段,1个验证器以及一个与男女数据项相关联的数据集。当从数据库获取这些数据之后,而后将其转换成FormInfo,FormInfos的中的对象个数取决于Fields的长度,即每一个Field都将转换成一个FormInfo。 ...

March 25, 2023 · 3 min · jiezi

关于angular:跨域的五种最常见解决方案

什么是跨域?跨域不是问题,是一种平安机制。浏览器有一种策略名为同源策略,同源策略规定了局部申请不能被浏览器所承受。值得一提的是:同源策略导致的跨域是浏览器单方面回绝响应数据,服务器端是处理完毕并做出了响应的。什么是同源策略一个url由三局部组成:协定,域名(ip地址),端口。只有当协定,域名,端口都统一的时候,才被称为同源。而同源策略规定,只有发送申请的那一边和承受申请的那一边处于同源的状况下,浏览器才会承受响应。举个例子发送申请地址:http:47.96.127.5:8080/index 承受申请地址:http:47.96.127.5:8081/index //不同源 端口不同发送申请地址:http:47.96.127.5:8080/index 承受申请地址:http:47.96.127.6:8080/index //不同源 ip不同发送申请地址:http:47.96.127.5:8080/index 承受申请地址:https:47.96.127.5:8080/index //不同源 协定不同发送申请地址:http:47.96.127.5:8080/index 承受申请地址:http:47.96.127.5:8080/login //同源 协定,端口,ip都雷同,门路不同无所谓复制代码而当咱们的申请不合乎同源策略的时候。往往会呈现以下谬误 解决跨域常见的5种办法第一种:JQuery的ajax(举荐JQuery我的项目中应用)jq的ajax自带解决跨域的办法。底层原理采纳的JSONP的跨域解决方案。如下function callback(){ console.log("月薪一千五,心比美式苦")} $.ajax({ url: 'http://www.domain2.com:8080/login',type: 'get',dataType: 'jsonp', // 申请形式为jsonp 设置跨域的重点jsonpCallback: "callBack", // 回调函数});复制代码在JQ我的项目中,这是最常见的解决方案。第二种:script标签解决跨域(远古Web我的项目中应用)如果你的我的项目是祖传下来的。没有框架连JQuery都没有。没关系,咱们能够尝试应用原生的办法去解决。原生采纳的是script标签能够不受跨域限度的个性来实现跨域。 <script> // 回调 function callBack(res) { console.log("跨域的回调",res); // ...实现你所有操作后记得删除script↓ document.head.getElementsByClassName("script")[0].remove(); } const scriptDom = document.createElement('script'); scriptDom.type = 'text/javascript'; scriptDom.class = 'script'; //用于删除 // 传参一个回调函数名给后端,不便后端返回时执行这个在前端定义的回调函数 scriptDom.src = 'http://192.167.0.152:9996/inface?callback=callBack'; document.head.appendChild(script); //将标签挂载到dom上 </script>复制代码这里须要留神的是,应用完申请之后记得删除script,否则会随着申请的变多script标签会始终挂载在DOM上。在远古的web中,这是一种计划。但当初曾经不必了。 vue/react/jq等框架性我的项目中不要应用这种办法,不是不行,只是有更好的抉择 第三种:前端代理解决跨域每一个框架的代理配置都不太一样。这里仅以我应用的umi.js(react)为例。Umi.js框架会有 config.ts / config.js 文件,文件中会有proxy字段、字段按图中配置办法。即可实现跨域 第四种:服务端代理(Nginx代理)nginx代理个别应用在生产环境。是服务端解决跨域的一种计划。简略配置模板 如果监听到申请接口地址是 www.xxx.com/api/page ,nginx就向http://www.yyy.com:9999/api/page这个地址发送申请server { ...

March 22, 2023 · 1 min · jiezi

关于angular:记一个用angular实现的动态表单demo可对不同角色的表单的字段进行新增删除操作

什么是动静表单“表单”这个词大家其实并不生疏,即angular中的form。所以对于“动静表单”的了解次要着眼于“动静”。就我集体了解而言,“动静”即是能够随状况的变动而进行变动的。按这个思维进行延长并与“表单”联合起来,我大抵将“动静表单”了解为:当用户进行不同的操作时,表单中的内容(如字段等)会产生动静变更。如下图:角色抉择学生时和抉择老师时的所渲染的表单是不同的,且不仅仅是V层不同,C层中对应的formGroup中的字段也必须是不同的。再高级一点那就是赋予用户新增和删除不同角色的表单的字段的权力。如下图:点击“addField”按钮后,其余角色就会(在V层和C层的formGroup中)新增“年龄”字段;点击“delete”后“年龄”字段便被删除。 动静表单demo的angular实现留神:此dome齐全在angular框架下写的,所以对于新增的字段的贮存是以缓存的模式贮存在Window.sessionStorage中,正式的开发此局部的内容应由后端进行解决。 实现思路要实现表单到动静表单的变迁,首先咱们应该理解,就angular的form表单而言,是由两个重要局部组成的:一是在V层与用户交互的UI,二是在C层中的用于接管字段的值的formGroup。在个别的表单中,二者都是曾经写死的,即动态的。所以问题的要害便是上述二者的内容如何随着用户的操作而变更。简略思考一下:前者的动静实现须要将V层中动态的值替换成动静变量,后者的的动静实现即formGroup的动态创建也须要一个动静变量来作为参数(详情请看下文createFormGroup()办法,位于代码实现->C层)。咱们可能发现:它们都须要一个动静变量。而这个动静变量须要可能由用户的操作而管制。咱们设那个动静变量为A(位于C层中),其实咱们还须要一个变量B(位于缓存中),用于解决字段的新增和删除。最终我的思路大抵如下图: 代码实现首先在angular我的项目中轻易初始化一个组件(C层、V层、css文件、测试文件) V层<!--动静表单demo--><div class="row"> <div class="col-3"> <h2>动静表单</h2> <div class="example-outlet"> <label>角色:</label> <select [formControl]="formSelect" (change)="formSelectChange()"> <option value="student">学生</option> <option value="teacher">老师</option> <option value="other">其余</option> </select> <form [formGroup]="formGroup" (ngSubmit)="onSubmit(formGroup)"> <div *ngFor="let item of baseFrom"> <label>{{ item.label }}:</label> <input type="text" formControlName="{{ item.key }}" placeholder="{{item.placeholder}}"> <button *ngIf="item.key !== 'name' && item.key !== 'number'" (click)="deleteField(item)">delete</button> </div> <button>submit</button> </form> </div> </div> <div class="col-3"> <h2>动静表单增加字段</h2> <div class="example-outlet"> <form [formGroup]="formGroupForAddField" (ngSubmit)="addField(formGroupForAddField)"> <label>角色:</label> <select formControlName="role"> <option value="student">学生</option> <option value="teacher">老师</option> <option value="other">其余</option> </select> <div> <label>字段:</label> <input type="text" placeholder="请输出字段名称" formControlName="field"> </div> <button>add field</button> </form> </div> </div></div>C层// 动静表单demo baseFrom: any = []; formGroup = this.createFormGroup(this.baseFrom); formSelect = new FormControl(null); formGroupForAddField = new FormGroup({ field: new FormControl('', [Validators.required]), role: new FormControl('', [Validators.required]), }); studentField: any = []; teacherField: any = []; otherField: any = []; ngOnInit(): void { this.initBseForm(); // @ts-ignore const sessionStorage = JSON.parse(window.sessionStorage.getItem(this.formSelect.value)); if (sessionStorage !== null) { for (const item of sessionStorage) { const pre = [ { key: item, label: item, value: null, placeholder: '请输出' + item, validators: [Validators.required] } ]; this.baseFrom = [...this.baseFrom, ...pre]; } } console.log('ngOnIit', this.baseFrom); this.formGroup = this.createFormGroup(this.baseFrom); } addField(formGroupForAddField: FormGroup): void { if (formGroupForAddField.get('field')?.value !== '' && formGroupForAddField.get('role')?.value !== '') { console.log('formGroupForAddField', formGroupForAddField.value); const role = formGroupForAddField.get('role')?.value; const field = formGroupForAddField.get('field')?.value; let addField: any = []; if (role === 'student') { this.studentField[this.studentField.length] = field; addField = this.studentField; } else if (role === 'teacher') { this.teacherField[this.teacherField.length] = field; addField = this.teacherField; } else if (role === 'other') { this.otherField[this.otherField.length] = field; addField = this.otherField; } window.sessionStorage.setItem(role, JSON.stringify(addField)); this.ngOnInit(); } } createFormGroup(options: any[]): FormGroup { const group: any = {}; options.forEach(item => { group[item.key] = new FormControl(item.value, item.validators); }); return new FormGroup(group); } deleteField(item: any): void { // @ts-ignore const sessionStorage = JSON.parse(window.sessionStorage.getItem(this.formSelect.value)); const role = this.formGroupForAddField.get('role')?.value; if (sessionStorage !== null) { console.log('deleteField', sessionStorage); this.deleteItemOfArr(sessionStorage, item); this.deleteItemOfArr(this.baseFrom, item); if (role === 'student') { this.deleteItemOfArr(this.studentField, item); } else if (role === 'teacher') { this.deleteItemOfArr(this.teacherField, item); } else if (role === 'other') { this.deleteItemOfArr(this.otherField, item); } window.sessionStorage.setItem(this.formSelect.value, JSON.stringify(sessionStorage)); } this.ngOnInit(); } deleteItemOfArr(ary: [], el: string): [] { // @ts-ignore const index = ary.indexOf(el); const delEle = ary.splice(index, 1); return ary; } formSelectChange(): void { this.ngOnInit(); } initBseForm(): void { if (this.formSelect.value === null || this.formGroupForAddField === null) { this.formSelect.setValue('student'); this.formGroupForAddField.get('role')?.setValue('student'); } this.baseFrom = [ { key: 'name', label: '姓名', value: null, placeholder: '请输出姓名', validators: [Validators.required] }, { key: 'number', label: '手机号', value: null, placeholder: '请输出手机号', validators: [Validators.required] } ]; } onSubmit(formGroup: FormGroup): void { console.log('formGroup', formGroup.value); }css文件.example-outlet { margin-bottom: 10px; padding: 10px; border: 1px dashed black; width: 350px; height: 250px;}成果预览 ...

March 11, 2023 · 2 min · jiezi

关于angular:Angular动态表单构建

1、什么是动静表单动静表单指的是一个能够在运行时依据用户输出或其余动静因素扭转的表单。与传统的动态表单不同,动静表单能够依据不同的条件、选项和输出值来扭转它的外观和行为。 如果你有这样一种表单,其内容必须常常更改以满足疾速变动的业务需要和监管需要,该技术就特地有用。一个典型的例子就是问卷。你可能须要在不同的上下文中获取用户的意见。用户要看到的表单格局和款式应该放弃不变,而你要提的理论问题则会因上下文而异。2、动静表单的构建思路之前的表单项是写死的hard-coded,而动静表单中的每个表单项实际上是一个组件,如果须要在某个中央动静的减少或者缩小表单项,那么只须要在此处减少一个实例化的组件或者删除该组件即可。 具体的管制须要一个数据对象数组,通过该数组获取须要展现的多个表单项,进而取得这些表单项组成的表单,再将该表单封装成一个组件,而后在须要调用动静表单的中央插入该组件即可。 3、动静表单具体构建过程3.1、引入ReactiveFormsModule因为动静表单基于响应式表单,所以须要在动静表单所在模块引入ReactiveFormsModule,本文以在根模块创立为例。即在app.module.ts中引入该模块。 @NgModule({ imports: [ BrowserModule, ReactiveFormsModule ], declarations: [ AppComponent], bootstrap: [ AppComponent ]})export class AppModule { constructor() { }}3.2、创立一个表单对象模型Form创立Form类如下: export class Form { key: string; // 关键字 value: string | undefined; // 值 label: string; // 标签内容 order: number; // 程序 controlType: string; // 表单项类型(input, dropdown, custom...) type: string; // 具体类型(input中的email, number, ...) options: {key: string, value: string}[]; // 子项(作用于controlType为dropdown, radio...时) rule = {} as { // 验证规定 required: boolean; } constructor(options: { value?: string; key?: string; label?: string; order?: number; controlType?: string; type?: string; options?: {key: string, value: string}[]; rule?: { required: boolean } } = {}) { this.key = options.key || ''; this.value = options.value; this.label = options.label || ''; this.order = options.order === undefined ? 1 : options.order; this.controlType = options.controlType || ''; this.type = options.type || ''; this.options = options.options || []; this.rule = options.rule ? options.rule : {} as {required: boolean}; }}3.3、创立动静表单模板有了表单对象模型咱们还须要创立一个放表单项的容器,这里咱们创立一个动静表单模板组件DynamicFormComponent。dynamic-form.component.ts ...

March 11, 2023 · 3 min · jiezi

关于angular:浅谈前端的状态管理以及anguar的状态管理库

背景在以后angular我的项目中尝试应用状态治理。上面先来理解状态治理。 什么是状态治理状态治理是一个非常宽泛的概念,因为状态无处不在。服务端也有状态,Spring 等框架会治理状态,手机 App 也会把数据保留到手机内存里。 在前端技术中, 状态治理能够帮忙你治理“全局”状态 - 那些应用程序的许多局部都须要的状态。比方组件之间共享的数据、页面须要及时更新的数据等等。 为什么须要状态治理咱们先来看一个 facebook 的例子。 在 Facebook 没有 Flux 这种状态治理架构以前,就有很多状态未同步的 bug: 在 Facebook 逛着逛着,忽然来了几条告诉,然而点进去之后居然没有了,过了一会儿,告诉又来了,点进去看到了之前的音讯,然而新的还是没有来。 这种问题就跟他们在 Flux 介绍 中所形容的一样: 为了更好地形容Flux,咱们将其与经典的MVC构造比拟。在客户端MVC应用程序中,数据流大略如下 用户交互控制器,控制器更新model,视图读取model的新数据,更新并显示给用户 然而在多个控制器,模型,父子组件等增加下,依赖变得越来越简单。 控制器不仅须要更新以后组件,也须要更新父子组件中的共享数据。 如下图,仅增加三个视图,一个控制器和一个模型,就曾经很难跟踪依赖图。 这时,facebook 提出了 Flux架构 ,它的重要思维是: 繁多数据源和单向数据流 繁多数据源要求: 客户端利用的要害数据都要从同一个中央获取单向数据流要求:利用内状态治理的参与者都要依照一条流向来获取数据和收回动作,不容许双向替换数据 举个例子: 一个页面中渲染了数据列表 books,这个 books 变量是通过 store 获取的,合乎单向数据流。 然而这时候要增加一本书,咱们调用了一个 api 申请 createBook. 通常有人为了不便,会在组件外面通过 http 调用申请后间接把新增的 book 加到列表外面。这就违反了繁多数据源和单向数据流 假如这时另一个组件也在渲染数据列表 books, 这时候咱们就须要手动更改这个组件中的数据。或者再次申请一次后盾。 当这样的状况一多,意味着利用内的状态从新变成七零八落的状况,重归混沌,同步生效,容易bug从生。 在同一期间,Facebook 的 React 跟 Flux 一起设计进去,React 开始逐步风行,同时在 Flux 的启发下,状态治理也正式走进了泛滥开发者的眼前,状态治理开始规范化地走进了人们的眼前。 状态模式的模式和库Vue与React都有较为简单的全局状态管理策略,比方简略的store模式,以及第三方状态治理库,比方Rudux,Vuex等 Store最简略的解决就是把状态存到一个内部变量外面,当然也能够是一个全局变量 ...

March 9, 2023 · 5 min · jiezi

关于angular:玩转Angular系列组件间各种通信方式详解

前言在前端框架Angular中,组件之间的通信很根底也很重要,不同组件间的通信形式也不同,把握组件间的通信形式会更加粗浅的了解和应用Angular框架。 本文解说不同类型组件间的不同通信形式,文中所有示例均提供源码,您能够 在线编辑预览 或 下载本地调试,置信通过本文您肯定能够把握组件通信这一知识点。 父组件传子组件@Input形式 @Input()装璜器容许父组件更新子组件中的数据,分为4步: 第一步:在父组件app.component.ts中定义要传递给子组件的数据parentMsg。 export class AppComponent { parentMsg: string = 'parent component message!';}第二步:在父组件app.component.html中的子组件标签<app-child>中定义属性[childMsg](子组件接收数据变量)来绑定父组件的数据parentMsg。 <app-child [childMsg]="parentMsg"></app-child>第三步:在子组件child.component.ts中引入@Input()装璜器,润饰childMsg接管父组件的传值。 import { Input } from '@angular/core';export class ChildComponent { @Input() childMsg: string = '';}第四步:在子组件child.component.html中通过模板标签{{childMsg}}展现数据。 <div>父组件传值内容:{{ childMsg }}</div>最终展现成果如下,您能够通过 在线示例 预览成果和编辑调试: 阐明:这里要了解父组件html中通过[]定义了一个子组件的属性,该值必须与子组件中所定义的变量名统一,而等号左边的值为父组件要传递的属性名,示例中是将父组件中parentMsg的值绑定在子组件childMsg属性上。 子组件传父组件@Output()形式 @Output()装璜器容许数据从子组件传给父组件,分为6步: 第一步:在子组件child.component.ts中引入Output和EventEmitter,通过@Output()来润饰一个EventEmitter实例的变量newItemEvent。 import { Component, Output, EventEmitter } from '@angular/core';export class ChildComponent { @Output() newItemEvent = new EventEmitter<string>();}第二步:在子组件child.component.html中增加点击事件,获取输出内容,点击按钮触发addNewItem()办法。 <label>输出我的项目名:<input type="text" #newItem /></label><button type="button" (click)="addNewItem(newItem.value)"> 增加我的项目到父组件</button>第三步:在子组件child.component.ts中通过newItemEvent的emit()办法,把数据发送到父组件。 export class ChildComponent { @Output() newItemEvent = new EventEmitter<string>(); addNewItem(value: string) { this.newItemEvent.emit(value); }}第四步:在父组件app.component.html中子组件标签<app-child>中增加父组件办法addItem($event)绑定到子组件的newItemEvent发射器事件上,其中$event为子组件的传递的值。 ...

March 2, 2023 · 4 min · jiezi

关于angular:解决angular-报错-url-unsafe

遇到报错应用的img 标签呈现了报错 <img class="img-thumbnail" [src]="qrCodeSrc"/> 报错这个url 为 unsafe XSS首先先理解为什么会呈现unsafe 为了系统性的防备 XSS 问题,Angular 默认把所有值都当做不可信赖的。即 unsafe 跨站脚本(XSS)容许攻击者将恶意代码注入到页面中。这些代码能够偷取用户及其登录数据数据,还能够假冒用户执行操作。它是 Web 上最常见的攻击方式之一。 比方,如果某个攻击者能把 <script> 标签插入到 DOM,就能够在你的网站上运行任何代码。又比方其余的一些标签<img alt="" src="..."> <a href="javascript:..."。 如果攻击者所管制的数据混进了 DOM,就会导致安全漏洞。 Angular 将对这些值进行消毒(Sanitize),对不可信的值进行编码。 如果咱们认为这个值是平安的,能够采纳下列办法把这个值标记为平安,‘ 注入 DomSanitizer 服务,而后调用上面的办法之一,就能够把一个值标记为平安。 bypassSecurityTrustHtmlbypassSecurityTrustScriptbypassSecurityTrustStylebypassSecurityTrustUrlbypassSecurityTrustResourceUrl解决办法: 消毒 this.qrCodeSrc = this.sanitizer.bypassSecurityTrustUrl(url);<img class="img-thumbnail" [src]="qrCodeSrc"/>然而消毒后,url 加上了 localhost 和 %22 为什么会加上localhost?起因: src 的若不是http, https, ftp...等结尾,会被认为是相对路径,理论导向时就会被加上http://域名/ 呈现的%22 , 依据谷歌找到是表明 双引号所以最初的后果是因为: url后面呈现了双引号 , 导致被认为是相对路径, 主动加上localhost. 解决: 去掉双引号 this.qrCodeSrc = this.sanitizer.bypassSecurityTrustUrl(src.replace(/\"/g, ""));

February 21, 2023 · 1 min · jiezi

关于angular:解决多project的angular应用单元测试报-ChromeHeadlessCI未注册的错误

此文章解决angular在单元测试时报:Cannot load browser "ChromeHeadlessCI": it is not registered! 谬误。 angular在开发环境中的单元测试默认是基于chrome浏览器的,但在 CI 过程中的环境往往并没有桌面环境,没有桌面环境也就意味着无奈在 CI 中运行chrome浏览器。这时候则须要一个能够在非桌面环境下运行的chrome浏览器:chromium。 同时又因为chromium默认在非root权限(sandbox)下运行,而咱们 CI 时借助了docker容器,这个容器在运行时又是root权限,所以咱们在 CI 的进行单元测试的过程中,则须要退出no-sandbox 的参数。 karma.confangular的单元测试由karma负责启动,咱们须要的实用于CI的无头浏览器也须要配置在这,找到配置文件,批改以下几行: browsers: ['Chrome'], customLaunchers: { ChromeHeadlessCI: { base: 'ChromeHeadless', flags: ['--no-sandbox'] } },上述配置的作用是减少一个自定义的浏览器类型ChromeHeadlessCI,这个名字轻易起。上面简略对配置做下阐明: browsers: ['Chrome'], customLaunchers: { // 自定义启动器 ChromeHeadlessCI: { // 自定义一个名字为ChromeHeadlessCI的启动器 base: 'ChromeHeadless', // 基于 ChromeHeadless flags: ['--no-sandbox'] // 在ChromeHeadless的根底上,减少--no-sandbox参数,这样就能够在root下运行了 } },命令如果咱们以后开发的是历史的我的项目,则新的angular cli与原我的项目中应用的angular cli必然会产生对node.js依赖的版本抵触。这时候咱们就不要去应用本地全局的angular cli了,改用node_modules/@angular/cli/bin/ng会好很多。最初因为在 CI 时咱们在为其退出--no-watch,以便能够顺利完结测试,最终的命令大体如下: # node_modules/@angular/cli/bin/ng test wechat -- --no-watch --no-progress --browsers=ChromeHeadlessCI多project如果你的angular也是一个多project的我的项目,那么须要留神的是每个子项目(比方你有一个web, 一个lib,还有一个wechat)都能够对应惟一的配置文件,至于这个配置文件在哪,则须要去angular.json中去查看: ...

February 18, 2023 · 1 min · jiezi

关于angular:NgMatero-v15-正式发布

前言Angular 依照既定的发版打算在 11 月中旬公布了 v15 版本。推延了一个月(简直每个版本都是这个节奏),Ng-Matero 也终于更新到了 v15。其实 Ng-Matero 自身的更新非常简单,然而同步保护的 Material Extensions 这个库要先于 Ng-Matero 公布,所以大部分精力都消耗在组件库下面了。 我曾经很久没有写对于 Ng-Matero 的发版文章了。上次介绍发版还是 v10 版本,居然曾经是两年前的事件了。在这两年的开源生涯中,次要精力都在 Material 的扩大组件库下面。值得兴奋的是,就在 2022 行将过来时,Material Extensions 的周下载量终于破万了,六月份时这个数据还只是 5k+。从 0 到 5k 用了两年,而从 5k 到 1w 只用了半年。 本文次要聊一下 Ng-Matero 降级 v15 时遇到的一些问题及感触。同时也简略说一下近期在 v14 中新增的几个性能。 GitHub: https://github.com/ng-matero/...日期工夫组件 Datetimepicker 的重磅更新日期工夫组件 datetimepicker 是在 v12 中增加的,同时也减少了 moment-adapter 日期模块。然而 datetimepicker 的工夫抉择模块并没有齐全遵循 Material 标准(短少工夫输出模式),这也影响了很多需要的实现。在外国友人的帮忙下,14.6.0 终于加上了工夫输出性能,成果如下所示: 在线体验地址 工夫输出性能默认是敞开的,须要增加 timeInput 参数启用该个性。 <mat-form-field> <input [mtxDatetimepicker]="datetimePicker" matInput> <mtx-datetimepicker #datetimePicker [timeInput]="true"></mtx-datetimepicker> <mtx-datetimepicker-toggle [for]="datetimePicker" matSuffix></mtx-datetimepicker-toggle></mat-form-field>另外一个比拟重要的更新是减少了 luxon-adapter 和 date-fns-adapter 两个日期模块,这算是和 Angular Material 对齐了,同样要感激外国友人的帮忙。具体应用形式能够参考这个 issue。 ...

December 30, 2022 · 2 min · jiezi

关于angular:2023-重学-Angular

<article class=“article fmt article-content”><p><em>作者:徐海峰</em><br/>就在前几天(2022-11-07) Angular 正式公布了 v15 版本,自己第一工夫用我那不业余的英文翻译了一下 [译] Angular 15 正式公布! 文章一出就受到社区局部人的质疑,什么 “Angular 落寞很久了,劝咱们换框架”,还有什么 “这玩意竟然能够活到 2022 年,远古生物忽然复活”。 对此呢我也不想过多的评估,我只在乎通过工具是否扭转我司前端的开发效率,让每位共事早点上班,高质量实现指标,让 PingCode 远超竞品。<br/>略微相熟 Angular 框架的人应该都晓得, Angular 是一个齐全遵循语义化版本的框架,每年会固定公布2个主版本,目前尽管曾经是 v15,但根本和 v2 版本保持一致的主旋律,那么 v15 能够说是 Angular 框架在尝试扭转迈出的一大步,独立组件 APIs 正式稳固,指令的组合 API 以及很多个性的简化。<br/>尽管很多 Angular 开发者曾经习惯了 NgModules,然而 Angular 模块对于老手来说确实会带来一些学习老本,对于很多小我的项目而言带来的收益比并不高,所以反对独立组件(无 Modules) 也算是笼罩了更多的场景,同时也简化了学习老本,那么明天我想通过这篇文章以最小化的示例重新学习一下 Angular 框架,让不理解 Angular 的人重新认识一下这个 “<del>远古的生物</del>"<br/></p><h2>创立一个独立组件的我的项目</h2><p>首先通过如下<code>ng new</code> 命令创立一个 <code>ng-relearning</code> 的示例我的项目:</p><pre><code class=“JavaScript”>ng new ng-relearning –style scss –routing false</code></pre><blockquote>ng 命令须要通过 npm install @angular/cli -g 全局装置 @angular/cli 模块才能够应用。</blockquote><p>创立后的我的项目目录如下:</p><pre><code class=“JavaScript”>.├── README.md├── angular.json├── package.json├── src│ ├── app│ │ ├── app.component.html│ │ ├── app.component.scss│ │ ├── app.component.ts│ │ ├── app.component.spec.ts│ │ └── app.module.ts│ ├── assets│ ├── favicon.ico│ ├── index.html│ ├── main.ts│ └── styles.scss├── tsconfig.app.json├── tsconfig.json└── tsconfig.spec.json</code></pre><p>默认生成文件的介绍见下方表格,曾经相熟 Angular 的开发者能够跳过,相比拟其余框架 CLI 生成的目录构造而言,我集体感觉 Angular 的最正当也是最优雅的(纯个人见解)。<br/><br/>目前 <code>ng new</code> 命令初始化的我的项目还是带 Modules 的,反对 <code>–standalone</code> 参数创立独立组件的我的项目个性正在开发中,能够关注 此 Issue 。<br/>把默认的我的项目改为独立组件须要做如下几件事: <br/>手动删除 <code>app.module.ts</code> <br/>启动组件 AppComponent 中 @Component 元数据增加 <code>standalone: true</code> 并增加 <code>imports: [CommonModule]</code> <br/>批改 main.ts 代码为:</p><pre><code class=“JavaScript”>import { bootstrapApplication } from ‘@angular/platform-browser’;import { AppComponent } from ‘./app/app.component’;bootstrapApplication(AppComponent).catch((err) => console.error(err));</code></pre><p>这样执行 <code>npm start</code> ,会在本地启动一个 4200 端口的服务,拜访 http://localhost:4200/ 会展现 Angular 默认的站点:<br/><br/><code>bootstrapApplication</code> 函数启动利用,第一个参数 AppComponent 组件是启动组件,一个利用至多须要一个启动组件,也就是根组件,这个根组件选择器为 <code>app-root</code> ,那么生成的 index.html 会有对应的 <code><app-root></code> 占位元素,Angular 启动时会把它替换为 AppComponent 渲染的 DOM 元素。</p><pre><code class=“JavaScript”><!doctype html><html lang=“en”><head> <meta charset=“utf-8”> <title>NgRelearning</title> <base href=”/"> <meta name=“viewport” content=“width=device-width, initial-scale=1”> <link rel=“icon” type=“image/x-icon” href=“favicon.ico”></head><body> <app-root></app-root></body></html></code></pre><p>为了让 Angular 更容易学习和上手,Angular CLI 在 v15 初始化的我的项目做了一些简化(大家能够疏忽):</p><ul><li>去掉了独立的 <code>polyfills.ts</code> 文件,应用 angular.json <code>build</code> 选项 <code>“polyfills”: [ “zone.js”]</code> 代替</li><li>去掉了 <code>environments</code> 环境变量, 这个性能还在,当你须要的时候独自配置 <code>fileReplacements</code> 即可</li><li>去掉了 main.ts 中的 <code>enableProdMode</code> </li><li>去掉了 .browserslistrc</li><li>去掉了 karma.conf.js</li><li><p>去掉了 test.ts</p><h2>Hello Angular Relearning</h2><p>因为 <code>app.component.html</code> 的示例太简单,为了简化学习,咱们尝试删除 html 所有内容,批改为绑定 title 到 h2 模板元素中,应用双花括号 <code>{{</code> 和 <code>}}</code> 将组件的变量 <code>title</code> 动静插入到 HTML 模板中,这种叫 <code>文本插值</code> ,花括号两头的局部叫插值表达式。<br/><code><h2>{{title}}</h2></code><br/>同时批改组件类的代码,增加 <code>title</code> 属性,初始化值为 <code>‘Hello Angular Relearning!’</code> </p></li></ul><pre><code class=“JavaScript”>import { Component } from ‘@angular/core’;@Component({ selector: ‘app-root’, templateUrl: ‘./app.component.html’, standalone: true, styleUrls: [’./app.component.scss’],})export class AppComponent { title = ‘Hello Angular Relearning!’;}</code></pre><p>这样关上浏览器,发现 title 被渲染在界面上:<br/><br/>Angular 的组件就是一个一般的 class 类,通过 <code>@Component</code> 装璜器装璜后就变成了组件,通过装璜器参数能够设置选择器(selector)、模板(templateUrl 或者 template)、款式(styleUrls 或者 styles),组件模板中能够间接绑定组件类的公开属性。<br/>默认模板和款式是独立的一个 html 文件,如果是一个很简略的组件,也能够设置内联模板,上述示例能够简化为:</p><pre><code class=“JavaScript”>import { Component } from ‘@angular/core’;@Component({ selector: ‘app-root’, template: &lt;h2&gt;{{title}}&lt;/h2&gt;, standalone: true, styleUrls: [’./app.component.scss’],})export class AppComponent { title = ‘Hello Angular Relearning!’;}</code></pre><h2>条件判断</h2><p>在理论的利用中会常常用到的就是条件判断,咱们批改一下 <code>app.component.ts</code> 为:</p><pre><code class=“JavaScript”>import { Component } from ‘@angular/core’;import { CommonModule } from ‘@angular/common’;@Component({ selector: ‘app-root’, templateUrl: ‘./app.component.html’, standalone: true, styleUrls: [’./app.component.scss’], imports: [ CommonModule, ],})export class AppComponent { relearned = false; constructor() { setTimeout(() => { this.relearned = true; }, 2000); }}</code></pre><p>组件类中新增了 <code>relearned</code> 属性,默认为 false,setTimeout 2s 后设置 <code>relearned</code> 值为 true。<br/> <code>app.component.html</code> 批改为:</p><pre><code class=“JavaScript”><p *ngIf=“relearned else default”>很快乐看到你重新学习 Angular 这款优良的框架!</p><ng-template #default> <p>我还没有接触过 Angular 框架</p></ng-template></code></pre><ul><li><code>*ngIf</code> 为 Angular 内置的条件判断结构型指令,当 ngIf 的表达式为 true 时渲染此模板,否则不渲染,那么示例中的表达式为 <code>“relearned”</code> ,也就是 AppComponent 组件中的 relearned 属性</li><li>else 示意表达式为 false 后的模板,通过 ng-template 定义了一个默认模板,并通过 #default 申明这个模板变量为 default,这样 <code>ngIf</code> else 就能够应用这个变量 default</li><li>ng-template 是 Angular 定义的一个模板,模板默认不会渲染,ngIf 指令会在表达式为 else 的时候通过 createEmbeddedView 创立内嵌视图渲染这个模板,同时也能够通过 <code>NgTemplateOutlet</code> 指令渲染模板 <br/>展现成果为:<br/><br/>在 AppComponent 中咱们设置了 <code>imports: [CommonModule]</code> ,如果去掉这行代码会报错:<br/><br/>这是因为独立组件的模板中应用其余指令或者组件的时候须要显示的通过 <code>imports</code> 申明, <code>ngIf</code> 结构性指令是在 <code>@angular/common</code> 模块中提供的,如需应用须要导入:</li></ul><pre><code class=“JavaScript”>import { Component } from ‘@angular/core’;import { NgIf } from ‘@angular/common’;@Component({ … imports: [NgIf]})export class AppComponent {}</code></pre><p> <code>@angular/common</code> 模块除了提供 <code>NgIf</code> 内置指令外还有大家罕用的 <code>NgClass</code> 、 <code>NgFor</code> 、 <code>NgSwitch</code> 、 <code>NgStyle</code> 等等,所以间接把整个 CommonModule 都导入进来,这样在组件模板中就能够应用 CommonModule 模块的任意指令。</p><h2>事件绑定</h2><p>咱们接下来通过如下 ng 命令创立一个新组件 <code>event-binding</code> 改善了一下上述的示例:</p><pre><code class=“JavaScript”>ng generate component event-binding –standalone // 简写 ng g c event-binding –standalone</code></pre><p>执行后会在 src/app 文件夹下创立一个 event-binding 文件夹寄存新增的 event-binding 组件</p><pre><code class=“JavaScript”>├── src│ ├── app│ │ ├── app.component.html│ │ ├── app.component.scss│ │ ├── app.component.spec.ts│ │ ├── app.component.ts│ │ └── event-binding│ │ ├── event-binding.component.html│ │ ├── event-binding.component.scss│ │ └── event-binding.component.ts│ ├── …</code></pre><p>批改 <code>event-binding.component.html</code> 增加一个按钮,绑定一个点击事件,同时在模板中通过 <code>*ngIf=“relearned”</code> 语法增加一个条件判断。</p><pre><code class=“JavaScript”><p *ngIf=“relearned”>很快乐看到你重新学习 Angular 这款优良的框架!</p><button (click)=“startRelearn()">开始重学 Angular</button></code></pre><p>EventBindingComponent 组件中增加一个 <code>relearned</code> 属性和 <code>startRelearn</code> 函数:</p><pre><code class=“JavaScript”>import { Component } from ‘@angular/core’;import { CommonModule } from ‘@angular/common’;@Component({ selector: ‘app-event-binding’, standalone: true, imports: [CommonModule], templateUrl: ‘./event-binding.component.html’, styleUrls: [’./event-binding.component.scss’]})export class EventBindingComponent { relearned = false; startRelearn() { this.relearned = true; }}</code></pre><p>最初在 AppComponent 组件中导入 EventBindingComponent,并在模板中插入 <code><app-event-binding></app-event-binding></code> ,运行的成果如下:<br/><br/>当用户点击按钮时会调用 <code>startRelearn</code> 函数,设置 <code>relearned</code> 属性为 true,模板中的 ngIf 构造指令检测到数据变动,渲染 p 标签。<br/> <code>(click)=“startRelearn()"</code> 为 Angular 事件绑定语法, <code>()</code> 内为绑定的事件名称,<code> =</code> 号右侧为模板语句,此处的模板语句是调用组件内的 <code>startRelearn()</code> 函数,当然此处的模板语句能够间接改为 <code> (click)=“relearned = true”</code> ,浏览器所有反对的事件都能够通过 () 包裹应用。</p><h2>循环渲染列表</h2><p>除了条件判断与事件绑定外,利用中最罕用的就是循环渲染元素,咱们通过如下命令创立一个 ng-for 组件</p><pre><code class=“JavaScript”>ng generate component ng-for –standalone</code></pre><p>同时在组件中新增 <code>items</code> 属性,设置为数组。</p><pre><code class=“JavaScript”>import { Component } from ‘@angular/core’;import { CommonModule } from ‘@angular/common’;@Component({ selector: ‘app-ng-for’, standalone: true, imports: [CommonModule], templateUrl: ‘./ng-for.component.html’, styleUrls: [’./ng-for.component.scss’],})export class NgForComponent { items = [ { id: 1, title: ‘Angular 怎么不火呢?’, }, { id: 2, title: ‘Angular 太牛逼了!’, }, { id: 3, title: ‘优良的前端工程师和框架无关,然而 Angular 会让你更快的成为优良前端工程师!’, }, ];}</code></pre><p>最初在 AppComponent 中导入 <code>NgForComponent</code> 后在模板中通过 <code><app-ng-for></app-ng-for></code> 渲染 NgForComponent 组件。</p><pre><code class=“JavaScript”>import { Component } from ‘@angular/core’;import { CommonModule } from ‘@angular/common’;import { NgForComponent } from ‘./ng-for/ng-for.component’;@Component({ selector: ‘app-root’, templateUrl: ‘./app.component.html’, standalone: true, imports: [CommonModule, NgForComponent], styleUrls: [’./app.component.scss’],})export class AppComponent { message = ‘Hello Angular Relearning!’; relearned = false; startRelearn() { this.relearned = true; }}</code></pre><p>渲染后的成果为:<br/></p><h2>路由</h2><p>以上咱们简略增加了三个示例组件:</p><ul><li> <code>event-binding</code> 展现事件绑定</li><li> <code>ng-for</code> 展现循环渲染一个列表</li><li>咱们再把条件判断的示例从 AppComponent 中挪动到独立的示例 <code>ng-if</code> 组件中</li></ul><p>接下来通过 router 路由模块别离展现这三个示例,首先须要批改 main.ts,bootstrapApplication 启动利用的第二个参数,通过 <code>provideRouter(routes)</code> 函数提供路由赋值给 providers ,routes 设置三个示例组件的路由,输出根路由的时候跳转到 ng-if 路由中,代码如下:</p><pre><code class=“JavaScript”>import { bootstrapApplication } from ‘@angular/platform-browser’;import { provideRouter, Routes } from ‘@angular/router’;import { AppComponent } from ‘./app/app.component’;import { NgForComponent } from ‘./app/ng-for/ng-for.component’;import { EventBindingComponent } from ‘./app/event-binding/event-binding.component’;import { NgIfComponent } from ‘./app/ng-if/ng-if.component’;const routes: Routes = [ { path: ‘’, redirectTo: ’ng-if’, pathMatch: ‘full’, }, { path: ’ng-if’, component: NgIfComponent, }, { path: ’event-binding’, component: EventBindingComponent, }, { path: ’ng-for’, component: NgForComponent, },];bootstrapApplication(AppComponent, { providers: [provideRouter(routes)],}).catch((err) => console.error(err));</code></pre><p>在 AppComponent 根组件中导入 <code>RouterModule</code> 模块。</p><pre><code class=“JavaScript”>import { Component } from ‘@angular/core’;import { CommonModule } from ‘@angular/common’;import { RouterModule } from ‘@angular/router’;@Component({ selector: ‘app-root’, templateUrl: ‘./app.component.html’, standalone: true, styleUrls: [’./app.component.scss’], imports: [CommonModule, RouterModule],})export class AppComponent { title = ‘Hello Angular Relearning!’; constructor() {}}</code></pre><p>这样在根组件的模板中就能够应用 <code>router-outlet</code> 和 <code>routerLink</code> 组件或者指令。</p><pre><code class=“JavaScript”><h2>{{ title }}</h2><div> <a [routerLink]=”[’./ng-if’]">NgIf</a> | <a [routerLink]=”[’./event-binding’]">EventBinding</a> | <a [routerLink]="[’./ng-for’]">NgFor</a></div><router-outlet></router-outlet></code></pre><ul><li>router-outlet 为路由占位器,Angular 会依据以后的路由器状态动静渲染对应的组件并填充它的地位</li><li><p>routerLink 让 a 标签元素成为开始导航到某个路由的链接,关上链接会在页面上的 router-outlet 地位上渲染对应的路由组件<br/>运行成果如下:<br/><br/>示例代码: ng-relearning v0.4 分支</p><h2>HttpClient 近程调用</h2><p>在 Angular 中内置了近程调用的 <code>HttpClient</code> 模块,能够间接通过此模块调用 API。<br/>批改 ng-for 的示例,在 NgForComponent 组件中通过构造函数注入 <code>HttpClient</code> 服务,并调用 HttpClient 的 get 函数获取 todos 列表。</p></li></ul><pre><code class=“JavaScript”>import { Component, OnInit } from ‘@angular/core’;import { CommonModule } from ‘@angular/common’;import { HttpClient } from ‘@angular/common/http’;export interface Todo { id?: string; title: string; created_by?: string;}@Component({ selector: ‘app-ng-for’, standalone: true, imports: [CommonModule], templateUrl: ‘./ng-for.component.html’, styleUrls: [’./ng-for.component.scss’],})export class NgForComponent implements OnInit { todos!: Todo[]; constructor(private http: HttpClient) {} ngOnInit(): void { this.http .get<Todo[]>(‘https://62f70d4273b79d015352b5e5.mockapi.io/items') .subscribe((items) => { this.todos = items; }); }}</code></pre><p>个别组件初始化的工作举荐放在 ngOnInit 生命周期函数中,比方初始化数据等。Angular 会在组件所有的 Input 属性第一次赋值后调用 ngOnInit 函数,生命周期更多理解参考: lifecycle-hooks 。<br/>而后在模板中通过 *ngIf 判断数据是否有值显示加载状态。</p><pre><code class=“JavaScript”><ol *ngIf=“todos else loading”> <li *ngFor=“let item of todos”> {{ item.title }} </li></ol><ng-template #loading> <p>加载中…</p></ng-template></code></pre><p>运行后发现代码报错,没有 HttpClient 的 provider 。<br/><br/>咱们须要批改 main.ts 增加 <code>provideHttpClient()</code> 到 providers 中去,这样才能够在零碎中通过依赖注入应用 http 相干的服务。</p><blockquote>留神:http 服务在 <code>@angular/common/http</code> 模块中。</blockquote><pre><code class=“JavaScript”>import { bootstrapApplication } from ‘@angular/platform-browser’;import { provideRouter, Routes } from ‘@angular/router’;import { provideHttpClient } from ‘@angular/common/http’;import { AppComponent } from ‘./app/app.component’;…const routes: Routes = [ …];bootstrapApplication(AppComponent, { providers: [provideRouter(routes), provideHttpClient()],}).catch((err) => console.error(err));</code></pre><p>运行成果为:<br/></p><h2>表单</h2><p>通过如下命令创立一个 forms 表单示例组件<br/><code>ng g c forms –standalone –skip-tests</code><br/>批改 FormsComponent 为如下代码,增加 user 对象蕴含 name 和 age,同时增加 save 函数传入 form,验证通过后弹出提交胜利提醒。</p><pre><code class=“JavaScript”>import { Component } from ‘@angular/core’;import { CommonModule } from ‘@angular/common’;import { FormsModule, NgForm } from ‘@angular/forms’;@Component({ selector: ‘app-forms’, standalone: true, imports: [CommonModule, FormsModule], templateUrl: ‘./forms.component.html’, styleUrls: [’./forms.component.scss’],})export class FormsComponent { user = { name: ‘’, age: ‘’, }; save(form: NgForm) { if (form.valid) { alert(‘Submit success’); } }}</code></pre><p>forms.component.html 编写一个表单,蕴含 name 输入框和 age 数字输入框,通过 <code>[(ngModel)]</code> 双向绑定到 user 对象的 name 和 age,设置 name 输入框为 required 必填,age 数字输入框最大值和最小值为 100 和 1,最终增加 type=“submit” 的提交按钮并在 form 上绑定 <code>(submit)=“save(userForm)"</code> 提交事件。</p><pre><code class=“JavaScript”><form name=“user-form” #userForm=“ngForm” (submit)=“save(userForm)"> <input name=“name” #userName=“ngModel” [(ngModel)]=“user.name” required=”” placeholder=“请输出用户名” /> <input name=“age” type=“number” #userAge=“ngModel” [(ngModel)]=“user.age” max=“100” min=“1” placeholder=“请输出年龄” /> <button type=“submit”>Submit</button> <div *ngIf=“userForm.invalid”> <div *ngIf=“userName.invalid”>用户名不能为空</div> <div *ngIf=“userAge.invalid”>年龄必须在 1-100 之间</div> </div></form></code></pre><p>运行成果为:<br/><br/><code>[()]</code> 是 Angular 双向绑定的语法糖, <code>[(ngModel)]=“value”</code> 相当于</p><pre><code class=“JavaScript”><input [ngModel]=“value” (ngModelChange)=“value = $event” /></code></pre><p>只有组件有一个输出参数和输入事件,且命名合乎 <code>xxx</code> 和 <code>xxxChange</code> ,这个 xxx 能够是任何值,这样就能够通过 <code>[(xxx)]=“value”</code> 这样的语法糖应用,ngModel 是 Angular Forms 表单内置的一个合乎这种规定的语法糖指令,理解更多查看: two-way-binding 。</p><h2>应用第三方类库 material</h2><p>通过如下命令引入 material 组件库。<br/><code>ng add @angular/material</code><br/>依据提醒抉择款式以及其余选项,最终装置依赖并主动批改代码:</p><ul><li>批改 package.json 的 dependencies 增加 <code> @angular/cdk</code> 和 <code>@angular/material</code> </li><li>会主动引入抉择的 theme 主题,在 angular.json 文件中增加 styles</li><li>批改 main.ts 导入 BrowserAnimationsModule (这是因为抉择了反对动画)</li><li>引入 google 字体和款式</li></ul><p>次要变更如下:<br/><br/>让咱们在之前的 forms 示例中导入 <code>MatButtonModule</code> 和 <code>MatInputModule</code> 模块,应用表单和按钮组件丑化一下界面。</p><pre><code class=“JavaScript”>import { Component } from ‘@angular/core’;import { CommonModule } from ‘@angular/common’;import { FormsModule, NgForm } from ‘@angular/forms’;import { MatButtonModule } from ‘@angular/material/button’;import { MatInputModule } from ‘@angular/material/input’;@Component({ selector: ‘app-forms’, standalone: true, imports: [CommonModule, FormsModule, MatButtonModule, MatInputModule], templateUrl: ‘./forms.component.html’, styleUrls: [’./forms.component.scss’],})export class FormsComponent { user = { name: ‘’, age: ‘’, }; save(form: NgForm) { if (form.valid) { alert(‘Submit success’); } }}</code></pre><p>forms.component.html 批改为:</p><pre><code class=“JavaScript”><form name=“user-form” #userForm=“ngForm” (submit)=“save(userForm)"> <mat-form-field appearance=“fill”> <mat-label>Name</mat-label> <input matInput name=“username” #userName=“ngModel” [(ngModel)]=“user.name” required=”" placeholder=“请输出用户名” /> </mat-form-field> <mat-form-field appearance=“fill”> <mat-label>Age</mat-label> <input matInput name=“age” type=“number” #userAge=“ngModel” [(ngModel)]=“user.age” max=“100” min=“1” placeholder=“请输出年龄” /> </mat-form-field> <div class=“error-messages” *ngIf=“userForm.invalid”> <div *ngIf=“userName.invalid”>用户名不能为空</div> <div *ngIf=“userAge.invalid”>年龄必须在 1-100 之间</div> </div> <button mat-raised-button color=“primary” type=“submit”>Submit</button></form></code></pre><p>最终的丑化成果为:<br/><br/>同时也应用 <code>MatTabsModule</code> 替换了之前导航链接。<br/>留神:因为通过 <code>ng add @angular/material</code> 装置 <code>material</code> 后批改了 angular.json 文件,须要重新启动才能够看到新的款式,Angular CLI 目前还没有做到 angular.json 变动后实时更新。</p><h2>应用服务</h2><p>在 Angular 中举荐应用服务把相干业务逻辑从组件中独立进来,让组件只关注视图,咱们革新一下 ng-for 示例,先通过以下命令创立一个服务:<br/><code>ng g s todo –skip-tests</code><br/>Angular CLI 会主动帮咱们在 app 目录创立一个 <code>todo.service.ts</code> 文件,代码为:</p><pre><code class=“JavaScript”>import { Injectable } from ‘@angular/core’;@Injectable({ providedIn: ‘root’})export class TodoService { constructor() { }}</code></pre><p>批改代码通过构造函数注入 <code>HttpClient</code> 服务,并增加 <code>fetchTodos</code> 函数调用 HttpClient 的 get 函数获取 todos 列表,并在最初赋值给服务的 todos 属性。</p><pre><code class=“JavaScript”>import { Injectable } from ‘@angular/core’;import { HttpClient } from ‘@angular/common/http’;import { tap } from ‘rxjs’;export interface Todo { id?: string; title: string; created_by?: string;}@Injectable({ providedIn: ‘root’,})export class TodoService { todos!: Todo[]; constructor(private http: HttpClient) {} fetchTodos() { return this.http .get<Todo[]>(‘https://62f70d4273b79d015352b5e5.mockapi.io/items') .pipe( tap((todos) => { this.todos = todos; }) ); }}</code></pre><p>而后咱们批改 ng-for 的示例,这次通过 <code>inject</code> 函数在属性初始化的时注入 TodoService 服务,在初始化时调用 fetchTodos 获取数据。</p><blockquote>Angular 在 v14 之前只能通过结构函数参数注入服务,在 v14 版本之后能够在属性初始化、构造函数以及 factory 函数中通过 <code>inject</code> 注入服务或者其余供应商,理解更多参考: inject</blockquote><pre><code class=“JavaScript”>import { Component, inject, OnInit } from ‘@angular/core’;import { CommonModule } from ‘@angular/common’;import { Todo, TodoService } from ‘../todo.service’;@Component({ selector: ‘app-ng-for’, standalone: true, imports: [CommonModule], templateUrl: ‘./ng-for.component.html’, styleUrls: [’./ng-for.component.scss’],})export class NgForComponent implements OnInit { todoService = inject(TodoService); constructor() {} ngOnInit(): void { this.todoService.fetchTodos().subscribe(); }}</code></pre><p>而后在模板中间接应用 <code>todoService</code> 的 todos 数据。</p><pre><code class=“JavaScript”><ol *ngIf=“todoService.todos else loading”> <li *ngFor=“let item of todoService.todos”> {{ item.title }} </li></ol><ng-template #loading> <p>加载中…</p></ng-template></code></pre><p>通过上述示例咱们发现,在 Angular 中把数据和逻辑抽取到服务中,只有设置为组件的属性就能够在模板中绑定服务的数据(也就是状态),这些状态变动后,Angular 视图会实时更新,同时只有多个组件注入同一个服务就实现了数据共享,这样轻松解决了前端的<strong>逻辑复用</strong>和<strong>数据共享</strong>两大难题,这一点和其余框架有很大的不同:</p><ul><li>React 必须要通过 setState 或者 Hooks 的 set 函数设置状态才会从新渲染</li><li>Vue 必须定义在 data 数据中或者通过 ref 标记</li></ul><p>Angular 什么也不须要做是因为通过 Zone.js 拦挡了所有的 Dom 事件以及异步事件,只有有 Dom Event、Http 申请,微工作、宏工作都会触发脏查看,从根组件始终查看到所有叶子组件,只有有数据变动就会更新视图,那么上述的示例中,fetchTodos 函数有一个 API GET 申请,这个申请被 Angular 拦挡,申请完结后赋值 todos 数据后,Angular 就开始从 app-root 根组件向下查看,发现 todos 数据变动了,更新视图。</p><h2>指令组合 API (Directive Composition API)</h2><p>通过服务在 Angular 中能够很好做逻辑复用,然而对于一些偏 UI 操作的逻辑复用,有时候应用服务会多加一些样板代码,因为在 Angular 中除了组件还有一个指令的概念,指令是对曾经的组件或者元素增加行为,咱们在后面示例中应用的 <code>NgIf</code> 、 <code>NgFor</code> 、 <code>NgModel</code> 都是内置的指令,有时候须要反复利用不同的指令组合,如果要实现逻辑复用只能通过 Class 继承和 Mixin 实现相似多重继承的成果,那么指令组合 API 就是解决此类问题的。<br/>让我先通过如下命令创立一个 <code>color</code> 设置文本色彩的指令:<br/><code>ng g d color –standalone –skip-tests</code><br/>而后批改 <code>color.directive.ts</code> 代码如下:</p><pre><code class=“JavaScript”>import { Directive, ElementRef, Input, OnInit, Renderer2 } from ‘@angular/core’;@Directive({ selector: ‘[appColor]’, standalone: true,})export class ColorDirective implements OnInit { @Input() color!: string; constructor(private elementRef: ElementRef, private renderer: Renderer2) {} ngOnInit(): void { this.renderer.setStyle(this.elementRef.nativeElement, ‘color’, this.color); }}</code></pre><p>次要通过注入获取 ElementRef,并通过 Renderer2 服务设置 DOM 元素的 color 款式。</p><blockquote>elementRef.nativeElement 就是以后指令绑定的 DOM 元素,通过 Renderer2 操作 DOM 次要是为了兼容不同的环境,比方服务端渲染等。</blockquote><p>这样在 AppComponent 组件中导入 AppColor 后就能够通过如下模板应用:<br/><code><div appColor color=“red”>我是红色</div></code><br/>展现成果为:<br/><br/>那么咱们再创立一个 <code>directive-composition</code> 组件,这个组件的选择器是 <code>app-directive-composition</code> ,如果这个组件也想要具备设置字体色彩的性能,咱们只能这样应用</p><pre><code class=“JavaScript”> <app-directive-composition appColor color=“blue”>我的字体色彩时红色</app-directive-composition></code></pre><p>如果是多个指令,等于须要在应用的中央自行组合,咱们革新一下这个组件,通过 <code>hostDirectives</code> 设置 <code>ColorDirective</code> 并指令 inputs color。</p><pre><code class=“JavaScript”>import { Component } from ‘@angular/core’;import { CommonModule } from ‘@angular/common’;import { ColorDirective } from ‘../color.directive’;@Component({ selector: ‘app-directive-composition’, standalone: true, imports: [CommonModule, ColorDirective], template: ‘<ng-content></ng-content>’, styleUrls: [’./directive-composition.component.scss’], hostDirectives: [ { directive: ColorDirective, inputs: [‘color’], }, ],})export class DirectiveCompositionComponent {}</code></pre><p>这样间接应用 <code>app-directive-composition</code> 传入参数 color 就具备了 appColor 指令的性能。</p><pre><code class=“JavaScript”><div appColor color=“red”>我是红色</div><app-directive-composition color=“blue”>我的字体色彩时红色</app-directive-composition></code></pre><p>展现成果如下:<br/><br/>以上就是组合指令 API 的魅力所在。</p><h2>总结</h2><p>以上我是想通过一种渐进式的 Angular 入门让大家初步理解 Angular 这款我认为特地优良的框架,摈弃了 Modules 后也更加适宜新手入门,站在 2022 乃至 2023 年的工夫点来说,它并不是一个落后的框架,反而是更加的先进,同时 Angular 也在一直的变得更好。 同时上述的性能只是 Angular 框架的冰山一角,深刻后还有更有的宝藏等着你去开掘。<br/>以上所有示例仓储地址为: https://github.com/why520crazy/ng-relearning <br/>Open in StackBlit: https://stackblitz.com/github/why520crazy/ng-relearning</p></article> ...

November 30, 2022 · 8 min · jiezi

关于angular:Angular-14-inject-函数使用过程中的一些注意事项

inject 函数只能用于结构器阶段,这意味着其只能在结构器函数作用域(constructor function scope)和字段初始化器(field initializers)中应用。 下列代码会遇到运行时谬误(runtime error): 因为应用到了 inject 函数的 fetchEntity 办法,在构造函数作用域之外的上下文里被调用,不合乎 inject 函数的应用前提。 当在构造函数阶段之外调用 refreshEntity 办法时,下面的代码将引发运行时谬误,例如在单击按钮时,因为 fetchEntity 应用了 inject()。 当用于初始化 entity$ 属性时,它不会抛出此谬误,因为它处于构造函数阶段。 解决方案也不难,应用 JavaScript 的 closure 闭包概念: 在下面的例子中,因为咱们应用了一个闭包,所以咱们可能将注入的 HttpClient 和 ActivatedRoute 存储在闭包范畴内,并且依然应用返回函数中的值。因而,咱们能够在构造函数阶段之外利用 inject() 函数。 正如咱们在下面的示例中曾经看到的那样,应用 inject 函数可能放弃组件合乎繁多职责的设计准则(single responsibility)和无依赖注入。 依赖注入或 DI 是 Angular 中的基本概念之一。 DI 被连贯到 Angular 框架中,并容许具备 Angular 装璜器的类(例如组件、指令、管道和可注入)配置它们须要的依赖项。 DI 零碎中存在两个次要角色:依赖消费者和依赖提供者。 咱们不再间接在组件构造函数中依赖 HttpClient 或 ActivatedRoute。 相同,咱们创立了一个可注入的函数来解决业务逻辑,这种思路相似于咱们如何将逻辑提取到一个 Facade 服务中。并且 Angular 的最佳事件认为,Component 须要实现的业务逻辑最好都封装到一个专门的 Facade 服务中。应用 inject() 函数,咱们不再须要组件中的任何依赖项。

November 6, 2022 · 1 min · jiezi

关于angular:angular-页面缩放时调整css

问题背景登录界面: 因为之前登录框设置的是相对地位,所以在浏览器页面放大后,登录框的地位会缩到看不见的中央。 比方left字段,设置的是370px。 当页面宽度放大至400左右时,登录框就会因为向右偏移了370px,而导致登录框隐没。 .loginBox { background-color: #F0F4F6; /*上divcolor*/ border: 1px solid #BfD6E1; border-radius: 5px; color: #444; margin: auto; width: 388px; height: 400px; left: 370px; top: 200px;}之后想谷歌找一下相干的解决办法,没有搜到,也可能是关键词不对。 于是想尝试当页面缩放时,动静地批改css款式。 Angular 获取页面缩放事件什么是 BOM首先咱们要理解BOM。 BOM ( Browser Object Model )即浏览器对象模型, 它提供了独立于内容而与浏览器窗口进行交互的对象,其外围对象是window。 另外BOM是浏览器厂商在各自浏览器上定义的,各个浏览器会有不一样的中央。 window窗口有很多事件,比方有:Scroll: 鼠标滚轮滑动事件Back: 页面后退Resize:窗口大小变更等 BOM比 DOM更大,它蕴含DOM能够形象地表述: window对象是浏览器的顶级对象,它具备双重角色。 1.它是 JS 拜访浏览器窗口的一个接口。 2.它是一个全局对象。 定义在全局作用域中的变量、函数都会变成window对象的属性和办法。 例如,在控制台定义一个name 监听window的 resize事件Resize:窗口大小变更。 就是本次咱们须要监听的指标 在Angular中,咱们能够应用 Observable 类提供的 fromEvent 函数来监听resize事件 代码: ngOnInit(): void { fromEvent(window, 'resize') .pipe(throttleTime(50), debounceTime(50)) .subscribe((event) => { console.log('页面变动了'); }}后果:当调整窗口大小时,控制台打印。 能够看到window也有的这个属性。 ...

November 2, 2022 · 2 min · jiezi

关于angular:angular中使用antd的tooltip主动关闭打开toolTip框的操作办法

在绝大部分状况下,antd的tooltip都体现的很好,非常好用,然而在一些tooltip在须要跳转到其余页面的按钮上的时候,跳转当前tooltip也会有继续存在,不隐没的问题,所以这时候就须要可能在点击的时候被动敞开tooltip 不啰嗦,间接上代码: //html<button nz-button nzType="default" nzDanger style="margin: 5px;"#toolTip="nzTooltip" nzTooltipTitle="测试" nz-tooltip (click)="testBtn()">测试用</button>//ts@ViewChild('toolTip',{static:false}) toolTip!:NzTooltipBaseDirective;testBtn(){ this.toolTip.hide();}要留神的点就是:html中绑定的对象须要为nzTooltip,留神大小写。ts中static要么不写,要么为false,一旦写了true那就会报toolTip未定义的问题。 成果如下,红色的是配置了点击敞开的操作 多提一嘴,如果在angular应用ngfor批量生成的tooltip,如果依照下面写的@viewChild进行敞开操作的话,就只有第一个会触发敞开,这时候须要将代码改为@ViewChildren('toolTip') toolTip!:QueryList<NzTooltipBaseDirective>同时ts中批改按钮操作为this.toolTip.forEach(item=>{item.hide()}) 完。

November 2, 2022 · 1 min · jiezi

关于angular:记录遇到的问题

文件过大写文件上传的时候,前台和后盾都遇到了文件过大的谬误。前台用的是nginx转发, 后盾用的是spring boot 前台:413 Request Entity Too Large" angular 后盾: 之后理解到,nginx 默认上传的最大body是 1M, 超过1M的数据申请就会报413谬误 解决: 在nginx.conf里设置配置: server { client_max_body_size 20M; ...}angular循环依赖 这里HttpInterceptor造成了循环依赖。 解决: export class HttpErrorInterceptor implements HttpInterceptor { public static DONT_INTERCEPT_HEADER_KEY = 'http-error-do-not-intercept'; thyNotifyService = this.injector.get(ThyNotifyService); constructor(private readonly injector: Injector) { }}间接在构造函数中结构Injector, 而后再应用this.injector.get(ThyNotifyService)结构service。 实际上,咱们间接获取了根注入器。再获取了注入器中的service。 这样的话,以后的类就不会间接结构ThyNotifyService,而是获取了根注入器。从而防止了循环依赖

October 12, 2022 · 1 min · jiezi

关于angular:本周遇到问题

问题一:须要进行深拷贝情景: this.divisionalWorksTemplateService.getById(id) .subscribe((value) => { this.chooseDivisionalWorksTemplate = value; this.divisionalWorksTemplate = value; })咱们想要这两个变量初始值雷同,然而后须要进行的操作不同,然而如果咱们这样操作会导致这两个变量其实都指向同一个地址。也就是说咱们进行如下操作:this.chooseDivisionalWorksTemplate.name = 'test'就会导致divisionalWorksTemplate.name也被改为了test。这时咱们就须要进行深拷贝,之前咱们也有说过能够间接应用lodash来进行深拷贝,如下: import * as _ from 'lodash'; . . . this.chooseDivisionalWorksTemplate = value; this.divisionalWorksTemplate = _.cloneDeep(this.chooseDivisionalWorksTemplate);问题二:angular set函数的触发 本周在set函数触发上遇到了问题:数据筹备: export interface TestFather{ id: number testSons: TestSon[]}export interface TestSon{ id: number number: number}testSon: @Input() set testFather(testFather: TestFather) { console.log("传入testFather" + testFather.id) } @Input() set testSon(testSon: TestSon) { console.log("传入testSon" + testSon.id) } @Input() set number(number: number){ console.log("传入number" + number) } @Input() set testSons(testSons: TestSon[]) { console.log("传入testSons" + testSons.length) }testFather: ...

October 12, 2022 · 1 min · jiezi

关于angular:angular-附件上传-以及深拷贝

ts里的深拷贝 原本想着点击勾销,复原数据后,能勾销掉实体的改变。比方如下图, 复原数据后,A 能复原到 a 数据的状态。 然而,通过测试发现,并没有。 测试的代码, 能够看到。 带星号的代码 1.ngOnInit()的时候复制了父组件传递的数据a, A = a。 2.在执行close()的时候复原了数据, A = a。 @Input()majorItemTemplate!: MajorItemTemplatengOnInit(): void { this.formGroup.addControl(this.formKey.percent, new FormControl(this.majorItemTemplate.percent, Validators.required)); this.formGroup.addControl(this.formKey.name, new FormControl(''))* this.minorItemTemplates = this.majorItemTemplate.minorItemTemplates; } close() { this.dialog.close(); // 复原数据* this.minorItemTemplates = this.majorItemTemplate.minorItemTemplates; this.formGroup.get(this.formKey.percent)?.setValue(this.majorItemTemplate.percent); this.formGroup.get(this.formKey.name)?.setValue(''); } 之后,通过下面的动图能够看到, 复原数据,并没有胜利。 找到问题通过多处的 console.log 输入察看到, 复原数据的时候, 父组件的传递的数据曾经扭转 所以,无论怎么复原, 都无奈胜利。 大家上c++课的时候也讲过深浅拷贝 这就是浅拷贝带来的问题: 创立一个新对象,而后将以后对象的非动态字段复制到该新对象,如果字段是值类型的,那么对该字段执行复制;如果该字段是援用类型的话, 因而,原始对象及其正本援用同一个对象。 所以咱们只有把浅拷贝换成深拷贝就好了, 如下图。 [...object]想起来,以前用到这种办法来进行深拷贝。 // 复原数据 this.minorItemTemplates = [...this.majorItemTemplate.minorItemTemplates];即es6里的操作符,三个点 ... ...

September 28, 2022 · 2 min · jiezi

关于angular:angular-指令

咱们之前并没有接触过指令,然而可能对这个词并不生疏,例如咱们常常会看到这样的编译器报错:那么咱们由此也能够大抵猜测出指令是在那里应用的了。 要想进一步理解指令就须要去官网文档中去理解了。 import {Directive} from '@angular/core';@Directive({ selector: '[appCheckSingle]'})export class CheckSingleDirective {}首先就是如何在组件中应用指令,也就是咱们所相熟的selector,这里的selector规定与组件不同,组件在应用是是间接作为标签来应用如: <app-xxxSelect></app-xxxSelect>而指令是作为标签的属性来应用的,也就是说下面的指令要这样来申明此标签用到了这个指令。 <div appCheckSingle></div>在官网文档中咱们还能够理解到咱们还能够靠selector来设置让哪些标签能够调用该指令。比方咱们只想让类型为输入框的input标签调用此指令: @Directive({ selector: 'input[type=text]'})export class CheckSingleDirective implements OnInit, OnDestroy {. . . @Input() appCheckSingle = {} as {id: number};. . .} 然而上述性能尽管能够失常实现然而编译器会产生报错: ESLint: The selector should start with one of these prefixes: &quot;app&quot; (https://angular.io/guide/styleguide#style-02-08) (@angular-eslint/directive-selector)说咱们前缀不符合要求,这是因为在angular配置文件中规定了其默认格局,将下述代码中"prefix": "app",正文掉就不会产生报错,可能是angular在版本更新中漠视了此点或是默认状况下不容许这么操作? 配置文件.eslintrc.json中有其默认格局: "@angular-eslint/directive-selector": [ "error", { "type": "attribute", "prefix": "app", "style": "camelCase" } ]1、须要是属性2、以app为前缀3、命名格局为小驼峰 咱们在官网文档中还能够得悉指令和组件一样也有着生命周期,也能够通过@input、@outPut来和组件进行数据的传递。 那么如果一个组件中退出了多个指令那么这多个指令间能够间接进行互动吗?答案是能够,能够通过exportAs来将此指令抛出。 @Directive({ selector: '[appCheckAll]', exportAs: 'appCheckAll'})咱们在应用时像上面这样即可: ...

September 28, 2022 · 1 min · jiezi

关于angular:总结本周遇到的问题921

本中要写到一个页面须要进行级联查问以后修建是属于服务区下的,所以咱们抉择了服务区能力持续依据所抉择的服务区,也就是说如果咱们抉择完 服务区一,再抉择修建一,如果咱们此时更换抉择的服务区里所该当应该去除所抉择的修建。依照之前的逻辑写的话只有在传入新的服务区后获取到对应的修建从而更改抉择列表项,所选内容就会主动重置为空,如下所示: @Input() set serviceAreaId(serviceId: number) { this.buildingId.setValue(null); if (Number.isInteger(serviceId)) { this.listOfOption = []; this.serviceAreaService.getAllBuildingByServiceAreaId(serviceId) .subscribe(buildings => { this.buildings = buildings; buildings.forEach(building => { this.listOfOption.push( { label: building.name, value: building.id } ) }) }) } else { this.listOfOption = []; } }然而这在<thy-custom-select>中并不实用,也就是说就算列表曾经更新然而并不会重置或是删除所选我的项目。这很显著不合乎咱们的需要,所以首先就是要找官网文档中有没有给出调用清空所选项的API。thy-custom-select官方网站然而很惋惜,并没有给出清空选项的API,那么就只能找其余方法。起初认为是子组件没有及时从新渲染导致的,起初依据这些关键词搜寻,并尝试在其获取完数据后对其进行从新渲染,然而并没有作用。 constructor(private serviceAreaService: BuildingService, private cdr: ChangeDetectorRef) { } this.serviceAreaService.getAllBuildingByServiceAreaId(serviceId) .subscribe(buildings => { this.buildings = buildings; buildings.forEach(building => { this.listOfOption.push( { label: building.name, value: building.id } ) }) //数据绑定变更的时候用ChangeDetectorRef驱动angular更新视图 this.cdr.markForCheck(); this.cdr.detectChanges(); })之后尝试改为angular原生的抉择组件,尽管能够实现想要的成果,然而款式与<thy-custom-select>有差异,格调不对立 ...

September 21, 2022 · 2 min · jiezi

关于angular:angular-springboot-实现文件上传

Angular1.html抉择文件首先写用户点击上传文件的按钮,按本人须要编写,上面只是一个简略的例子 <div class=" row p-3"> <label class="col-3 text-right col-form-label" >文件<code>*</code></label> <input style="display: none" type="file" class="file-input" (change)="onFileSelected($event)" #fileUpload> <div class="file-upload col"> <button type="button" (click)="fileUpload.click()" class="upload-btn btn-sm btn-success"> <i class="fas fa-file-image"></i> 抉择文件 </button> </div> </div>用户抉择文件后触发onFileSelected($event)办法,将文件传入c层的办法 2.c层获取文件onFileSelected(event) { const file: File = event.target.files[0]; if (file) { this.file = file; } }此时将用户上传的file存在变量file中 文件上传如果不须要显示进度间接用post办法传输即可 constructor(private httpClient: HttpClient) { }public saveFile(file:File): Observable<string> { formData = new FormData(); formData.append('file', this.file); return this.httpClient.post<string>(`/file`, formData); }要用formdata传file 须要显示进度 const req = new HttpRequest('POST', '/file', formData, { reportProgress: true }); // 产生一个 event 流 return this.http.request(req).subscribe((event: HttpEvent<{}>) => { if (event.type === HttpEventType.UploadProgress) { if (event.total > 0) { // 计算上传的百分比 const percent = event.loaded / event.total * 100; } } });到此为止,angular的工作就实现了 ...

September 15, 2022 · 1 min · jiezi

关于angular:angular中单元测试中Uncaught-undefined-thrown

单元测试没跑通过 在跑单元测试的时候遇到了这个问题, 控制台报错: An error was thrown in afterAll Uncaught undefined thrown 浏览器: 1.因为没有报错组件的地位,过后第一工夫想到的是,可能在某个单元测试afterAll()办法中,呈现了问题。于是用全局搜寻:afterAll。 然而并没有找到该关键字。 2.同时在mingao的电脑上,同样的代码,他那里跑单元测试并不会呈现该问题。 只有我这里以及机器人跑的时候有这个问题。 3.之后也去之前的我的项目中找了afterAll 关键字, 也没有找到。 4.谷歌之后,发现有人遇到和我一样的问题,只不过我的是undefind. 他的是object. 尝试一显示第一的是一个100多赞的答案,他说申明了两个beforeEach, 会造成竞争状况,导致运行失败。所以他把两个beforeEach 合并成同步了。 先简略形容async。 async函数的实质就是返回了一个promise对象,在这个函数里里咱们加上await后,即便调用的是异步代码,它也会变成相似于同步,只有让这个异步代码执行完后,才会执行上面的同步代码来执行。首先不说是否能解决问题。 实际上,合并与不合并,只是angular官网举荐的两种不同的设置: 离开设置: 组合设置: 所以这只是两种不同的写法。 我预计他只是,忘了写await了,导致两个beforeEach同步执行了。 钻研了一下这种办法,最终不起作用。 尝试二第二个答案: 在单元测试中增加这个: 在每个单元测试后销毁夹具 afterEach(() => { fixture.destroy(); });因为是跑全局单元测试时报的错,所以我并不知道在哪个单元测试中增加这个项。 好在目前组件并不算多,于是我挨个尝试了一下。 最初在menu.component组件中增加了该项后。单元测试没有再报错。 钻研起因:留神到该组件和别的中央不一样的是, 增加了teardown: { destroyAfterEach: false } 之前也介绍过:angular13 新版本中每次测试的时候会清理DOM, 该项默认为true。 设置为false之后咱们能够在fit下, 察看到v层的变动。 否则看见的是空白。 所以我猜测是设置了destroyAfterEach: false 才导致的下面的报错。 于是我删除了teardown: { destroyAfterEach: false } 和 afterEach(() => {fixture.destroy();}) ...

September 12, 2022 · 1 min · jiezi

关于angular:总结本周遇到的问题

之前的文章中有写过在thy-form 与 formgroup 联结应用时呈现的问题,过后间接认定它们不能同时应用。在起初的尝试中发现能够同时应用,上面来讲一下应用时遇到的问题。原文章: 记录最近遇到的问题 因为所给的示例代码中并没有给出配合formGroup应用的状况,所以只能尝试在其余标签中接入formGroup,如下 <form thyForm name="demoForm" #demoForm="thyForm" #demoNgForm="ngForm" class="ml-4"> <thy-form-group [formGroup]="formGroup" thyLabelText="名称" thyLabelRequired thyTipsMode="label"> <input [formControlName]="formKeys.name" thyInput class="ml-4" name="name" required placeholder="请输出名称"/> </thy-form-group><thy-form-group-footer> <button [disabled]="formGroup.invalid" [thyButton]="'primary'" (thyFormSubmit)="onSubmit(formGroup)"> 提交 </button></thy-form-group-footer></form>它的绝对应的提交形式是通过 thyFormSubmit 进行绑定,然而尝试着把它改为 click 发现也能够失常执行。这就呈现了一个问题,为什么不间接应用最原始的(click)绑定到button从而进行提交呢?还时说两者作用雷同只是因为咱们的应用习惯才导致总是通过(ngSubmit)进行绑定。上面是官网对于ngSubmit的解释,通过配置ngsumit来监听按钮被点击的事件。就是在点击Submit按钮时主动调用所绑定的办法。综上之所以引入这些绑定办法更多的是为了对立写法,配置起来更不便。 这次之所以说这个问题更多的是之前在通过click连贯提交办法时呈现了其余谬误,过后认为是由此引发的谬误然而起初再次凭记忆复现时并没有呈现谬误,所以在对谬误有猜测时要先验证本人的猜测,并不能太置信本人。 再来说一下本周遇到的一个小问题有如下情景须要咱们进行解决:以后页面是服务区治理,服务区治理能够查看 以后服务区的修建,然而修建治理也是菜单中的一栏,问题就是如何把服务区ID传到修建治理中。做到能够查看所有的修建也能够在服务区列表中点入查看修建从而只查看属于本服务区的修建。 之前对于查看XXX下的XX都是间接通过路由门路来传递参数即(serviceArea/1/building),然而很显著在此种状况下并不实用或者说如果硬要应用的话也不是不行,能够让路由 building和 serviceArea/1/building都跳转到修建页面,即如下配置 service-area-routing.module.ts:const routes: Routes = [. . . { path: ':id/building', loadChildren: () => import('../building/building.module').then(m => m.BuildingModule) }]app-routing.module.ts:const routes: Routes = [. . . { path: 'serviceArea', loadChildren: () => import('./service-area/service-area.module').then(m => m.ServiceAreaModule) }, { path: 'building', loadChildren: () => import('./building/building.module').then(m => m.BuildingModule) },]配置完之后再和之前一样获取数据然而这样的话就会导致路由很凌乱操作起来也并不不便,咱们必定是不能采取的。 ...

September 12, 2022 · 1 min · jiezi

关于angular:Angular-8开发拼多多WebApp-从基础到项目实战内附文档源码

download:Angular 8开发拼多多WebApp-从根底到我的项目实战内附文档源码如果咱们做时光机回到 Go 1.18 以至一路追溯到 Go 1.4 版本,你会发现 atomic 包诚然提供了很多函数,但只有一个 Type,那就是 atomic.Value。这一节咱们回顾一下 atomic.Value 提供的能力。 A Value provides an atomic load and store of a consistently typed value. The zero value for a Value returns nil from Load. Once Store has been called, a Value must not be copied.A Value must not be copied after first use. 回顾一下咱们的此前的原子操作解读,所谓 atomic.Value 就是一个容器,可能被用来“原子地”存储和加载任意的值,并且是开箱即用的。使用场景atomic.Value 的两个经典使用场景: 周期性更新配置,并提供应多个协程 package main import ( "sync/atomic""time") func loadConfig() map[string]string { return make(map[string]string)} ...

August 18, 2022 · 2 min · jiezi

关于angular:Angular-应用要启用-Service-Worker-所需满足的一些前提条件

要应用 Angular 服务工作者的所有性能,请应用最新版本的 Angular 和 Angular CLI。 以后 SAP 电商云 Spartacus UI 应用的 Angular 版本:^13.3.0 要注册 Service Worker,必须通过 HTTPS 而不是 HTTP 拜访应用程序。 浏览器会疏忽通过不平安连贯提供服务的页面上的服务工作者。 起因是 Service Worker 提供了弱小的 HTTP 申请拦挡性能,因而须要分外小心以确保 Service Worker 脚本没有被篡改。 这条规定有一个例外:为了使本地开发更间接,浏览器在拜访 localhost 上的应用程序时不须要平安连贯。 要从 Angular Service Worker 中受害,Angular 应用程序必须在通常反对 Service Worker 的 Web 浏览器中运行。 目前,最新版本的 Chrome、Firefox、Edge、Safari、Opera、UC 浏览器(Android 版)和三星互联网都反对 Service Worker。 IE 和 Opera Mini 等浏览器不反对 service worker。 如果用户应用不反对 Service Worker 的浏览器拜访 Angular 应用程序,则 Service Worker 没有注册,并且不会产生离线缓存治理和推送告诉等相干行为。 进一步来说: ...

August 4, 2022 · 1 min · jiezi

关于angular:组件关系问题

新增和列表组件关系之前的我的项目的设计中,列表组件和新增组件是兄弟组件。 const routes: Routes = [ { path: '', component: IndexComponent }, { path: 'add', component: VehicleAddComponent, }, { path: 'edit/:id', component: VehicleEditComponent, },];开始时,位于列表组件index。点击新增,跳到add路由,此时加载add组件,并且index组件销毁。新增实现后,须要回跳到index组件。此时须要从新加载index组件,显示第一页的内容。这样就会导致一个问题,假如原来处在第二页进行编辑或新增,当操作实现之后,并没有回跳到原来的页面。而是加载第一页的内容。 基与这个问题,咱们能够尝试把 新增 和 编辑 组件变成 列表 的子组件 同时,咱们须要解决 新增或编辑的内容,不能实时的反馈到父组件中 这个问题。 indexComponet: @Component({ selector: 'app-project', templateUrl: './index.component.html', styleUrls: ['./index.component.css']})export class IndexComponent implements OnInit { component = 'index' as 'index' | 'add';}index.html: <app-add *ngIf="component === 'add'" (isFinish)="onAddFinish($event)"></app-add><div class="mt-3" *ngIf="component === 'index'"><button thyButton="success" type="button" (click)="component = 'add'">New Project</button> // index组件内容</div>次要思路就是定义componet变量,当值为'index'时,显示列表内容, 当值为 'add'时,显示列表组件的内容。 ...

August 1, 2022 · 2 min · jiezi

关于angular:Angular-Ngrx-Store-工具库里-Action-定义指南

Store 文件夹数据结构的一个典型例子: 当您第一次应用 ngrx/store 模块时,必须决定运行应用程序所需的操作。 首先剖析从服务器端加载 Heroes 数据的过程并决定应该进行哪些 Action 建模。 从服务器端加载所有英雄数据,因而须要一个加载英雄的动作。一个 Effect 启动并从服务器端检索英雄数据。 Effect 还须要告诉 Store 咱们曾经胜利地检索到了 Heroes 数据,因而它须要调度一个动作(Load Heroes Success Action)。如果与服务器端的通信失败或出于任何其余起因,Effect 会调度另一个动作(加载英雄失败)进行错误处理。Ngrx Store 提供的 Action 接口: export interface Action { type: string;}咱们创立一些字符串常量,作为 Action 的标识: export const LOAD_HEROES = "[Heroes] Load Heroes";export const LOAD_HEROES_FAIL = "[Heroes] Load Heroes Fail";export const LOAD_HEROES_SUCCESS = "[Heroes] Load Heroes Success";其中[] 中括号,代表 Action 的类型-category. 而后咱们就有了很多 Action Class: export class LoadHeroes implements Action { readonly type = LOAD_HEROES;}export class LoadHeroesFail implements Action { readonly type = LOAD_HEROES_FAIL; // can pass any error from server-side constructor(public payload: any) {}}export class LoadHeroesSuccess implements Action { readonly type = LOAD_HEROES_SUCCESS; constructor(public payload: fromCore.Hero[]){}}每个动作类都定义了一个类型属性,因为它实现了 Action 接口。 此外,在某些状况下,须要在自定义 Action 类上定义一个可选的无效负载属性,以便调度此操作的代码能够传入一些额定的数据,这些数据稍后会被 Reducer 用来组成新的状态。 ...

July 23, 2022 · 1 min · jiezi

关于angular:Angular-Inject-注解的实际应用例子和工作原理浅析

看一个理论的例子: import { Component, Inject } from '@angular/core';import { Http } from '@angular/http';@Component({ selector: 'example-component', template: '<div>I am a component</div>'})class ExampleComponent { constructor(@Inject(Http) private http) { // use `this.http` which is the Http provider }}此时,@Inject 是指定此查找标记的手动形式,后跟小写的 http 参数通知 Angular 调配它的对象。 当组件或服务须要大量依赖项时,这可能(并且将会)变得十分凌乱。 因为 Angular 反对从收回的元数据中解析依赖关系,因而大多数时候不须要应用 @Inject。 咱们惟一须要应用 @Inject 的中央是像 OpaqueToken 这样的场景——它创立一个惟一的空白令牌,用作依赖注入提供程序。 咱们应用@Inject 的起因是因为咱们不能应用 OpaqueToken 作为参数的类型,例如上面的代码将不会工作: const myToken = new OpaqueToken('myValue');@Component(...)class ExampleComponent { constructor(private token: myToken) {}}在这里,myToken 不是类型,它是一个值——这意味着 TypeScript 无奈编译它。 然而,当咱们将 @Inject 与 OpaqueToken 一起引入时,整个依赖注入会从新开始工作: ...

July 19, 2022 · 1 min · jiezi

关于angular:Angular-Injectable-注解的工作原理浅析

上面是 SAP 电商云 Spartacus UI 两个 Angular Service 类,都加上了 @Injectable 的注解,区别就在于是否具备输出参数 providedIn: @Injectable() 装璜器指定 Angular 能够在 DI 零碎中应用这个类。 这个注解的输出元数据,providedIn: 'root',意味着被注解的 Angular service 类,在整个应用程序中都是可见的。 当将服务(提供者)注入到咱们的组件/服务中时,通过构造函数中的类型定义来指定咱们须要的提供者。上面是一个例子: import { Component } from '@angular/core';import { Http } from '@angular/http';@Component({ selector: 'example-component', template: '<div>I am a component</div>'})class ExampleComponent { constructor(private http: Http) { // use `this.http` which is the Http provider }}这里的类型定义是 Http(留神大写的 H),Angular 会主动将其调配给 http。 对于 JavaScript 开发人员来说,下面的工作形式或者有些神奇。类型定义是特定于 TypeScript 的,所以咱们编译的 JavaScript 代码实践上应该不晓得在浏览器中运行它时咱们的 http 参数是什么。 ...

July 19, 2022 · 1 min · jiezi

关于angular:Angular-InjectionToken-APPINITIALIZER-的实现方法介绍

APP_INITIALIZER 是 InjectionToken的一个实例。它是 Angular 提供的内建注入令牌。 Angular会在利用加载时执行这个令牌提供的函数。如果函数返回promise,那么angular会始终期待,直到promise被解析。这将使它成为在应用程序初始化之前执行一些初始化逻辑的现实地位。 Angular 注入器应用 DI 令牌来定位 Angular providers 中的依赖项。咱们应用令牌在提供者中注册依赖项: providers :[{ provide: token, useClass: SomeService }]下面代码里的 token,能够是一个 type,一个字符串,或者是 InjectionToken 的一个实例。 type 的例子:providers :[{ provide: productService, useClass: productService}]字符串的例子:providers :[ {provide:'MESSAGE', useValue: 'Hello Angular'}]当所应用的类型没有运行时示意时,例如注入接口、可调用类型(callable type) 等,就会应用 InjectionToken - TypeScript 代码里的 interface,被编译成 JavaScript 之后,后者从编程语言层面不存在 interface 这种 representation. 此时能够应用 InjectionToken. export const HELLO_MESSAGE = new InjectionToken<string>('Hello Angular'); providers :[ { provide: HELLO_MESSAGE, useValue: 'Hello World!' }];如前所述,APP_INITIALIZER在应用程序初始化时运行。Angular会暂停利用的初始化,直到APP_INITIALIZER提供的所有函数运行结束。如果其中任何一个初始化器返回一个promise,那么angular就会期待它的解析,而后再持续进行App的初始化。 这使咱们有机会连贯到初始化过程并运行一些应用程序自定义逻辑。能够加载运行时配置信息。从后盾加载重要数据等。 看一个例子。新建文件 app-init.service.ts: ...

July 19, 2022 · 2 min · jiezi

关于angular:记录遇到的一个循环依赖问题

最近想应用一个service的时候报了循环依赖的问题。 官网领导给的调用办法很简略,在构造函数中增加notifyService,而后间接调用即可。 export class ThyNotifyBasicExampleComponent implements OnInit { constructor(private notifyService: ThyNotifyService) {} ngOnInit() {} showDefault() { this.notifyService.show({ title: '增加我的项目胜利!' }); }} 然而我在应用的时候却报了notifyService的循环依赖。NG0200: Circular dependency in DI detected for ThyNotifyService如图 尝试解决:上谷歌搜寻后,最开始尝试了这种办法: 构造函数里结构的是Injector,接着在函数给定义的属性赋值。 private payrollService:PayrollService;constructor(injector:Injector) { setTimeout(() => this.payrollService = injector.get(PayrollService));} 在 Angular 中 Injector (注入器) 用来治理服务对象的创立和获取,Injector 抽象类中定义了一个 get() 形象办法,该办法用于依据给定的 Token 从注入器中获取相应的对象,每个Injector 抽象类的子类都必须实现该办法。 尽管不晓得为什么他说这样做能够解决循环依赖问题,然而通过我尝试之后,很显著没有用。 Provide:之后想起了之前我的项目中,解决formControl组件循环依赖个别通过providers。 providers: [ { provide: NG_VALUE_ACCESSOR, multi: true, useExisting: forwardRef(() => { return VehicleColourSelectComponent; }) } ]发现自己忽然对provide注入忘得差不多了。于是大略讲一下 ...

July 11, 2022 · 1 min · jiezi

关于angular:关于-Angular-应用-Components-和-Directives-的实例化问题

同 Angular Module 不同,Angular Components 和 Directives 要实例化屡次,每个呈现在 HTML template 中的 markup 都会对应一次实例化。 此外,这些项的作用域也限定在它们被导入的 NgModule中,以避免两个组件应用雷同的选择器时产生命名抵触。因为依赖注入(DI)行为的这种差别,须要辨别一个蕴含组件和指令的 NgModule 和一个蕴含组件、指令和 providers 的 ModuleWithProviders 是很有帮忙的,这正是forRoot()办法进行辨别的中央。 上面是 SAP Spartacus 对于 forRoot 办法的一个例子: export class SiteContextModule { static forRoot(): ModuleWithProviders<SiteContextModule> { return { ngModule: SiteContextModule, providers: [ provideDefaultConfigFactory(defaultSiteContextConfigFactory), contextServiceMapProvider, ...contextServiceProviders, ...siteContextParamsProviders, provideConfigValidator(baseSiteConfigValidator), { provide: CONFIG_INITIALIZER, useFactory: initSiteContextConfig, deps: [SiteContextConfigInitializer, SiteContextConfig], multi: true, }, ...contextInitializerProviders, ], }; }}然而,依赖注入并不总是这么简略。有些时候,应用程序的所有ngmodule在疏导过程中都不可用。提早加载就是这样一个例子。当在路由过程中惰性加载NgModule时,在惰性加载的NgModule中注册的提供商及其子模块在疏导过程中不可用,Angular 那时无奈注册它们。因而,它们只有在加载路由时才会被增加为提供商,而且它们的作用域会从惰性加载的NgModule及其子模块开始注入。如果有多个惰性加载 NgModule 试图注册雷同的提供商,那么 NgModule 树中的每个节点最终都会领有不同的实例。通过在根目录下导入提供商,它有助于确保所有惰性加载的 ngmodule 都能取得该提供商的同一个实例,这也是forRoot() 被命名为该实例的起因。 ...

July 10, 2022 · 1 min · jiezi

关于angular:关于-Angular-应用-Module-的-forRoot-方法的讨论

在 Angular 开发中,咱们常常遇到一个 NgModule 在导入时须要调用它的动态 forRoot 办法。,最值得注意的例子是 RouterModule. 当在 Angular 利用的根目录注册这个模块时,导入RouterModule的形式如下: import { RouterModule, Routes } from '@angular/router';const routes: Routes = [ { path: '', redirectTo: '/index', pathMatch: 'full' }];@NgModule({ imports: [ RouterModule.forRoot(routes) ], ...})export class AppModule { }这个约定在 ngx-bootstrap 中也应用过,以前在 Angular Material 中也应用过。这种约定意味着,给定的模块必须在调用 forRoot()办法时,注册到应用程序的根 NgModule 中。这个办法有什么特别之处,以至于它须要在应用程序的根目录调用,而不是在其余NgModule中调用? 对于初学者来说,forRoot() 约定返回什么?通常,这个办法的返回类型是一个合乎ModuleWithProviders 接口的对象。这个接口是一个可承受的NgModule导入接口,它有两个属性: interface ModuleWithProviders { ngModule: Type<any> providers: Provider[]}forRoot() 办法会返回一个 NgModule 及其依赖的提供商。这和根 NgModule 有什么关系?兴许什么都没有。事实上,只管这种约定意味着它必须在利用的根模块处导入,但在很多状况下,你能够在非根模块中导入它,它也能工作。 上面是 ModalModule 在ngx-bootstrap中应用forRoot()约定的形式: import { NgModule, ModuleWithProviders } from '@angular/core';import { ModalBackdropComponent } from './modal-backdrop.component';import { ModalDirective } from './modal.component';import { PositioningService } from '../positioning';import { ComponentLoaderFactory } from '../component-loader';@NgModule({ declarations: [ModalBackdropComponent, ModalDirective], exports: [ModalBackdropComponent, ModalDirective], entryComponents: [ModalBackdropComponent]})export class ModalModule { public static forRoot(): ModuleWithProviders { return {ngModule: ModalModule, providers: [ComponentLoaderFactory, PositioningService]}; }}只管从实践上讲,导入forRoot()办法的额定提供程序。在子ngmodule中是可行的,但将它注册到应用程序的根目录,在很多方面都有帮忙。 ...

July 10, 2022 · 1 min · jiezi

关于angular:angular项目中使用introjs

angular8的时候,须要装置4.3.0版本,而且须要装置@types/intro.js; angular9则不须要装置特定版本,间接5.1.0就行,且不须要装置解释器。 还要在angular.json文件中进行配置,引入对应的css文件 如果不采纳本地的angular.json文件的话,则须要本人新建一个款式文件,外面放用到的款式,记得用款式穿透属性,能力全局失效。 在打包的的时候可能会报cannot call a namespace ('intro.js')的谬误,解决办法为:批改引入形式为: import * as introJs_ from 'intro.js'introJs = introJs_.default();如果在angular8的版本里,须要批改tsconfig.json文件退出如下两行代码 "allowSyntheticDefaultImports":true, "esModuleInterop":true如果是angular9的环境,则不须要上述步骤 失常跑起来的话,依照官网的文档进行开发就行啦,须要留神的是,如果想要在步骤框中引入事件,必须这样 import * as _introJs from "intro.js";introJs = _introJs.default();this.introJs.setOptions({ ..., setps:[ { title:"测试", element:element, intro:`<a onclick="test('123')"></a>` } ]}).start();function test(params){ console.log(params)}window['test']=test;须要挂载到全局事件上才行;

July 6, 2022 · 1 min · jiezi

关于angular:angular-ChangeDetectorRef

随着子组件的增多和嵌套,遇到数据扭转,然而组件页面没有渲染的问题开始变多。 例如:此页面为子组件,关上该页面后,本来应该显示已存在的附件,然而当初却显示空。正确显示: 问题显示:没有渲染 此问题的呈现往往是因为变更检测器没有检测到组件的数据变更,没有进行变更检测。 到底为什么没有检测到,探索了一段时间也没有探究出产生问题根本原因是什么。 但往往能够过手动变更检测来解决,如下述代码的第9行,调用ChangeDetectorRef的detectChanges函数。明天就来讲一讲对于ChangeDetectorRef的相干常识。 1 constructor(private attachmentService: AttachmentService,2 private ref: ChangeDetectorRef) {3 }4 getAttachmentByIds() {5 this.attachmentService.getAttachmentByIds(this.attachmentIds)6 .subscribe(attachments => {7 this.attachments = attachments;8 // 进行强制变更检测 避免页面不显示attachments的变更9 this.ref.detectChanges();10 })11 }变更检测先来说说angular变更检测的两种策略。 DefaultOnPushDefault策略Angular的组件能够依赖其余的组件来构建应用程序的页面逻辑,最初造成一棵组件树。每个组件都有本人的变更检测器(change detector)。因而,变更检测器的构造也是一棵同构的树 当某个组件的状态产生扭转时,Angular会从这棵树的根节点开始遍历,登程所有组件节点的变更检测器,这样Angular就晓得那些组件的状态产生了扭转,须要更新相应的UI。 这个过程看似开销很大,但Angular曾经进行了大量优化,理论变更检测的速度很快。 这种策略在咱们利用组件过多时会对咱们的利用产生性能的影响, 不过在不相熟相干细节的状况下,Default策略是咱们最好的抉择。 OnPush策略OnPush策略我目前没有尝试用过,但能够作为理解。 Angular 还提供了一种 OnPush 策略,咱们可以批改组件装璜器的 changeDetection 属性来更改变化检测的策略。 如下述代码的第4行。 1 @Component({2 selector: 'app-A',3 // 设置变化检测的策略4 changeDetection: ChangeDetectionStrategy.OnPush,5 template: ...6 })7 export class AComponent {8 ...9 }在OnPush策略下,只有这几种状况能够触发以后组件的变更检测: 组件的输出属性(绑定)的援用被扭转组件外部触发了异步事件手动触发变更检测以后组件或子组件之一触发了事件, 如click简略谈谈第一点,这是angular中比拟经典的解决办法。其余的都比拟直观。 例如父组件向子组件应用@Input传入一个对象 @Component({template: `<child [people]="people"></child>`})export class AppComponent {people = {name: '张三'};onClick1() {this.people.name = '李四';}onClick2() {this.people = { name: '李四'};}}父组件调用onClick1函数并不会触发变更检测,因为这仅仅是扭转了对象的属性,并没有扭转对象的援用。 ...

June 27, 2022 · 1 min · jiezi

关于angular:Angular-项目中导入-styles-文件到-Component-中的一些技巧

家喻户晓,咱们应用 Angular CLI 创立 Component 之后,每个 Component 都会有本人的专属 styles 文件。通常状况下,也存在另一种可能性,即可能须要在组件中蕴含全局(global)款式文件(尤其是变量文件,即 variable files)。假如在 src/stylings 文件夹下有一个文件:_variables.scss: 其内容如下:// your _variables.scss file$brand-color: #800000;复制代码咱们的 Component 模板文件:<!-- hello.component.html --><h1> Hello World!</h1>复制代码Component 的 style 文件:// hello.component.scss@import "../../../stylings/variables"; // this is not cool! h1 { color: $brand-color;}复制代码下面代码里呈现的层级构造操作符,../, 意思是当前目录的父目录,这种写法可读性不好,且容易出错。如果您的我的项目是应用 Angular CLI 生成的,您能够在 .angular.cli.json 文件中增加配置 stylePreprocessorOptions > includePaths。 此配置容许开发人员增加将查看导入的额定根本门路。 它通知 Angular CLI 在解决每个组件款式文件之前,在上述门路中查找款式文件。例如,在咱们的例子中,让咱们在门路中增加 ./stylings。 因为配置承受数组类型,因而咱们能够增加多个门路。{ ..."apps": [{ "root": "src", ... "stylePreprocessorOptions": { "includePaths": [ "./stylings" ] } }]}复制代码留神,在高版本的 Angular 我的项目里,上述配置位于文件 angular.json 内: ...

May 28, 2022 · 1 min · jiezi

关于angular:Angular动态组件的表单校验功能

零、需要剖析前置条件:本文基于Angular动静组件https://angular.cn/guide/dyna... 这是一个有着三层组件的动静表单,因为表单项是动静生成的,与父组件不共用FromControl,因而对于必填项的拦挡无奈反馈到最外层组件的submit上。 为了不便交换,咱们把最外层的模态框页面称为第一层把Angular动静组件加载器称为第二层把表单项子组件称为第三层。 一、第一层和第二层之间传值这里用到的是一般的组件间传值。先创立一个变量: // 第一层:TS// 以后组件按钮是否能够点击 submitButtonActive: boolean;在第一层的提交按钮上加上[disabled]="!submitButtonActive", // 第一层:HTML// Submit按钮<button [disabled]="!submitButtonActive" class="btn btn-primary" (click)="onSubmit()" type="button">确认</button>这样button就指向了一个变量,以便前面在第二层数据变动时,扭转第一层的变量从而管制按钮是否激活。 而后在第二层组件建设一个弹射器 // 第二层:TS@Output() isFormGroupValid = new EventEmitter<boolean>();因为第二层组件有一堆子组件,这些子组件的输出是否非法由第二层决定,第二层只负责向第一层弹射一个计算后的最终值即可。 当第二层弹射新的值时,将会更新第一层提交按钮的激活状况。 二、第三层向第二层传值接下来就是思考第二层和第三层传递数据的问题了。 这段内容基于Angular动静组件。先回顾一下动静组件是怎么生成的:首先须要一个接口,接口规定了第二层组件加载器和第三层组件间能够传递什么值;而后动静组件加载器就能够生成组件并向接口规定的变量中传值了。基于此形式,如果向让子组件向父组件传值,咱们还须要两个变量:一个输出值index,用来标记以后子组件在父组件中是第几个被加载进去的,即地位索引。一个输入值isValid,用来通知父组件,以后子组件的信息是否非法。 当输入值isValid变动时,将变动值弹射给父组件(也就是第二层组件加载器),第二层校验所有组件的表单值是否全副非法,校验实现后向第一层弹射。 批改动静组件实现的接口,减少两个变量, // 接口// 组件索引,类型:number@Input() index: number; // 以后组件的表单值是否非法,类型:弹射器@Output() isValid: EventEmitter<{index: number, isValid: boolean}>;并且所有成员实现这两个变量: // 第三层:TS@Input() index: number; @Output() isValid = new EventEmitter<{index: number, isValid: boolean}>();在第二层减少赋值和订阅: // 第二层:TS 加载动静组件时componentRef.instance.index = i; componentRef.instance.isValid.subscribe(({index, isValid}) => { console.log(index); console.log(isValid); })在第三层组件上,减少对formControl的订阅,输入valid属性: // 第三层:TS , ngOninit办法中this.formControl.valueChanges.subscribe(value => { this.formItemValue.value = value; // 打印表单值是否非法 console.log(this.formControl.valid); })此时,一旦把输入框的内容全副删掉,就会打印false属性:、 ...

May 27, 2022 · 1 min · jiezi

关于angular:angular中使用ngzorro组件库实现表格拖拽控制每列大小功能

首先间接看成品成果 上面开始解说:进入ngzorro组件库官网网页 https://ng.ant.design点击试验性功能,找到调整尺寸 滚动到列表对应的中央,复制其相干的代码 留神要在对应的模块和款式文件中引入对应的模块和款式,不然就会出问题 复制完后什么都不动的话,页面是这样的 能够看到表格上有两个竖线,很不美观,咱们先去掉其中一个竖线批改对应的款式能够看到页面体现失常了一点。 而后想要拖动的时候扭转大小,而不是拖动完结当前扭转大小,单纯的批改触发形式发现并不行nzResizeEnd改为nzResize并不能实现成果,而且拖动完结当前也不能扭转。批改代码 onResize({ width }: NzResizeEvent, col: string): void { // this.cols = this.cols.map(e => (e.title === col ? { ...e, width: `${width}px` } : e)); this.cols.map(e=>{ if(e.title===col){ e.width=`${width}px` } }) }这时候表格能够拖拽了 如果不想要点击当前呈现那一条竖线,把html中的nzPreview删掉就行 当初来实现文字溢出显示省略号性能 能够看到,只有设置包裹文字的容器的宽度随着顶部的宽度随动就行 外围代码如下 //html <tbody> <tr *ngFor="let data of basicTable.data"> <td>{{ data.name }}</td> <td>{{ data.age }}</td> <td > <div style=" white-space: nowrap; overflow: hidden; text-overflow: ellipsis; " [ngStyle]="{'width':data.width}"> {{ data.address }} </div> </td> <td>-</td> </tr> </tbody>//ts listOfData:any = [ { key: '1', name: 'John Brown', age: 32, address: 'New York No. 1 Lake Park' }, { key: '2', name: 'Jim Green', age: 42, address: 'London No. 1 Lake Park' }, { key: '3', name: 'Joe Black', age: 32, address: 'Sidney No. 1 Lake Park' } ]; onResize({ width }: NzResizeEvent, col: string): void { // this.cols = this.cols.map(e => (e.title === col ? { ...e, width: `${width}px` } : e)); this.cols.map(e=>{ if(e.title===col){ e.width=`${width}px`; this.listOfData.map((item:any)=>{ item.width = `${width}px`; }) } }) }这样一个简略的可调整宽度的列表就实现啦。 ...

May 26, 2022 · 1 min · jiezi

关于angular:angular-多选表单数据绑定

前言对于一个多选类型,如何获取所有已抉择的数组。 尝试获取formControl的value。 this.formControl.valueChanges.subscribe(value => { console.log(value);})对于绑定多选类型的formControl的value,只会有true或者false。如果你选中就是true,如果不选中就是false。然而我想要的是所有选中对象数组。谷歌搜寻得悉,能够绑定点击事件,再去减少或删除数组中的对象。 <div *ngFor="let option of formItem.formItemOptions; index as i" class="form-check"> <input [formControl]="formControl" [value]="option.id" (click)="selectCheckBox(formControl.value, option)" class="form-check-input" type="checkbox" > <label class="form-check-label mr-1" for="{{id}}_{{option.id}}"> <span>{{option.content}}</span> </label> </div>selectCheckBox(isCheck: boolean, option: FormItemOption): void { if (isCheck) { this.formItemValue.formItemOptions.push(option); } else { const index = this.formItemValue.formItemOptions.indexOf(option); this.formItemValue.formItemOptions.splice(index, 1); }}然而获取传入的formControl.value变量为null,猜想可能先出发点击工夫,后进行表单数据绑定。改写办法 selectCheckBox(isCheck: boolean, option: FormItemOption): void { // 如果index值为-2,示意数组为定义,发明一个数组 // 如果index值为-1,示意所选选项并未处于数组内,增加之 // 如果index值大于等于0,口试所选选项处于数组内,删除之 const index = Array.isArray(this.formItemValue.formItemOptions) ? this.formItemValue.formItemOptions.indexOf(option) : -2; if (index < -1) { this.formItemValue.formItemOptions = [option]; } else if (index < 0) { this.formItemValue.formItemOptions.push(option); } else { this.formItemValue.formItemOptions.splice(index, 1); } }测试 ...

May 23, 2022 · 1 min · jiezi

关于angular:总结一下最近遇到的问题

当咱们应用@ManyToMany注解时运行后盾,数据库中会主动生成关联表,比方咱们有以下两个实体,他们之间的关系是ManyToMany public class Task{ . . . @ApiModelProperty("工作表单") @ManyToMany() private List<FormItem> formItems = new ArrayList<>();}public class FormItem{ . . . @ApiModelProperty("工作表单") @ManyToMany() private List<Task> tasks = new ArrayList<>();}这样的话会生成两个两头表即form_item_tasks和task_form-items,并且默认状况下如果你在task端保护formItems的话数据只会主动存到task_form_items表中,反过来在formItem中保护task也会像下面一样——存到form_item_tasks中。即后面的那一项为“主项”。如果咱们想只生成一个表那么须要进行以下操作: public class Task{ . . . @ApiModelProperty("工作表单") @ManyToMany() @JoinTable(name = Task.TABLE_NAME + "_" + FormItem.TABLE_NAME, joinColumns = @JoinColumn(name = FormItem.TABLE_NAME + "_id"), inverseJoinColumns = @JoinColumn(name = Task.TABLE_NAME + "_id") ) private List<FormItem> formItems = new ArrayList<>();}public class FormItem{ . . . @ApiModelProperty("工作表单") @ManyToMany() @JoinTable(name = Task.TABLE_NAME + "_" + FormItem.TABLE_NAME, joinColumns = @JoinColumn(name = Task.TABLE_NAME + "_id"), inverseJoinColumns = @JoinColumn(name = FormItem.TABLE_NAME + "_id") ) private List<Task> tasks = new ArrayList<>();}这样的话只会生成你在@JoinTable中name中申明的表,并且无论在哪一端进行保护都能够失常执行。 ...

May 23, 2022 · 1 min · jiezi

关于angular:Angular-项目里使用-scss-文件的一些技巧

应用 Angular CLI 新建一个 Angular 我的项目: ng new my-sassy-app --style=scss创立如下的 scss 文件: styles.scss 是咱们应用的次要 scss 文件,外面导入了以下划线结尾的 _variables.scss 和 _mixins.scss: // src/sass/styles.scss@import './variables';@import './mixins';最初在 angular.json 里指定这个 styles.scss 文件即可: "styles": [ "sass/styles.scss"],当初咱们有了新的 _variables.scss 和 _mixins.scss 文件,咱们心愿在咱们其余的 Angular Component 中应用它们。在其余我的项目中,您可能习惯于在任意地位拜访这些 scss 文件里定义的 Sass 变量。 在 Angular CLI 中,所有组件都是自蕴含的,它们的 Sass 文件也是如此。 为了在组件的 Sass 文件中应用 _variables.scss 中定义的变量,您须要导入 _variables.scss 文件。 一种办法是应用组件的相对路径 @import,比方 ../../ 这种写法。如果您有许多嵌套文件夹或最终须要挪动这些 scss 文件,这或者不是一个好方法——可读性差,并且容易出错。 一个好方法是应用 alias 别名语法,用特殊符号波浪号代表:~. 在英语里 ~ 的单词是 tilde. // src/app/app.component.scss@import '~sass/variables';// now we can use those variables!波浪号 (~) 将通知 Sass 查看 src/ 文件夹,它是导入 Sass 文件的快捷方式(short cut). ...

May 12, 2022 · 1 min · jiezi

关于angular:Angular-项目中导入-styles-文件到-Component-中的一些技巧

家喻户晓,咱们应用 Angular CLI 创立 Component 之后,每个 Component 都会有本人的专属 styles 文件。通常状况下,也存在另一种可能性,即可能须要在组件中蕴含全局(global)款式文件(尤其是变量文件,即 variable files)。 假如在 src/stylings 文件夹下有一个文件:_variables.scss: 其内容如下: // your _variables.scss file$brand-color: #800000;咱们的 Component 模板文件: <!-- hello.component.html --><h1> Hello World!</h1>Component 的 style 文件: // hello.component.scss@import "../../../stylings/variables"; // this is not cool!h1 { color: $brand-color;}下面代码里呈现的层级构造操作符,../, 意思是当前目录的父目录,这种写法可读性不好,且容易出错。 如果您的我的项目是应用 Angular CLI 生成的,您能够在 .angular.cli.json 文件中增加配置 stylePreprocessorOptions > includePaths。 此配置容许开发人员增加将查看导入的额定根本门路。 它通知 Angular CLI 在解决每个组件款式文件之前,在上述门路中查找款式文件。 例如,在咱们的例子中,让咱们在门路中增加 ./stylings。 因为配置承受数组类型,因而咱们能够增加多个门路。 { ... "apps": [{ "root": "src", ... "stylePreprocessorOptions": { "includePaths": [ "./stylings" ] } }]}留神,在高版本的 Angular 我的项目里,上述配置位于文件 angular.json 内: ...

May 12, 2022 · 1 min · jiezi

关于angular:改用户功能为动态

前言做一个issue,要求是这样的他说的@是相似于这样的当输出@时,会弹出能够@的用户列表。 解决一开始想到从html层面解决,看看input标签有没有能够限度输出内容。 <input nkeyup="value=value.replace(/[^0-9]/g,'')" npaste="value=value.replace(/[^0-9]/g,'')" oncontextmenu = "value=value.replace(/[^0-9]/g,'')">查到能够增加几个事件,上述代码为限度input输出内容为数字。onkeyup事件每当输出就会执行,咱们设置一个办法,替换非数字为空。外面检测非数字用到了正则表达式。 onpaste事件每当粘贴时执行,咱们设置同样的办法。 oncontextmenu事件很少用到,感兴趣的能够理解理解。 批改为替换规定为当检测到 '@'时替换为空。测试输出并没有弹出能够@的用户列表。这种禁止输出并不是输出@没有反馈,而是输出@会呈现0.5秒而后再隐没。接下来就是把这种事件变为动静,当为合作伙伴时,启用事件,当部位合作伙伴时,不启用事件。 过后这么写的,而后没有失效,没找到问题。而后认真一想,发现如果写进去也不太对,如果输出一个邮箱地址的话,咱们这样就没法输出@符号了,咱们只想他不弹出用户列表,最初导致无奈输出@符号了。 老师也倡议这么写不太好,这相当于退出一个监听,当监听到输出@符号时,将@符号用空替换。而弹出用户列表相当于另一个监听,当监听到@符号时去弹出用户列表。两个监听事件可能并没有影响,所以不起作用。 如果不想弹出用户列表,咱们把用户列表暗藏掉就好了。找到相干html代码,而后加上*ngIf = "false",后果并没有暗藏掉,一度狐疑本人找错相干html代码了。 问了老师后,发现这是一个angular的ng-template模版标签,ng-template并不会被页面渲染,而是会被替换成咱们定义的要显示的理论内容。所以ngif并没有失效。 扭转思路,咱们将@的用户列表设置为空,这样其实当输出@时也会触发弹出用户列表事件,然而用户列表为空,所以看起来是不显示的。 能@一个人,不仅仅当输出@时能弹出用户列表,还要在提交输出内容时检测到含有'@XXX(能@的人名)'时要揭示被@的人。这样设置也能够去除当提交时@性能的执行。

May 9, 2022 · 1 min · jiezi

关于angular:ActivatedRoute-和-Router以及记录后台遇到的问题

ActivatedRoute 和 Router的区别前台angular应用这两个来进行路由的操作,然而如同始终不大清楚区别。这里简略记录一下。 区别ActivatedRoute是以后组件的路由对象,蕴含了以后的路由信息。router是全局路由器对象。能够在各个路由之间跳转所以最大的区别我认为就是作用域不同,一个是在获取以后路由的信息,另一个则是对全局路由操作跳转。 ActivatedRoute:将ActivatedRoute在ngOnit中打印之后,能够看到有如下属性。component, date , fragment,params,queryParams,snapshot,url,_futurnSnapshot_routerState等。 Angular 文档将 ActivatedRoute 定义为。提供给每个路由组件的服务,其中蕴含路由特定信息,例如路由参数、静态数据、解析数据、全局查问参数和全局片段。每个都Route将一个 URL 映射path到一个组件。 这里讲一下常看到或者用到的几个属性: component咱们能够看到对应的是IndexComponet, 也就是以后路由对象对应着的是IndexCompoent. snapshot在Snapshot 中,咱们在组件和路由中的值是不同步的。如果应用 snapshot 并且在路由定义中有一个参数,例如 product/:id,那么如果页面更改了id,那么将不会取得任何新的 id。快照意味着它是在 ngOnInit 运行时,这是它在那个工夫点的状态。 咱们能够通过如下的形式获取snapshot快照, 并能够获取它的params路由参数。 constructor(private route: ActivatedRoute){} ngOnInit() { const id = this.route.snapshot.params.id;}这里params和queryParams用的比拟多,所以重点讲一下。 params 路由参数params生成的URL采纳matrix法(;key=value;kye1=value1) Angular 路由器应用它来确定路由。它们是路由定义的一部分。咱们须要 product/:id 这种模式来跳转到正确的路由。 咱们常应用这样的模式,当如下定义路由时,id就是params参数 { path: 'product/:id', component: ProductDetailComponent }获取id就是params参数: constructor(private route:ActivatedRoute){} this.route.params.subscribe(param => { const id = +param.id; });queryParams 查问参数queryParams生成的URL采纳传统表示法(?key=value&key1=value1) queryParams查问参数是可选的,通常会以 /product?page=10 的模式传递。 传递查问参数:第一种是通过[queryParams]指令增加。 <a [routerLink]="['product']" [queryParams]="{ page:10 }">Page 10</a>第二种应用navigate形式导航增加。 ...

May 9, 2022 · 2 min · jiezi

关于angular:angular-拖动功能

前言写到表单性能的时候,就思考如果表单可能拖动式地增加,无疑可能增强用户的应用体验感。所以就尝试着实现拖动性能。如下图,可能将一个表单拖动到另一个表单。 环境配置应用到了angular的cdk:@angular/cdk/drag-drop Angualr drag-drop外面的性能能让咱们十分不便的解决页面上视图的拖拽(自在拖拽、列表排序拖拽、列表之间拖拽)问题。 官网:https://material.angular.io/c... cdk是Angular Material 下的一个模块.咱们装置一下Material。 装置materialng add @angular/materialng add命令将装置 Angular Material、 组件开发工具包 (CDK) Module导入import { DragDropModule } from '@angular/cdk/drag-drop';imports: [...DragDropModule]性能实现讲一下最罕用的几种用法 拖拽html代码:最次要的就是加上了 cdkDrag <div cdkDrag> drag me</div>成果: 排序应用cdkDropList,它增加在一组元素增加cdkDrag可拖动元素的汇合里面。随着元素的挪动,我的项目将主动重新排列。 html: <div class="list-group" cdkDropList (cdkDropListDropped)="drop($event)"> <div class="list-group-item row" *ngFor="let customer of customers" cdkDrag> {{customer.name}} </div></div>ts: drop(event: CdkDragDrop<string[]>) { moveItemInArray(this.customers, event.previousIndex, event.currentIndex); }效果图: 当然还能够横向排序,只须要增加cdkDropListOrientation="horizontal" <div class="box-list-horizontal" cdkDropList cdkDropListOrientation="horizontal">增加动画看起来下面的效果图有些不太好看,咱们能够给它增加一点动画。 css: // 拖拽时显示的占位符元素,而不是理论的元素.cdk-drag-placeholder { opacity: 0;}// 从动画的地位到最终把它放在列表的地位上时的动画.cdk-drag-animating { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}// 拖动元素时,看到的预览元素.cdk-drag-preview { box-sizing: border-box; border-radius: 4px; box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}// 拖动元素时,其余元素扭转地位看到的动画.list-group.cdk-drop-list-dragging .list-group-item:not(.cdk-drag-placeholder) { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}效果图: ...

April 25, 2022 · 2 min · jiezi

关于angular:运行在-CCV2-环境上的-Angular-服务器端渲染应用的性能瓶颈分析

在 Angular 应用程序中应用服务器端渲染,出于以下几种起因: SSR 有助于搜索引擎优化。 搜索引擎爬虫能够解析通过服务器端渲染的 HTML 页面源代码。而运行在 CSR 模式下的单页面利用,页面源代码是在客户端浏览器里执行简单的 JavaScript 生成,古代很多爬虫对此内容无能为力。Facebook 和 Twitter 等社交媒体平台,能够在共享时显示 SSR 渲染出的网站的预览。在服务器上出现网页后,页面内容能够被缓存,从而可能更快的响应用户雷同的页面申请。要在 Angular 应用程序中实现服务器端渲染,能够应用 Angular Universal package. 当咱们应用 Angular Universal 时,向 OCC 服务器发动业务数据申请的 API 触发两次。首先是在服务器渲染页面时履行一次,第二次是在客户端应用程序启动时触发。这种 API 反复触发的形式会导致提早问题和蹩脚的用户体验,因为产生这种状况时屏幕通常会闪动(flickering). 此外,SAP CCV2 上的 SSR 容器是运行 Node.js 代码的中央,因而更容易受到性能降落的影响。在抉择 Node.js 和 server.js jsapps-* 之后,能够从 Dynatrace 中的技术和过程页面查看 CPU 和内存。留神 SSR 利用文件的名称可能不同,默认为 main.js. 随后的页面将显示所选任何给定指标的均匀利用率。 单击单个 pod 将进入其过程详细信息页面,其中能够查看无关 cpu/内存利用率和重新启动工夫的信息。 对于 SSR,无关 CPU 的详细信息将在零碎性能选项卡下,而无关 V8 内存应用的详细信息将在 Node.js 指标选项卡下。 当发现 SSR Pod 的 CPU 利用率居高不下时,基于 Node.js 的设计,只管咱们能够通过垂直扩大来缓解这个问题, 另一方面,如果并发申请的数量很大,那么也能够思考通过程度扩大的形式来升高每个 Pod 的 CPU 利用率。 ...

April 25, 2022 · 1 min · jiezi

关于angular:angular中ExpressionChangedAfterItHasBeenCheckedError错误

最近在应用ngAfterViewInit的时候产生了一些谬误。又发现自己对变更检测的流程其实不是很了解,所以来梳理一下过程,并来讲讲这个谬误为什么会产生。 变更检测首先来理解一下angular对组件的检测操作: 当Angular 对每个组件进行查看,这些组件大抵按指定程序执行的以下操作: 更新所有子组件/指令的绑定属性 (例如@Input)在所有子组件/指令上调用 ngOnInit、OnChanges 和 ngDoCheck 生命周期hook更新以后组件的 DOM为子组件运行变更检测为所有子组件/指令调用 ngAfterViewInit 生命周期钩子 然而在开发模式下会额定执行以下的操作查看: 在每次操作之后,Angular 都会记住它用来执行操作的变量的值。它们存储在组件视图的 oldValues 属性中。 Angular 执行下列的操作: 查看传递给子组件的值是否与oldValues雷同查看用于更新 DOM 元素的值是否与oldValues雷同对所有子组件执行雷同的查看抛出ExpressionChangedAfterItHasBeenCheckedError谬误的例子举个例子: 假如当初是开发模式 定义了A组件,并传递text给B组件 @Component({ selector: 'a-comp',template: ` <span>{{name}}</span> <b-comp [text]="text"></b-comp> ` })export class AComponent { name = 'Im A' text = 'A to B`;}@Component({ selector: 'b-comp', })export class BComponent { @Input() text; constructor(private parent: AComponent) {} ngOnInit() { this.parent.text = 'B to A'; }}让咱们依照的5步变更检测进行: 更新所有子组件/指令的绑定属性 angular先执行B组件的text与A组件的text绑定,B组件的text = "A to B"。在开发模式下,会记录这个值view.oldValues[0] = '传递给子组件';在子组件上调用 ngOnInit等钩子 此时执行AComponent.text = "B to A";执行到第二步的时候产生异样,抛出ExpressionChangedAfterItHasBeenCheckedError如下图 ...

April 18, 2022 · 1 min · jiezi

关于angular:异步验证器防抖的补充-以及-记录动态组件遇到的问题

补充这里对上文的异步验证器防抖做一点补充。https://segmentfault.com/a/11... vehicleBrandNameNotExist(): AsyncValidatorFn { return (control: AbstractControl): Observable<ValidationErrors | null> => { if (control.value === '') { return of(null); } return timer(this.debounceTime).pipe( // 调用服务, 获取后果 switchMap(() => this.vehicleBrandService.existByName(control.value)), // 对后果进行解决,null示意正确,对象示意谬误 map((exists: boolean) => (exists ? {vehicleBrandNameExist: true} : null)), ) }; }上文的最初提到,发现这种写法能够。通过老师的判断后,发现几种写法的关键在于一个问题: 每次取得新值的时候,fromControl是否会勾销对上一个数据源的订阅? 这种问题从源码中找比拟艰难,于是我搜寻了相干材料: 找到一篇同样介绍这种办法的文章。 如下图,同样用的是time().pipe, 他的解释说, 当有新的control值时,fromControl会勾销对上一个正在期待实现的observable的订阅,因而,旧的observable不会收回值。 我画了一张图来阐明: 看图就能够理解到,fromControl勾销了对旧值的订阅,旧的observable并不会收回值。 简化明确了这个情理之后,防抖就能够简化成这种写法: vehicleTypeNameIsAvailable(VehicleTypeId?: number): AsyncValidatorFn { return (control: AbstractControl): Observable<ValidationErrors | null> => { if (control.value === '') { return of(null); } return of(null).pipe( delay(1000), // 调用服务, 获取后果 switchMap(() => this.vehicleTypeService.nameIsAvailable(control.value, VehicleTypeId)), // 对后果进行解决,null示意正确,对象示意谬误 map((available: boolean) => (available ? null : {vehicleTypeNameNotAvailable: true})), ) }; }间接返回null作为observable, 如果delay(1000),通过了,那就调用service服务。 ...

April 11, 2022 · 2 min · jiezi

关于angular:Angular13引入Echarts图表操作实录

前言因为毕设的软件须要前端绘图,所以果决抉择知名度较高的Apache Echarts。对于原生JS或TS来说,引入第三方库非常简略,只须要在Header处增加即可。本文旨在介绍Angular尤其是最新的Angular13应用Echarts的一些不同之处。 一、装置npm依赖在Angular我的项目的根目录执行: npm install echarts --savenpm install ngx-echarts --save其中--save的作用是将包写入到package.json中,下次一键装置。 二、引入CSS及JS文件Angular.json在Angular.json的scripts中引入"node_modules/echarts/dist/echarts.js"留神:build和test中的scripts都要引入,否则单元测试中款式会生效。 模块imports在模块的imports中引入NgxEchartsModule个别状况下是这样引入的: 但这样会导致空注入器问题: 解决方案很简略,改为如下形式引入: NgxEchartsModule.forRoot({ echarts: () => import('echarts') }) 接下来能够粘贴款式了。 三、粘贴款式拜访Apache Echarts官网能够看到目不暇接的款式,而且每种款式都给了JS和TS示例代码:https://echarts.apache.org/ex... 轻易点进去一个,就能看到它的代码: 接下来钻研的就是代码怎么用的问题。尽管左边的选项卡中给出了残缺的代码,但这种代码也是不能间接往Angular我的项目中粘贴的(因为Angular13加强了语法查看)。 查了一下才发现其实用法非常简单,外围就是options这个变量。 在V层定义图表:<div echarts [options]="options"></div>其中"options"是变量名。 在组件中申明变量不能够这么写,因为新版本的语法查看强制变量赋初始值: public options:{};能够有这几种形式: // 赋初始值为空数组public options:{} = {};// 加!容许变量为空public options!:{};// 加?容许变量为undefinedpublic options?:{};为options赋值写一个办法为options赋值,此时就能够得心应手替换数组的值了: initChart(){ this.options={ xAxis: { type: 'category', data: ['1', '2', '3', '4', '5', '6', '7'] }, yAxis: { type: 'value' }, series: [ { data: [820, 932, 901, 934, 1290, 1330, 1320], type: 'line', smooth: true } ] } }最初一步在适合的机会调用办法即可,例如在Oninit生命周期: ...

April 9, 2022 · 1 min · jiezi

关于angular:HertzBeat入GVP啦并-v10beta7-发布易用友好的云监控系统

HertzBeat赫兹跳动 是一个由Dromara孵化的反对网站,API,PING,端口,数据库,全站,操作系统等监控类型,反对阈值告警,告警告诉(邮箱,webhook,钉钉,企业微信,飞书机器人),领有易用敌对的可视化操作界面的开源监控告警我的项目。 很快乐Hertzbeat被评定为GVP - Gitee最有价值开源我的项目! 老哥们帮忙在Gitee STAR起来,冲!https://gitee.com/dromara/her... 官网:hertzbeat.com | tancloud.cn 而后来说说最新的版本,这个版本看这么多feature,其实简略来说次要是这几个 反对了ORACLE数据库的监控,包含ORACLE的根本信息,表空间,连接数,TPS,QPS等指标 反对了LINUX的CPU利用率,内存利用率,磁盘占用相干指标,使LINUX监控贴合理论业务 还有前端参数反对了KEY-VALUE,当前咱们就能够在页面上配置HTTP Headers等相似参数了,还有就是参数配置那优化改版,把十分用告警参数暗藏起来了,略微难看些,而后反对了windows下bat启动脚本,更多的就是稳定性的晋升和一些其它的小修复小需要啦! 版本个性: feature 反对oracle数据库监控类型-xgf 由 @gf-8 奉献 thanksfeature oracle监控反对tablespace,连接数,qps,tps等指标feature linux监控反对设置超时工夫 (#49)feature 检测网站SSL证书是否过期 (#50) 由 @weihongbin 提出 thanksfeature 页面配置参数反对KEY-VALUE数组(#57)feature API和网站监控反对页面配置Headers和Params (#58)(#59)feature API和网站监控反对页面配置 basic auth, digest auth (#60)feature http 端口追随SSL是否启用变更443或80 (#61)feature 批改默认超时工夫3000毫秒为6000毫秒 (#55)feature:make tdengine optional, not required (#62)feature:support win bat service (#65)feature:support hide advanced params define (#68)feature:enable auto redirect when 301 302 http code (#69)feature:only collect available metrics when detect (#70)feature:[website api]monitor support keyword match (#72)feature:support linux cpu usage,memory usage,disk free (#76)BUG修复 ...

April 8, 2022 · 1 min · jiezi

关于angular:关于动态表单遇到的一些问题

前言一个demo利用到我的项目上的时候总会呈现各种各样的问题。 版本问题在编写我的项目时,照着前几天的试验demo写,viemContainerRef.createComponent<A>(B),B是A接口的实现类,然而在这里B却报错了,一开始没认真看报错详细信息,认为是B对A的继承关系有问题,然而查看了后并没有错。去网上找了一番后也没有发现解决办法。而后我去查看源码,发现办法参数须要一个工厂类而不是一个子类,感到了纳闷,怎么demo里没问题呢。又去demo里看了一下源码。发现参数类型并不相同,发现是angular版本问题,我的项目用的是angular12版本,而demo用的是angular13版本。最终起因是版本问题造成的。去谷歌viemContainerRef.createComponent()的应用实例,网上还是老版本的应用,照着写上 constructor(private resolver: ComponentFactoryResolver) {}loadComponent() { const factory: ComponentFactory<FormItemTypeComponent> = this.resolver.resolveComponentFactory(FormItemTextComponent); const viewContainerRef = this.formItem.viewContainerRef; const componentRef = viewContainerRef.createComponent(factory); componentRef.instance.formItem = formItem;}起初又去了的老版本的angular官网,也发现了相似的写法。 援用问题@ViewChild(FormItemDirective, {static: true})formItem!: FormItemDirective;loadComponent() {for (var i = 0; i < this.task.formItems.length; i++) { const formItem = this.task.formItems[i]; console.log(this.formItem); const factory: ComponentFactory<FormItemTypeComponent> = this.resolver.resolveComponentFactory(FormItemTextComponent); const viewContainerRef = this.formItem.viewContainerRef; const componentRef = viewContainerRef.createComponent(factory); componentRef.instance.formItem = formItem;}遇到一个undefined问题,此时的formItem是应用@ViewChild注解注入进来的,这个也没有什么报错信息,只能猜了。为了避免版本问题,我也查看了12版本的官网代码和源码,并没有什么问题。官网实例里指令跟组件在一个模块里,没有通过@ViewChild注解注入进来,猜想可能是因为没有引入指令,而指令跟组件在不同的模块里,我尝试将两者放入批准模块已排除不是援用的谬误,也没有解决问题。一时没有了思路,在宇轩同学的帮忙下,找到了问题所在。解决办法是将ViewModule引入TaskModule中,ViewModule是指令所在组件的所在模块,TaskModule是大功能模块的总模块。 ├── add├── directive├── edit├── form-item-type├── index├── item-add├── item-edit├── item-view├── task-routing.module.ts├── task.module.ts└── view然而为什么改这个就能够了,我俩都没有想明确。 ...

March 28, 2022 · 1 min · jiezi

关于angular:angular异步验证器防抖

背景:以后输入框的formControl设置了异步验证器,会依据以后的值进行申请后盾,判断数据库中是否存在。 原版异步验证器: vehicleBrandNameNotExist(): AsyncValidatorFn { return (control: AbstractControl): Observable<ValidationErrors | null> => { if (control.value === '') { return of(null); } return this.vehicleBrandService.existByName(control.value).pipe(map(exists => exists ? {vehicleBrandNameExist: true} : null)); }; }然而测试下来发现,该异步验证器触发的太频繁了。输入框每输出一个字母都会对后盾进行申请,不利于节俭资源。 防抖节流这个相干的操作叫做防抖和节流。什么是防抖和节流?有什么区别? 实质上是一种优化高频率执行代码的一种伎俩。 比方浏览器的鼠标点击,键盘输入等事件触发时,会高频率地调用绑定在事件上的回调函数,肯定水平上影响着资源的利用。 为了优化,咱们须要 防抖(debounce) 和 节流(throttle) 的形式来缩小调用频率。 定义:防抖: n 秒后在执行该事件,若在 n 秒内被反复触发,则从新计时 节流: n 秒内只运行一次,若在 n 秒内反复触发,只有一次失效 举个例子来阐明: 乘坐地铁,过闸机时,每个人进入后3秒后门敞开,期待下一个人进入。 闸机开之后,期待3秒,如果中又有人通过,3秒期待从新计时,直到3秒后没人通过后敞开,这是防抖。 闸机开之后,每3秒后准时敞开一次,间隔时间执行,这是节流 代码实现:防抖操作恰好合乎咱们的需要。 找异步验证器中防抖的代码实现中恰好看到了liyiheng学长的文章:https://segmentfault.com/a/11...,于是便参考了一下。 这里仅是阐明angular中formContorl异步验证器如何防抖的步骤: 1.创立(改写)异步验证器 vehicleBrandNameNotExist(): AsyncValidatorFn { return (control: AbstractControl): Observable<ValidationErrors | null> => { if (control.value === '') { return of(null); } return control.valueChanges.pipe( // 防抖工夫,单位毫秒 debounceTime(1000), // 过滤掉反复的元素 distinctUntilChanged(), // 调用服务, 获取后果 switchMap(value => this.vehicleBrandService.existByName(value)), // 对后果进行解决,null示意正确,对象示意谬误 map((exists: boolean) => (exists ? {vehicleBrandNameExist: true} : null)), // 每次验证的后果是惟一的,截断流 first() ) }; }增加异步验证器let formControl = new FormControl('', [], asyncValidate.vehicleBrandNameNotExist());之后咱们在v层在相干的标签上绑定该fromControl就能够了。 ...

March 28, 2022 · 2 min · jiezi

关于angular:angular动态表单

前言我的项目中须要实现一个模块,工作治理,这里的工作是多样的。比如说打疫苗是一个工作,咱们首先建设这个工作,而后在工作中规定几个字段,比如说打疫苗的地点,工夫,接种疫苗品牌等等。关爱老人是另一个工作,工作中也规定几个字段,何时去关爱老人,行为记录等。打疫苗工夫和关爱老人工夫须要用到工夫抉择组件,打疫苗地点个接种疫苗品牌须要用到列表抉择组件,行为记录须要用到文本输出组件。不同的字段须要用到不同的输出组件。 E-R图一个工作对应多个表单项,一个表单项有一个表单类型。一个工作对应每一个居民生成一个工作详情,一个表单项和一个工作详情独特对应一个表单值。比如说打疫苗这是工作,打疫苗的工夫,打疫苗的地点是表单项,打疫苗的工夫(表单项)对应表单类型是工夫(表单类型),打疫苗的地点(表单项)对应表单类型是单向抉择(表单类型)。打疫苗这个工作对应张三(居民)生成张三打疫苗工作(工作详情),对应李四(居民)生成李四打疫苗工作(工作详情),张三打疫苗工作(工作详情)的打疫苗工夫(表单项)是2022年3月18日(表单值),李四打疫苗工作(工作详情)的打疫苗工夫(表单项)是2022年3月10日(表单值)。 动静表单对于设置一个工作的多个表单项很简略,无非就是抉择一下表单项的表单类型,表单类型比方文本,工夫,单选,多选等。问题是如何依据表单类型显示不同的输出形式。比方表单类型是工夫就显示一个工夫选择器,表单类型是文本就显示一个输入框。当然咱们能够依据表单类型去进行判断,不同的表单类型显示不同组件,如下图。然而这意味着如果咱们日后每新增一种表单类型,就要去减少一个if,也就是变动一次代码,这显然不是咱们想要的。angular有动静表单的性能,帮忙咱们脱离繁冗的if判断(在减少一个新的表单类型时,动静表单也须要改变代码,比方新增一个表单类型对应组件)。首先咱们须要定义一个指令,这个指令的作用是标记一个地位通知angular把组件插到什么中央。 @Directive({ selector: '[FormItem]',})export class FormDirective { constructor(public viewContainerRef: ViewContainerRef) { }}而后咱们创立主页面组件v层 <p>form-detial works!</p><div class="ad-banner-example"> <h3>工作详情</h3> <ng-template FormItem></ng-template></div>咱们将咱们定义的名叫FormItem指令放入到ng-template标签中,ng-template是一个空白容器,不会有任何的负作用。主页面c层 export class IndexComponent implements OnInit, OnDestroy { formItems: FormItem[] = []; @ViewChild(FormDirective, {static: true}) adHost!: FormDirective; interval: number|undefined; constructor(private formService: FormService) {} ngOnInit() { this.formItems = this.formService.getFormData(); this.loadComponent(); } ngOnDestroy() { clearInterval(this.interval); } loadComponent() { for (var i = 0; i < this.formItems.length; i++) { const adItem = this.formItems[i]; const viewContainerRef = this.adHost.viewContainerRef; const componentRef = viewContainerRef.createComponent<FormComponent>(adItem.component); componentRef.instance.data = adItem.data; } }}初始化时,先通过服务层获取表单项数组,而后循环遍历每一个表单项,最初通过viewContainerRef的createComponent()办法加载表单项类型对应组件。对于不同的表单类型输出组件,咱们先规定一个公共父类接口。 ...

March 21, 2022 · 1 min · jiezi

关于angular:简述Angular组件间传值

对于略微接触过Angular组件的同学来说,父子组件传值应该没有什么问题。本文想谋求的是用一个艰深解释,帮忙同学们了解的更精确。 零、常识铺垫CSS选择器在介绍父子组件之前,先要理解一个概念——selector、选择器 咱们定义一个新组件时,肯定会有这个属性: @Component({ selector: 'app-village-edit', ① templateUrl: './village-edit.component.html', styleUrls: ['./village-edit.component.scss']})其中①就是选择器,就是通知别的组件,如果想调用我这个组件,就要应用本组件的选择器<selectorName></selectorName>来调用。 实质上就是定义了组件的HTML标签,就像常见的<p>标签、<button>标签一样。 一、什么是父子组件就像事实中父母和孩子的关系是绝对的一样,一个人对于它的父母来说,就承当了孩子的角色;对于它的孩子来说则承当了父母的角色。 父组件和子组件也是绝对的。 假如,一个组件在本人的HTML模板中,通过选择器(也就是特定的HTML标签)来调用其余组件时。咱们称这个组件为父组件,而那个被调用的组件称为子组件。 二、父组件调用子组件的办法定义了两个类: child.component.ts,它的选择器selector是: 'app-child'parent.component.ts,它的选择器selector是: 'app-parent', 此时,在parent组件的HTMl中援用child组件的选择器: <app-child></app-child>这样就实现了子组件的调用。此时,如果通过路由加载父组件,就会发现子组件也会在特定的地位被渲染进去。 三、父组件向子组件传值子组件应用@input装璜器接收数据子组件从父组件接管的值,会保留到子组件的变量中。所以用来接管传值的变量与一般变量惟一的区别,就是在惯例的变量上减少一个@input()注解。 定义一般变量是这样的: master = 'Master';如果用来接管传值,只有改成这样: @Input() master = 'Master';这样,master变量默认是'Master'字符串。 但如果父组件向其传值,变量就变成了接管的值。 父组件应用方括号[]发送数据惯例形式调用子组件: <app-child></app-child>如果子组件能够接收数据,就能够用[propertyName] = value的办法来传值。例如: <app-child [master]="hero"> </app-child>用这种写法能够实现:一旦组件渲染实现后,子组件中的master变量就是'hero'的值了。 当父组件中的变量值变动时,子组件也会同步变动,也就是说,子组件能够监听传过来的值的变动信息。 降级:子组件通过set办法监听传入数据变动在下面的形式中,对于传过来的值,尽管能够监听变动,但局限在于:子组件只能间接应用传入的值。如果想对传入的值进行解决或过滤,就要略微调整一下子组件。 惯例状况下,子组件是通过给变量加上@Input装璜器来接管参数的: @Input() name = 'name';如果想解决参数,只须要把接管传值的变量变成set办法即可: @Input() get name(): string { return this._name; }set name(name: string) { // 此处能够减少其余解决逻辑 this._name = name; } private _name = '';此时,_name是外部变量,当父组件对于name属性传入值的时候,会主动执行set name办法给_name赋值并减少其余的解决逻辑。 另一种降级:子组件通过ngOnChanges()生命周期钩子监听传入数据变动官网文档中写到:“当须要监督多个、交互式输出属性的时候,ngOnChanges()比用属性 setter 办法更适合。” ...

March 21, 2022 · 1 min · jiezi

关于angular:Angular如何在跨字段验证器中直接调用其它独立的验证器

在angular中对于表单动静验证的一种新思路一文中咱们给出了Angular我的项目进行字段校验的三种办法。本文咱们将重点围绕第一种办法展开讨论。 假如有如下利用: 该利用的性能是对输出的数值的奇偶数进行判断,如果满足条件,则启用Submit按钮,否则不启用。 跨字段验证因为对输出数值的校验是依据输出类型来辨别的,所以这里咱们须要一个跨输出类型及输出数值的验证器: ngOnInit(): void { this.formGroup.setValidators((formGroup) => { formGroup = formGroup as FormGroup; const type = formGroup.get('type').value as number; if (type === 0) { // 验证是否是偶数 1️ } else { // 验证是否为奇数 1️ } }); }尽管咱们能够在1️处间接写入验证器的逻辑,但从分工的角度上来讲,这往往是最坏的一种的计划。为此,咱们同时筹备了验证器: /** * 数字校验器 */export class NumberValidator { /** * 偶数校验器 */ static isEven(control: AbstractControl): ValidationErrors | null { const value = +control.value as number; if (Number.isInteger(value) && value % 2 === 0) { return null; } else { return { isEven: '输出的数字不是偶数' }; } } /** * 奇数校验器 */ static isOdd(control: AbstractControl): ValidationErrors | null { const value = +control.value as number; if (Number.isInteger(value) && value % 2 === 1) { return null; } else { return { isOdd: '输出的数字不是奇数' }; } }}应用独立的验证器有了独立的验证器后,咱们能够应用相似如下的代码,间接在跨字段校验器中进行调用: ...

March 17, 2022 · 1 min · jiezi

关于angular:angular中关于表单动态验证的一种新思路

在我的项目中,咱们有时候往往须要动表单的验证做动静的布局。比方在一个注册界面中同步注册两种用户,但两种用户的输出项却不是雷同的。 老师的话,要求输出工号: 学生用户的话,则要求输出学号: 咱们把这种情景,称为动静的表单验证。 在上述表中校验中,咱们要求: 工号与学号互不烦扰。抉择老师类型时,只判断工号是否曾经输出。抉择学生类型时,则只判断学号是否曾经输出。实现计划其实这个实现的计划有很多种。在我的项目中咱们曾经应用过的大体有三种: 应用跨字段验证器。订阅用户类型,将用户类型发生变化时,重置工号或学号的验证规定。订阅用户类型,将用户类型发生变化时,在fromGroup中增加或移除工号,学号FromControl。跨字段验证器Anguar的官网给出在在跨字段验证器的应用示例,该思维是在FromGroup上增加一个验证器,而后在该验证器中获取FormControl的值,在依据具体的状况来进行验证。 长处: 官网示例,学习成本低。间接将验证放到了验证器中,逻辑清晰。验证器不会对获取FromGroup的值产生影响。毛病: 无奈在FormControl间接定义验证条件,不直观。重置验证规定FromControl提供了clearValidators()来清空验证器,以及setValidators()来设置验证器,所以咱们能够订阅用户类型是否发生变化,在发生变化时,依据状况清空穿插字段的验证器,而后再从新对其验证器进行设置。 长处: 为动静地增加异步验证器提供了一种新的思路毛病: 验证规定不直观。代码量大。重置FromGroup项FromGroup提供的removeControl()使得咱们能够移除其中的FormControl,利用该机制咱们能够订阅用户类型发生变化后,依据状况来移除、增加相应的FormControl,从而达到动静验证表单的目标。 示例代码 C 层: export class AppComponent implements OnInit { name = 'Angular ' + VERSION.major; formGroup = new FormGroup({}); // 学号 studentNoFormControl = new FormControl(null, Validators.required); // 工号 teachterNoFormControl = new FormControl(null, Validators.required); // 用户类型 typeFormControl = new FormControl(null, Validators.required); ngOnInit(): void { this.formGroup.addControl('name', new FormControl('', Validators.required)); this.formGroup.addControl('type', this.typeFormControl); // 订阅类型的变动,从而决定在formGroup中增加学号还是工号FormControl this.typeFormControl.valueChanges.subscribe((type) => { if (type === 0) { this.formGroup.removeControl('studentNo'); this.formGroup.addControl('teacherNo', this.teachterNoFormControl); } else { this.formGroup.removeControl('teacherNo'); this.formGroup.addControl('studentNo', this.studentNoFormControl); } }); // 初始化用户类型为老师 this.typeFormControl.setValue(0); } onSubmit(): void { alert('submit'); } /** * 显示学号或是工号的input */ showStudent(): boolean { return this.typeFormControl.value === 1; }}V 层: ...

March 17, 2022 · 1 min · jiezi

关于angular:Angular-内容投影-content-projection-的一个问题的单步调试

问题形容我应用如下代码测试一个最简略的 Angular 内容投影场景: import { Component } from '@angular/core';@Component({ selector: 'app-zippy-basic', template: ` <h2>Single-slot content projection</h2> <ng-container></ng-container> `})export class ZippyBasicComponent {} 我冀望 app-zippy-basic 的使用者,将传入的 content,投射到元素 ng-container 外部。 生产代码如下图所示: <app-zippy-basic> <p>Is content projection cool?</p> <div>ok</div></app-zippy-basic>然而运行时,我在渲染出的页面里,基本看不到 Is content projection cool? 的显示。 问题剖析关上 Chrome 开发者工具,发现 app-zippy-basic 外部只有一个 comment 节点:ng-container 咱们在提供内容投影插槽的 Component 的 ng-container 之间轻易键入一些字符串,例如 DIV: 渲染后发现,ng-container 没能依照咱们冀望的工作。 通过查阅 Angular 官网,发现这里把 ng-container 和 ng-content 弄混同了。此处应该应用 ng-content. 解决方案ng-content 之间不容许再插入其余元素。仅仅充当一个占位符的角色。 ...

March 5, 2022 · 1 min · jiezi

关于angular:如何在子组件中观测父组件中方法的执行

咱们可能遇到上面这种情景:以后咱们要编写一个auto-complete组件,当用户输出的内容在数据库中有记录的话就依照记录保留,如果没有记录的话就先把用户输出的内容作为实体存在数据库中再进行保留。然而咱们必定不能只有用户输出的内容在数据库中没有找到就即便进行存储,如果这样的话只有用户扭转一次输入框中的内容就会进行一次存储,显然这是不合理的。所以咱们要做的是——当用户曾经确认输出完了所有内容点击了保留之后再进行上述操作。那么问题就来了——如何在子组件中得悉父组件点击了保留按钮呢? 前情提要:咱们规定了当点击保留按钮时会执行C层的onSubmit办法来提交表单。 <form [formGroup]="formGroup" (ngSubmit)="onSubmit(formGroup)"><app-vehicle-brand-auto-complete [formControlName]="formKeys.vehicleBrand" ></app-vehicle-brand-auto-complete> //这是咱们要调用的子组件 <div> <div> <button [disabled]="formGroup.invalid" type="submit">保留 </button> </div> </div></form>所以咱们要做的就是在子组件中得悉父组件中onSubmit办法的执行状况。起初我想的是angular会不会内置了这种办法,咱们只须要把父组件的onSubmit传过来就能够得悉它的调用工夫,然而通过测试并没有发现相似的办法。 要想得悉执行状况首先咱们要在子组件中用@Input注解申明一个observable类型的变量。 @Input() doSubmit: Observable<void>而后咱们再在父组件中这样进行申明 /** * 用于向子组件弹值,让子组件进行性断定 */ doSubmitSubject = new Subject<void>(); /** * 用于传递给子组件并让子组件进行订阅 */ doSubmit = this.doSubmitSubject.asObservable();此时doSubmitSubject就有了向自组件弹射信息的性能,doSubmit用来传递给子组件用来进行订阅。咱们如果想要实现在用户点击保留时执行子组件相应办法的话只须要在onSubmit办法中调用doSubmitSubject.next()进行传值即可。 onSubmit(formGroup: FormGroup) { this.doSubmitSubject.next(null); . . .}之后咱们再在子组件中对传过来的doSubmit进行订阅即可 ngOnInit(): void { this.doSubmit.subscribe( //咱们想要进行的操作 ) . . .}理解完办法之后咱们还须要晓得Subject是什么,为什么靠它就能够实现上述性能。 export class Subject<T> extends Observable<T> implements SubscriptionLike {. . .}查看其代码后咱们发现Subject继承自Observable。咱们查看其中的asObservable办法: asObservable(): Observable<T> { const observable = new Observable<T>(); (<any>observable).source = this; return observable; }官网给出的正文如下: ...

March 4, 2022 · 1 min · jiezi

关于angular:关于Angular引入swiper后autoplay失效的解决办法

因为angular不熟,最近在批改一个NG-ZORRO的我的项目。期间为了引入swiper,依照网上的装置引入形式(这步就不说了,应该都能搜到),引入后发现autoplay怎么设置都没成果,试了很多方法,前面去swiperjs官网看了才发现这段话。好家伙!!这么根底的性能也要导入么 import { Swiper, Autoplay } from 'swiper';Swiper.use([Autoplay]);export class TestComponent { swiper: Swiper; constructor(){} ngAfterViewInit() { this.swiper = new Swiper('.swiper-container', { slidesPerView: 3, initialSlide :1, centeredSlides: true, loop: true, observer: true, observeParents: true, autoplay:{ delay:2000, disableOnInteraction:false, stopOnLastSlide: false, }, }); } }完结出工!_(:」∠)_

February 28, 2022 · 1 min · jiezi

关于angular:基于-Angular-的企业级-Web-应用服务器端渲染的推荐建构

图片起源: 一个一般的 Angular 应用程序在浏览器中执行,在 DOM 中出现页面以响应用户操作。 Angular Universal 在服务器上执行,生成动态应用程序页面,而后在客户端上疏导。 这意味着应用程序通常会更快地出现,让用户有机会在应用程序齐全交互之前查看应用程序布局。 终点是用户的申请,通常从浏览器收回。 申请应该达到缓存层(例如 CDN),该层可能蕴含曾经在服务器端出现的应用程序,在这种状况下响应十分快。 CDN 通常将服务器端渲染存储一段时间,具体取决于业务需要。在给定工夫之后,缓存生效。为了以最佳形式进行此生效,倡议 CDN 在缓存被驱赶之前申请新的服务器端渲染,并在执行新渲染时持续提供现有缓存。 如果 CDN 没有缓存 SSR 渲染,它会将申请进一步转发到反向代理(例如负载均衡器)。 反向代理(通常是负载均衡器)将决定将申请转发到哪个 SSR 节点(在节点集群中)。 SSR 节点接管申请并开始渲染。它向 OCC API 收回 OCC 调用。 不倡议将 SSR 服务器/节点间接裸露给用户,因为渲染速度很慢并且无奈满足预期的响应工夫。 OCC API 缓存层负责缓存来自 OCC API 服务器的 OCC API 响应。通常,这意味着缓存 GET 和 HEAD 申请的响应。如果 OCC API 缓存层缓存了响应,则立刻将其返回给 SSR 节点,而无需将申请达到理论的 OCC API 服务器,从而使 SSR 节点执行渲染的速度十分快。 倡议为 OCC API 服务器设置某种缓存层,因为这部分在服务器端渲染时破费的工夫最多。 如果 OCC API 缓存层不蕴含给定申请的缓存响应,它会将其转发给 OCC 服务器进行解决。 ...

February 21, 2022 · 1 min · jiezi

关于angular:本周遇到的问题

问题一在写新增模块时遇到了这样一个问题——备用联系电话为非必填字段,那么在验证时就不必增加Validators.required字段进行验证,然而因为是填写电话号码,就须要验证电话号码是否符合规范,然而在已有的验证电话号码办法中输出为空也会被验证为不符合规范,这也就导致了如果用[disabled]="formGroup.invalid"判断保留按钮是否可用与理论布局不同——即备用联系电话成了必填字段。于是我作了以下尝试。 一开始想着只须要像这样订阅备用联系电话变动,当其有内容时减少验证,没有内容时勾销验证。 this.formGroup.get(this.formKeys.alternatePhone).valueChanges.subscribe( (value) => { if(value) { const alternatePhoneFormControl = new FormControl(value, YzValidators.isChinaMobileNumber); this.formGroup.setControl(this.formKeys.alternatePhone, alternatePhoneFormControl); } else { const newAlternatePhoneFormControl = new FormControl(value); this.formGroup.setControl(this.formKeys.alternatePhone, newAlternatePhoneFormControl); } } )然而这样的话订阅局部代码只会在第一次输出数据时执行一次,之后再有变动便不再执行,对此集体猜想尽管这是通过setControl()扭转所订阅的formControl,然而订阅关系并不会扭转,即这相当于新建了一个formControl,咱们订阅的还是之前的formComtrol的变动。 之后我又想试试间接订阅整个formGroup的变动来实现所需要求 this.formGroup.get(this.formKeys.alternatePhone).valueChanges.subscribe( (value) => { if(value) { const alternatePhoneFormControl = new FormControl(value, YzValidators.isChinaMobileNumber); this.formGroup.setControl(this.formKeys.alternatePhone, alternatePhoneFormControl); } else { const newAlternatePhoneFormControl = new FormControl(value, YzValidators.isChinaMobileNumber); this.formGroup.setControl(this.formKeys.alternatePhone, newAlternatePhoneFormControl); } } )然而这样的话也显然是不对的,setControl()也扭转了formGroup,这也就导致进入了死循环。 最初我又尝试着把订阅办法独立进去,当咱们设置完formControl后再对咱们设置的formControl进行订阅,从而实现目标。 ngOnInit(): void { this.initFormGroup(); this.subscribeAlternatePhoneChange(this.formGroup.get(this.formKeys.alternatePhone)) } subscribeAlternatePhoneChange(formControl: AbstractControl): void { formControl.valueChanges.subscribe( (value) => { console.log(value); if (value) { const alternatePhoneFormControl = new FormControl(value, YzValidators.isChinaMobileNumber); this.formGroup.setControl(this.formKeys.alternatePhone, alternatePhoneFormControl); this.subscribeAlternatePhoneChange(alternatePhoneFormControl); } else { const newAlternatePhoneFormControl = new FormControl(value); this.subscribeAlternatePhoneChange(newAlternatePhoneFormControl); this.formGroup.setControl(this.formKeys.alternatePhone, newAlternatePhoneFormControl); } } ) }

February 16, 2022 · 1 min · jiezi

关于angular:深入浅出-Angular-变更检测

Angular 中的变更检测是一种用来将应用程序 UI 的状态与数据的状态同步的机制。当应用逻辑更改组件数据时,绑定到视图中 DOM 属性上的值也要随之更改。变更检测器负责更新视图以反映以后的数据模型。浏览本文之前,倡议先查看我的前两篇和变更检测严密相干的博文,即《 揭秘Angular 生命周期函数》 和 《 Angular 之 zone.js 介绍》。 纸上得来终觉浅,绝知此事要躬行。为了让读者敌人们更容易了解,本文先从一个小的示例动手,而后逐渐开展。示例如下: // app.component.tsimport { Component } from '@angular/core';@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css']})export class AppComponent { title = 'aa'; handleClick() { this.title = 'bb'; }}// app.componnet.html<div (click)="handleClick()">{{title}}</div>示例比较简单,就是给div元素绑定了一个点击事件,点击该元素就会扭转变量title的值,界面的显示也会随之更新。框架如何晓得什么时候须要更新视图,以及如何更新视图的呢?咱们来一探到底。 当咱们点击div元素时,handleClick函数会被执行。那么在 Angular 利用中该函数是如何被触发执行的呢?如果你看过我之前的对于zone.js介绍的文章就会晓得,Angular 利用中点击事件曾经被zone.js接管。基于此答案便不言而喻,最开始必定是被zone.js触发执行,但在这里我还们还要进一步剖析间接调用关系进而层层开展。最靠近handleClick函数调用的是上面的代码: function wrapListener(listenerFn, ...) { return function wrapListenerIn_markDirtyAndPreventDefault(e) { let result = executeListenerWithErrorHandling(listenerFn, ...); }}上述代码中listenerFn函数指向的便是handleClick,但它又是wrapListener函数的参数。示例中元素绑定点击事件,相干模板编译产物大略是这样: function AppComponent_Template(rf, ctx) { ...... i0["listener"]("click", function AppComponent_Template_div_click_0_listener() { return ctx.handleClick(); })}首次加载利用会顺次执行renderView、而后执行executeTemplate,接着便触发了上述的模板函数,就这样元素的点击函数便一路传递到了listenerFn参数。到这里咱们理解了,点击函数的触发源头是zone.js,实在的点击函数传递却是由 Angular 实现,那么zone.js和 Angular 是如何关联的呢?zone.js会为每个异步事件安顿一个工作,联合本文示例来说,invokeTask便是由上面代码调用: ...

February 5, 2022 · 2 min · jiezi

关于angular:Angular-依赖注入原理

依赖注入是 Angular 的一大个性,通过它你可能写出更加易于保护的代码。但 JavaScript 语言自身并没有提供依赖注入的性能,那么 Angular 是如何实现依赖注入性能的呢?浏览本文,你就可能找到答案了。 一个典型的 Angular 应用程序,从开发者编写的源代码到在浏览器中运行,次要有 2 个关键步骤: 模板编译,即通过运行 ng build 等构建命令调用编译器编译咱们编写的代码。运行时运行,模板编译的产物借助运行时代码在浏览器中运行。首先咱们来编写一个简略的 Angular 利用,AppComponent 组件有个依赖项 HeroService: import { Component } from '@angular/core';import { Injectable } from '@angular/core';@Injectable({ providedIn: 'root'})export class HeroService { name = 'hero service'; constructor() { }}@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css']})export class AppComponent { title: string; constructor(heroService: HeroService) { this.title = heroService.name; }}下面的代码通过 Angular 编译器编译打包后的产物大略是这样: 由图可知编译的产物次要分为 HeroService 和 AppComponent 两个局部,因为本文次要是解说依赖注入的实现原理,所以对于编译产物的其余局部不做开展解说。当初咱们来重点关注一下依赖注入相干的代码,其中箭头所示代码: AppComponent.fac = function AppComponent_Factory(t) { return new (t || AppComponent)(i0.directiveInject(HeroService));};AppComponent_Factory 函数负责创立 AppComponent,不言而喻依赖项 HeroService 是通过 i0.directiveInject(HeroService) 创立的。 咱们持续来看 i0.directiveInject 函数做了什么。 ...

January 25, 2022 · 2 min · jiezi

关于angular:angluar中在input标签动态设置选择日期

前言在我的项目中须要设置用户不能抉择明天以前的日期,然而每天的日期是会变的,所以须要动静地设置截至日期。 <input>的max和min属性max,min 属性规定 <input> 元素的最大值和最小值,也就是能够顺次来设置截止日期。 // 抉择最大日期为2022年2月201 <input type="date" max="2022-02-20"> // 抉择最小日期为2022年2月182 <input type="date" min="2022-02-18"> // 日期为两者两头3 <input type="date" min="2022-02-18" max="2022-02-20">所以只有在组件初始化时获取当初的工夫戳,转换成yyyy-MM-dd的格局就能实现。另外留神月份的如果小于10,如果不写后面的0会设置失败。 设置截至日期首先获取当初的工夫戳,angular中能够间接应用new Date().valueOf()获取。 // 获取以后工夫戳 const nowDateTime = new Date().valueOf();获取到工夫戳后有以下几种解决形式。 办法一:用angular内置管道1 <input class="form-control" type="date" id="date"2 max = "{{nowDateTime | date:'yyyy-MM-dd'}}"3 [formControl]="formControl" >应用angular自带的管道间接将工夫戳转换成yyyy-MM-dd格局, 办法二:利用document.getElementById //获取以后工夫戳1 this.nowDateTime = new Date().valueOf(); //将工夫戳转换为yyyy-MM-dd格局2 this.maxTime = this.getTime(this.nowDateTime); //利用document.getElementById获取到相应的input的标签3 const inputElement = document.getElementById('date'); //设置最大日期4 inputElement.setAttribute('max', this.maxTime);这种办法能够间接在c层设置input的属性,不必在v层手动增加max属性。然而须要手动将工夫戳转换成yyyy-MM-dd格局。能够参考一下转换代码。 /** * 把工夫戳转化为 y-m-r 模式 * @param timeStamp 工夫戳 */ getTime(timeStamp: number): string { const date = new Date(timeStamp); const year = date.getFullYear().toString(); const month = date.getMonth() + 1; const day = date.getDate(); return year + '-' + (month < 10 ? '0' + month : month) + '-' + (day < 10 ? '0' + day : day); }办法三: 用[max]或[attr.max]模式增加在c层获取到yyyy-MM-dd格局的工夫maxTime后,也能够手动在input标签这增加[max]或[attr.max]属性。c层: ...

January 24, 2022 · 2 min · jiezi

关于angular:Angular-Universal-的演进历史

设想这样一个场景:您曾经在您的 Web 我的项目上工作了几个月,这很可能是一个 Web 应用程序,更具体地说,是一个“单页应用程序”。 然而当初是时候将您的应用程序交付并公布给数百万用户和……搜索引擎了。 为了使您的应用程序胜利,它必须被搜索引擎索引,即须要增加 SEO 反对! 咱们能够把 Angular Universal 了解成:Universal is Angular for the Headless Web. 您不再须要浏览器容器(也称为 WebView)来运行 Angular。 因为它与 DOM 无关,因而 Angular 能够在任何有 JavaScript 运行时的中央运行,比方 Node.js. 此图阐明了 Universal 在浏览器之外运行典型 Angular Web 应用程序的能力。 显然咱们须要一个 JavaScript 运行时,这就是咱们默认反对 Node.js(由 V8 引擎提供反对)的起因。 当然,当初也涌现出了越来越多的其余服务器端技术,如 PHP、Java、Python、Go…… 有了 Angular Universal 之后,您的应用程序能够在浏览器之外解释——让咱们以服务器为例——申请您的 SPA 的客户端将收到所申请路由/URL 的动态齐全出现页面。 此页面蕴含所有相干资源,即图像、样式表、字体……甚至是通过 Angular 服务传入的数据。 Universal 可能从新连贯一些默认的 Angular provider 实现,以便它们能够在指标平台上工作。 当客户端收到渲染的页面时,它也会收到原始的 Angular 应用程序—— Angular Universal 使得应用程序在浏览器里看起来简直是霎时就实现了加载。 加载后,Angular 客户端利用会解决剩下的事件。 事实上,Universal 与 Preboot.js 库捆绑在一起,其惟一作用是确保两个状态同步。Preboot.js 在幕后所做的只是简略而智能地记录 Angular 疏导程序之前产生的事件; 并在 Angular 实现加载后对这些事件进行重播。 ...

January 9, 2022 · 3 min · jiezi

关于angular:Angular-Universal-学习笔记

如果配置切当,咱们能够将所有的内容都在服务器端渲染,防止在浏览器端再次调用 API. 首先命令行装置 Angular Universal: ng add @nguniversal/express-engine执行命令行 npm run build:ssr browser:是执行命令行 ng build — prod 之后的后果。server folder: 是执行命令行 ng run PROJECT_NAME:server:production 的后果。运行命令行 npm run serve:ssr, 这会启动 Node.js Express 服务器,endpoint 来自 server.ts. 如何防止 server 和 client 反复调用 API? import TransferHttpCacheModule and BrowserTransferStateModule into AppModuleimport ServerTransferStateModule into AppServerModule查看服务器返回的 HTML 源代码,发现蕴含了 serverApp-state: src/app/app.server.module.ts 这个文件是主动生成的,定义了运行在服务器端 Angular 利用的 Root module. AppServerModule 导入了 AppModule,当前者 addon 的模式进行工作,确保 AppModule 不会被批改。 AppServerModule 的职责: disables animation by NoopAnimationsModule.很显然,服务器端运行的 Angular 利用不须要动画成果。disables handling of scrolling by Angular批改了 HTTP 申请的调用形式。浏览器端的 HTTP API 调用采取 XMLHttpRequest 实现,而服务器端的 API 调用通过 xhr2 (XMLHttpRequest Emulation for node.js) 实现。 ...

January 9, 2022 · 2 min · jiezi

关于angular:Angular-HostListener-装饰器的使用笔记

在 angular 中,通过不同的形式检测点击。 因为 click 是一个事件,因而在组件外部它是通过简略的事件绑定来检测的。 通过事件绑定在组件内进行检测的简略单击如下所示: @Component({selector: "geeks",template: `<h1 (click)="clicked()">{{ some_text }}</h1>`})export class GeeksComponent {constructor() {}some_text = "Click Here";clicked() { this.some_text = "Event Triggered";}}为了持续检测组件外的点击,@HostListener 装璜器在 angular 中应用。 它是一个装璜器,它申明一个要侦听的 DOM 事件,并提供一个带有处理程序办法的链接,以便在该事件产生时运行。 办法:这里的办法是应用@HostListener 装璜器。 在 angular 中,它是一个装璜器,有助于捕捉 DOM 内产生的任何类型的事件,并为开发人员提供基于该事件执行任何操作的灵活性。 在这里,在简略的点击事件上,处理程序将把点击事件援用到组件上,对于整个 DOM 的点击,它将应用 document:click 捕捉。 应用 HostListener 的语法如下: @HostListener(events, args)handler_name(args){ // Do something}HostListener的语法有三点须要留神: (1) eventName:顾名思义,它接管 DOM 中须要监听的事件的名称。(2) args:这些是在事件产生时传递给处理程序办法的参数集。 它以列表格局输出。(3) handlen_name:这里是事件触发时调用的办法定义。 它由 HostListener 主动调用。 示例:在组件内绑定单击 为了在组件内绑定单击事件,将 hostListener 的 eventName 保护成值 “click”。 在这种状况下,下面的代码将写为: ...

January 8, 2022 · 1 min · jiezi

关于angular:Angular-Change-Detection-的学习笔记

Angular 变化检测机制比 AngularJs 中的等效机制更通明且更易于推理。然而在某些状况下(例如在进行性能优化时),咱们的确须要晓得幕后产生了什么。因而,让咱们通过以下主题深刻理解变更检测: 如何施行变更检测?Angular 变动检测器是什么样子的,我能看到吗?默认的变更检测机制是如何工作的关上/敞开更改检测,并手动触发它防止变更检测循环:生产与开发模式什么是OnPush变化检测模式实际上呢?应用 Immutable.js 简化 Angular 应用程序的构建如何施行变更检测?Angular 能够检测到组件数据何时发生变化,而后主动从新渲染视图以反映该变动。然而,在像单击按钮这样的低级事件之后,它怎么能做到这一点,这可能产生在页面的任何中央? 要了解这是如何工作的,咱们须要首先意识到在 Javascript 中整个运行时(runtime)在设计上是可重载的。如果咱们违心,咱们能够重载 String 或者 Number 这些原生函数。 Overriding browser default mechanismsAngular 利用在启动时,会 patch 几个低级浏览器 API,例如 addEventListener,它是用于注册所有浏览器事件(包含单击处理程序)的浏览器函数。 Angular 将其替换addEventListener 的另一个新版本: // this is the new version of addEventListenerfunction addEventListener(eventName, callback) { // call the real addEventListener callRealAddEventListener(eventName, function() { // first call the original callback callback(...); // and then run Angular-specific functionality var changed = angular.runChangeDetection(); if (changed) { angular.reRenderUIPart(); } });}新版本 addEventListener为任何事件处理程序增加了更多功能:不仅调用了注册的回调,而且 Angular 有机会运行更改检测和更新 UI。 ...

January 8, 2022 · 2 min · jiezi

关于angular:登录的实现

本周写了登录方面的内容,理清了一些思路。 先写一下遇到的其余方面的问题: 问题一过后莫名就呈现了这个.angular文件,后果push的时候报错:超出文件大小。在.gitignore中疏忽掉.angular后仍旧上传。而后尝试着新建分支后把代码合一下再上传,但如同没有起作用。最初解决办法是回退版本:抉择相应的文件,右键点击git,而后show histroy ,抉择须要回退到的分支后,reset current Branch to here,就能够回退到相应的版本。 问题二ng s的时候控制台报了这个错,看起来应该是循环注入报的错,然而不晓得从何下手。组件名仅为t,而且文件地位也没有参考价值。最初解决办法是参考历史代码,发现在app.module.ts中没有注入HttpClinentModule引起的。引入后运行失常。 问题三在想给路由id为1的参数的时候,控制台报错parmsSubject为null。 fit('should create', () => { route.paramsSubject.next({id: 1}); ... }); 之后发现注入的RouterTestingModule引的angular库的,不是团队的库。如果不必团队的库的话,解决办法是间接调用loadById办法测试。 登录大略画了一个流程图来一步步依据代码来了解一下。 一V层通过submit提交from表单,并触发login办法 <form [formGroup]="loginForm" (ngSubmit)="login()">二login办法中:调用userService.login()办法,并以结构的user作为形参。 const user = { username: this.loginForm.get('username').value as string, password: this.loginForm.get('password').value as string }; this.userService.login(user) .subscribe(next:() => {},error:() => {})三userService中的login办法: login(user: { username: string, password: string }): Observable<User> { // 新建Headers,并增加认证信息 let headers = new HttpHeaders(); // 增加 content-type headers = headers.append('Content-Type', 'application/x-www-form-urlencoded'); // 增加认证信息 headers = headers.append('Authorization', 'Basic ' + btoa(user.username + ':' + encodeURIComponent(user.password))); // 发动get申请并返回 return this.httpClient.get<User>(`${this.baseUrl}/login`, {headers}) .pipe(tap(data => this.setCurrentLoginUser(data))); }1.首先结构一个HttpHeaders。2.给其增加 content-type.其中Content-Type 被指定为 application/x-www-form-urlencoded;其次,提交的数据依照 key1=val1&key2=val2 的形式进行编码,key 和 val 都进行了 URL 转码。大部分服务端语言都对这种形式有很好的反对。例如 PHP 中, $_POST['title'] 能够获取到 title 的值,$_POST['sub'] 能够失去 sub 数组3.encodeURIComponent()办法对明码进行编码。起因:如果明码字符串中蕴含了=或者&,那么势必会造成接管Url的服务器解析谬误,因而必须将引起歧义的&和=符号进行本义, 也就是对其进行编码。如将符号=,本义成%3D。btoa() 办法用于创立一个base-64 编码的字符串,也就说将编码好的明码和账号拼一起进行btoa编码。4.将构建好的header作为申请头,向后盾发送get申请。 ...

January 3, 2022 · 1 min · jiezi

关于angular:angular版本更新与配置文件问题

本周写日志零碎,须要咱们本人初始化一个angular我的项目,在初始化过程中遇到很多问题 问题版本问题因为初始化时angular版本问题导致无奈应用单元测试,而后尝试本人初始化一个angular我的项目,在初始化过程中遇到一些问题。首先是我初始化的的时候因为我电脑上装的Angular-cli的版本是教程的版本11.0.7 这个版本单元测试尽管没有问题,然而在应用团队一些库的时候呈现问题,因为智慧社区的版本是12.1.2,而教程的版本比拟旧,有一些库在引入时会有问题。 解决办法最开始想到的是有没有间接就能够对以后我的项目降级的办法,通过Google后,发现能够执行ng update 进行降级,然而那个博客写的不太分明,我执行后发现没什么成果,然而在博客中发现这样一句话而后尝试间接批改package中所有@angular的版本号,而后不报错,这种做法尽管感觉不太靠谱,然而能用。package.json 的 devDependencies 区列出的这些包能够帮忙你在本机开发利用之后就没有再管这个货色,但在写博客时又在官网文档中发现一个十分有用的工具angular更新指南这个工具会通知你如何进行更新ng update通过运行以下命令,对外围框架和 CLI 的以后稳固版本执行根本更新 ng update @angular/cli @angular/core要从一个次要版本更新到另一个,请应用以下格局 ng update @angular/cli@^<major_version> @angular/core@^<major_version>咱们倡议你始终更新到最新的补丁版本,因为它蕴含咱们自最后的主版本以来公布的修复程序。例如,应用以下命令获取最新的 10.xx 版本并应用该版本进行更新。 ng update @angular/cli@^10 @angular/core@^10总结对于一个刚开始的我的项目最好的更新办法就是间接删了,切换angular-cli的版本,而后间接新建,如果时曾经写了很多的则须要用下面提到的工具,依据给出的办法更新 angular 配置文件一个angular 文件初始化时有如下文件 ├── README.md 我的项目介绍文件,前期咱们能够变更为对以后我的项目的介绍,比方我的项目实现了什么性能,在开发时须要什么├── angular.json Angular我的项目的配置文件├── e2e 专门放集成测试文件的文件夹 ├── karma.conf.js Karma对应的配置文件├── node_modules 本我的项目依赖的其它npm包├── package-lock.json 本我的项目依赖于其它包(库)的具体装置状况(版本、下载地址等)├── package.json 本我的项目依赖于其它包(库)的状况├── src 源代码文件夹├── tsconfig.app.json typescript相干的配置文件├── tsconfig.json typescript相干的配置文件├── tsconfig.spec.json typescript测试相干的配置文件└── tslint.json 语法校验配置文件智慧社区次要批改的只有 TypeScript 配置TypeScript 是 Angular 利用开发中应用的主语言。 它是 JavaScript 的“方言”之一,为类型平安和工具化而做了设计期反对。浏览器不能间接执行 TypeScript。它得先用 tsc 编译器转译(transpile)成 JavaScript,而且编译器须要进行一些配置。官网文档链接 package-lock.json无论应用 npm 还是 yarn 装置的包,都会记录在 package.json文件中。CLI 的 ng new 命令会在创立新的工作区的同时创立一个 package.json。 这个 package.json 用于此工作区中的所有我的项目,包含由 CLI 在创立工作区时创立的那个初始我的项目。最后,这个 package.json 包含一组初始包,其中有些是 Angular 本身须要的,另一些是用来反对一些常见的利用场景。 随着利用的演变,你可能会往 package.json 中增加甚至移除一些包。package.json 文件中的包被分成了两组:dependencies 是运行利用的根底,包含Angular 包:Angular 的外围和可选模块,它们的包名以 @angular/ 结尾。反对包:那些 Angular 利用运行时必须的第三方库。腻子脚本:腻子脚本负责抹平不同浏览器的 JavaScript 实现之间的差别。devDependencies 只有在开发利用时才会用到。 ...

January 3, 2022 · 1 min · jiezi

关于angular:本周遇到的一些问题汇总

问题一:如何援用公有库起初认为既然node_modules是用来寄存装置的包的,那么间接把 @yunzhi放进node_modules里就能够了。尝试之后发现,尽管能够应用库中的函数,然而当间接援用函数时零碎不主动援用@yunzhi,须要人工手动引入。询问之后发现只须要依据援用的文件名在package.json中引入相应依赖,再从新执行npm install即可。之后又发现package.json中次要蕴含以下几种参数 "scripts":{...}"dependencies":{...}"devDependencies":{...}其中 scripts: 定义了一组能够运行的 node 脚本。 "scripts": { "ng": "ng", "start": "ng serve", "build": "ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" }也就是平时用到的ng s,ng t等。 dependencies 设置了作为依赖装置的 npm 软件包的列表。比方: "@angular/animations": "~12.1.2", "@angular/common": "~12.1.2", "@angular/compiler": "~12.1.2", "@angular/core": "~12.1.2", "@angular/forms": "~12.1.2","@yunzhi/ng-common": "0.0.6", "@yunzhi/ng-mock-api": "0.0.9", "@yunzhi/ng-theme": "0.0.8", "@yunzhi/utils": "0.0.3"这些都是为咱们的利用理论提供相应性能的依赖,也就是为MVC层提供服务的依赖。 devDependencies 设置了作为开发依赖装置的 npm 软件包的列表。比方: "karma": "~5.1.0", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.0.3", "karma-jasmine": "~4.0.0", "@angular/cli": "~12.1.2", "@angular/compiler-cli": "~12.1.2", "@types/jasmine": "~3.6.0", "@types/node": "^12.11.1", "@yunzhi/ng-router-testing": "0.0.2",这些依赖都是在开发时用于测试时所须要的依赖,理论产品中用户并不会用到这些依赖。 ...

January 2, 2022 · 1 min · jiezi

关于angular:现在你可以使用Angular来开发小程序

当初,你能够应用Angular来开发小程序历史大家可能都已经搜寻过,Angular平台是否能开发小程序,然而得出的搜寻后果并不是那么乐观.也已经有人讥嘲过,搞Angular的一帮人都是吹的很响,然而实际上连个小程序都不反对....这个我想大家应该都能看到...毕竟搜寻Angular开发小程序关键字,前几个搜寻后果就有...这条后果就像是挂在耻辱柱上...挂了将近4年.angular-miniprogram的诞生兴许是为了像其他人证实什么,兴许是对Angular现有的生态不甘心,我建设了这个我的项目大略半年多以前,这个还是一个仅仅能跑通hello-world的一个我的项目,甚至过后的我都没有预料到,短短半年我就能将这个我的项目的兼容水平进步到靠近原生的水平.明天2022.1.1,angular-miniprogram正式公布,你能够应用与原生一样的思维,甚至应用原生的大部分生态,来开发小程序我的项目的难点其实正如后面说的,这4年来,如同没有一个人来开发这个货色,起因很简略,就是难.其实抵赖这个是很不容易的,Angular我的项目的开发可能不须要理解太多常识,然而兼容一个平台,可能就须要查看Angular大量的源码,这些源码有的是运行时的,有点是构建时的,要适配的货色极多,并且,Angular还自带一套生态,我的项目能启动?没问题,再来适配下配套生态吧?这半年多的工夫整个我的项目思维上的重构,就有3次(颠覆设计,从新实现),直至最初一次的实现,就忽然有一种感觉,就是如同与设计Angular框架的人对上了电波,如同程序就应该这么设计,于是顺着这个思路,一直的实现,直至明天正式实现可能,整个我的项目即便公布,依然会有瑕疵,然而路线对了,我想,将来应该不会有大的扭转最初一次重构,简直从新批改了所有文件,为的就是不便保护及起初人查看及提交prAngular下小程序的启动流程我的项目地位都闪开,我当初要开始吹了...从Angular平台角度来讲,angular-miniprogram是即Angular(web 平台),Ionic(挪动端,ios,android),angular-electron(pc),nativescript(类原生 ios,android)之后,新反对的小程序平台(各个大厂的小程序)从小程序平台来讲,目前react营垒有taro代表,vue营垒有uni-app代表,而angular-miniprogram则为即这两者之后,Angular平台的惟一的一个所以说即便不分明这个我的项目到底是否难度高,也能够清晰的理解到这个我的项目的战略地位,也就是俗话说的,我能够不必,你不能没有我的项目地址最初,给所有的Angular开发者,心愿你们在有开发需要的时候想起这个我的项目,也欢送提issue,提pr,甚至火暴一点喷我也没关系,只有能对Angular的生态起作用,推广Angular有帮忙,说什么都能够的源码地址模板地址视频地址

January 1, 2022 · 1 min · jiezi

关于angular:echarts中使用graph关系图并且以markLine为底图的相关知识积累

之前写了一篇文章讲怎么在关系图里应用markLine作为底图,并且可能随着关系图的挪动而挪动前文链接 最近来了新需要,如下1.点击某个节点,只留下与之相干的节点,再次点击,复原原样2.鼠标挪动节点下来与之相干的线条高亮3.把底图的标识文字居中4.增加一个下拉框筛选要展现的节点。 其实总结下来,第一个需要以及第二个需要和最初一个需要一部分是共通的,也就是其实都是对ehcarts所展现的数据的操作,不同的中央在于:第一个和第二个需要只须要思考数据就行,最初一个需要须要思考底图也就是markLine的挪动以及某个区域内的节点数据没有了当前整体的挪动,也就是每一次点击树状下拉框,都要从新计算每个节点和markLine底图的地位。 本文章并非手把手教会怎么做出图中的成果,只是解说做这个我的项目中遇到的问题和具体的解决思路。 首先解决第一个需要,也就是点击某个节点,只展现这个节点相干的数据 解决思路:首先必定是拿到点击事件,而后点击节点的时候,拿到这个节点相干的数据,依据与之相干的某个筛选条件,把无关的数据筛选进去,赋值给echarts,数据就展示进去了。 第二点是再次点击的时候,复原到点击之前的数据。 解决思路:数据隔离,本地保留一份展现数据的拷贝,而后点击的时候,应用lodash的cloneDeep办法拷贝本地的拷贝数据,点击的时候给点击的项计数+1,同时把本地数据的拷贝中的对应项的计数+1。当某个节点的计数为2的时候,清空所有计数。 也就是点击的时候,把点击节点的计数+1,依据这个节点的某个条件筛选出无关的其余节点。再次点击的时候,如果有节点的计数为2,就把本地未经筛选的数据赋值回来,并且清空所有计数。 上代码解说 //html 代码<div id="room-bar-chart" echarts [options]="echartOptions" (chartMouseOver)="mouseOver($event,'over')" (chartMouseOut)="mouseOver($event,'out')" (chartInit)="InitChart($event)" (chartClick)="chartClick($event)" (chartContextMenu)="chartDbClick($event,menu)" style="height: 650px;"></div>//要看的其实就是chartClick事件//tschartClick($event: any){ if($event.dataType==="edge") return //点到线的时候就不做操作 let series = _.cloneDeep(this.echartOptions.series[0]);//备份以后echarts图的series let name = $event.name//拿到点击节点的名字 let relative: any[] = []//保留筛选出不论是source还是target与点击项无关的数据 edgeList.forEach((item,index,array)=>{//edgeList就是节点关系的数据 if(item.source===name||item.target===name){//如果指标节点或者起始节点是点击节点的name relative.push(item)//将其退出到关系数组中保留 } }) let seriesData:Array<any> = series.data//拿到现有echarts图的数据 let dataArray = []//保留 展现的数据里与relative无关的数据 for (let w = 0; w < seriesData.length; w++) {//循环本体的数据 let item = seriesData[w]; let index = w; if (item.name == name) {//找到本体上点击的数据 item.count++ this.echartOptions.series[0].data[index].count++//数据改变到原始数据上 } else {//如果不是,把本体上其余节点的计数清0 this.echartOptions.series[0].data[index].count = 0//数据改变到原始数据上 } if (this.echartOptions.series[0].data[index].count >= 2) {//如果同一个数据点击了两次 this.echartOptions.series[0].data.forEach((element: { count: number; }) => {//把所有数据的count计数清空 element.count = 0 }); this.setEchartsOption({ series: series })//重绘所有数据 重绘办法在上一篇文章里有。 return } for (let x = 0; x < relative.length; x++) { let data = relative[x]; if (item.name == data.source || item.name == data.target) { dataArray.push(item) } } } series.data = _.uniq(dataArray)//去重 this.setEchartsOption({series:series}) }这样点击某个节点的时候就可能实现第一次点击保留相干节点,第二次点击复原原样。 ...

December 28, 2021 · 2 min · jiezi

关于angular:Angular-变化检测详解

前言变化检测是前端框架中很乏味的一部分内容,各个前端的框架也都有本人的一套计划,个别状况下咱们不太须要过多的理解变化检测,因为框架曾经帮咱们实现了大部分的工作。不过随着咱们深刻的应用框架,咱们会发现咱们很难防止的要去理解变化检测,理解变化检测能够帮忙咱们更好的了解框架、排查谬误、进行性能优化等等。 什么是变化检测 ?简略的来说,变化检测就是通过检测视图与状态之间的变动,在状态产生了变动后,帮忙咱们更新视图,这种将视图和咱们的数据同步的机制就叫变化检测。 变化检测触发机会咱们理解了什么是变化检测,那何时触发变化检测呢?咱们能够看看上面这两个简略的DemoDemo1: 一个计数器组件,点击按钮Count会始终加 1 @Component({ selector: "app-counter", template: ` Count:{{ count }} <br /> <button (click)="increase()">Increase</button> `,})export class CounterComponent { count = 0; constructor() {} increase() { this.count = this.count + 1; }}Demo2: 一个Todo List的组件,通过Http获取数据后渲染到页面 @Component({ selector: "app-todos", template: ` <li *ngFor="let item of todos">{{ item.titme }}</li> `, }) export class TodosComponent implements OnInit { public todos: TodoItem[] = []; constructor(private http: HttpClient) {} ngOnInit() { this.http.get<TodoItem[]>("/api/todos").subscribe((todos: TodoItem[]) => { this.todos = todos; }); } }从下面的两个 Demo 中咱们发现,在两种状况下触发了变化检测: ...

December 27, 2021 · 5 min · jiezi

关于angular:如何解决-Angular-custom-library-module-在-ng-build-时无法被识别的错误

SAP Spartacus angular.json, 是 Angular CLI 主动生成的文件,外面针对 storefrontapp,生成的 tsConfig 属性,指向一个 tsconfig.app.json 文件,该文件指定 ng build 如何对该 app 进行构建。 这里应用的构建工具 builder 是 @angular-builders/custom-webpack:browser. types 属性为何为空? 很多 JavaScript 库,比方 jQuery、Jasmine 测试库和 Angular,会通过新的个性和语法来扩大 JavaScript 环境。 而 TypeScript 编译器并不能原生的辨认它们。 当编译器不能辨认时,它就会抛出一个谬误。 能够应用TypeScript 类型定义文件 —— .d.ts 文件 —— 来通知编译器你要加载的库的类型定义。 比方上图第13行代码的 build.process.env.d.ts: 很多库在本人的 npm 包中都蕴含了它们的类型定义文件,TypeScript 编译器和编辑器都能找到它们。Angular 库也是这样的。 任何 Angular 应用程序的 node_modules/@angular/core/ 目录下,都蕴含几个 d.ts 文件,它们形容了 Angular 的各个局部。 TypeScript 带有一个非凡的申明文件,名为 lib.d.ts。该文件蕴含了 JavaScript 运行库和 DOM 的各种罕用 JavaScript 环境申明。 ...

December 19, 2021 · 1 min · jiezi

关于angular:百度地图-测距控件-在多个地图上使用错误问题解决

我应用的框架是 Angular,在三个页面中都有应用到百度地图(实时监控、历史轨迹、区域查问),测距工具在 实时监控 页面增加的,切换其余两个页面,而后返回 实时监控 页面时,测距工具无奈应用,也没报错信息。 依照作者分享的这个代码,应用测距性能齐全失常。次要区别是第 550 行,能够看到与官网代码的一处不同。 原链如右:https://www.freesion.com/arti...

December 18, 2021 · 1 min · jiezi

关于angular:记录一下遇到的问题

问题一:ionic标签辨认不到 这个时候是在ng s下启的环境,始终报这个错。过后想到的是app.module.ts那里没有引入ionicModule,然而回去一看曾经引入了 imports: [ BrowserModule, AppRoutingModule, IndexModule, IonicModule.forRoot() ],而后就始终google,发现跟我雷同的经验 他报错的起因是遗记在app.module.ts declaration AppComponent,而后我看了果真如此 @NgModule({ declarations: [ ],因为以前都是都是写好的,也没想到问题居然出在这里。 问题二:页面不跳转在app-routing-module.ts写路由的时候遇到的问题这是web端的根路由,过后也没有认真想,就仿照着写了小程序端的路由 { path: '', component: BasicComponent, children: [ { path: 'dashboard', loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule) }, { path: 'party-building', loadChildren: () => import('./party-building/party-building.module').then(m => m.PartyBuildingModule), data: { title: '党建治理' } },写法如下: { path: '', component: IndexComponent, data: { title: '' }, children: [ { path: 'notice', loadChildren: () => import('./notice/notice.module').then(m => m.NoticeModule), }, { path: 'volunteer', loadChildren: () => import('./volunteer/volunteer.module').then(m => m.VolunteerModule), } ] }接着测试的时候,尽管控制台没有报错,然而ng s环境下的时候,路由不能失常跳转,点击告诉布告和志愿者的时候,页面始终刷新到本页面,不能失常跳转到相应页面。起初才明确不应该这么写。1.web的路由BasicComponent是父组件,包含菜单导航,头部主题等,每个子页面都须要显示父组件的内容。2而如果在微信小程序端这么应用的话,因为IndexComponent是父页面,每个子路须要显示父页面的内容,所以即便点击了志愿者,路由也曾经跳转到http://localhost:4200/volunteer,然而须要先显示父组件的IndexComponent内容,所以会笼罩掉子页面的内容,所以始终显示的是主页面的内容。 ...

November 20, 2021 · 2 min · jiezi

关于angular:angular路由中的惰性加载

默认状况下,NgModule 都是急性加载的,也就是说它会在利用加载时尽快加载,所有模块都是如此,无论是否立刻要用。对于带有很多路由的大型利用,思考应用惰性加载 —— 一种按需加载 NgModule 的模式。惰性加载能够减小初始包的尺寸,从而缩小加载工夫。 app.modules中: const routes: Routes = [ { path: 'items', loadChildren: () => import('./items/items.module').then(m => m.ItemsModule) }];loadChildren是路由的一个属性,该属性接管一个回调函数,这使得Angular能够在有需要的时候调用这个回调函数,从而实现了惰性加载。其后是一个应用浏览器内置的 import('...') 语法进行动静导入的函数。 其导入门路是到以后模块的相对路径。 m => m.ItemsModule这也是一个回调函数。m为参数代表后面import胜利的文件,所以能够应用任意的非法要害来替换,比方:f => f.ItemsModule。m.ItemsModule则示意文件中对应的ItemsModule类。 **then()办法是异步执行,就是当.then()前的办法执行完后再执行then()外部的程序这样就防止了,数据没获取到等的问题.** 此时再认真看一下loadchildren办法:LoadChildren函数返回一组要加载的路由。点开此办法咱们发现是这样定义的 type LoadChildren = LoadChildrenCallback;LoadChildrenCallback是一个回调函数,调用此函数以解析惰性加载的路由汇合。 Angular 会把 RouterModule.forRoot(routes) 增加到 AppRoutingModule 的 imports 数组中。 这会让 Angular 晓得 AppRoutingModule 是一个路由模块,而 forRoot() 示意这是一个根路由模块。 它会配置你传入的所有路由、让你能拜访路由器指令并注册 Router。 forRoot() 在利用中只应该应用一次,也就是这个 AppRoutingModule 中。 Angular 还会把 RouterModule.forChild(routes) 增加到各个个性模块中。这种形式下 Angular 就会晓得这个路由列表只负责提供额定的路由并且其设计用意是作为个性模块应用。你能够在多个模块中应用 forChild()。 forRoot() 办法为路由器治理全局性的注入器配置。 forChild() 办法中没有注入器配置,只有像 RouterOutlet 和 RouterLink 这样的指令。 ...

November 20, 2021 · 1 min · jiezi

关于angular:Angular-10-不再支持-IE910

本文作者|Stephen Fluin 译者|王强 策动|李俊辰,原文链接:原文链接https://blog.angular.io/versi...Angular v10.0.0 正式公布了!这是逾越整个平台(包含框架、Angular Material 和 CLI)的一次次要版本更新。这次的新版公布距离比以往短一些。自咱们公布 Angular 9.0 版以来只过来了四个月。 咱们尝试每年公布两个次要版本,以使 Angular 与其余 JavaScript 生态系统放弃同步,并给出可预测的时间表。咱们打算在往年秋天公布 v11 版。 新版内容新的日期范畴选择器Angular Material 当初提供了一个新的日期范畴选择器。能够通过 mat-date-range-input 和 mat-date-range-picker 组件应用它。 请参阅 StackBlitz 上的这个示例:https://stackblitz.com/angula... 更多细节请查阅 date range selection:https://next.material.angular... 对于 CommonJS 导入的正告当用户应用 CommonJS 打包的依赖项时,它可能导致应用程序收缩且变慢。 https://web.dev/commonjs-larg... 从 v10 开始,当你的构建引入这种包时就会看到正告。如果你解决依赖项时看到了这类正告,请将依赖项替换为 ECMAScript 模块(ESM)包。 可选的更严格设置当你应用 ng new 创立新的工作区时,v10 提供了一个更严格的我的项目设置选项。 ng new --strict启用此标记会应用一些新设置初始化你的新我的项目,这些设置能够进步可维护性,帮忙你提前捕捉谬误并容许 CLI 在你的利用上执行一些高级优化措施。具体来说,strict 标记执行以下操作: 在 TypeScript 中启用严格模式;将模板类型查看设置为 Strict;将默认包估算缩小约 75%;配置 linting 规定以避免申明 any 类型;https://palantir.github.io/ts...将你的利用配置为 side-effect-free,以实现更高级的 tree-shaking 优化。与生态系统放弃同步与平常一样,咱们对 Angular 的依赖项进行了一些更新,以与 JavaScript 生态系统放弃同步。 ...

November 19, 2021 · 1 min · jiezi

关于angular:Angular-tsconfigjson-文件里的-paths-用法和-scoped-module-定义

执行命令行: ng run storefrontapp:server:production 报错: Error: projects/storefrontapp/src/app/app.module.ts:33:30 - error TS2307: Cannot find module 'feature-libs/my-lib/src/public-api' or its corresponding type declarations.33 import { MyLibService } from 'feature-libs/my-lib/src/public-api'; 如果把谬误音讯里提到的第 33 行代码正文掉: build 就没有任何问题: 阐明这个谬误是 33 行代码引起的。 本地 storefrontapp Angular 利用,应用的配置文件是 tsconfig.app.json,这个文件扩大了工作区根目录下的 tsconfig.json 文件: 在 angular.json 里,tsconfig.app.json 作为 storefrontapp 的 tsConfig 的配置文件: 每当应用 Angular CLI 新建一个 library 时,该 library 的名称,都会主动写入 tsconfig.json 的 paths 节点里: 咱们执行完 npm build test-lib 之后,dist 文件夹里生成对应的资源文件: ...

November 16, 2021 · 1 min · jiezi

关于angular:Angular-Form-响应式Form-学习笔记

Angular 响应式表单应用显式的、不可变的形式,治理表单在特定的工夫点上的状态。对表单状态的每一次变更都会返回一个新的状态,这样能够在变动时保护模型的整体性。响应式表单是围绕 Observable 流构建的,表单的输出和值都是通过这些输出值组成的流来提供的,它能够同步拜访。 响应式表单通过对数据模型的同步拜访提供了更多的可预测性,应用 Observable 的操作符提供了不可变性,并且通过 Observable 流提供了变动追踪性能。 要应用响应式表单控件,就要从 @angular/forms 包中导入 ReactiveFormsModule,并把它增加到你的 NgModule 的 imports 数组中。 import { ReactiveFormsModule } from '@angular/forms'; 而后,生成一个新的 FormControl 实例,并把它保留在组件中。 要注册一个表单控件,就要导入 FormControl 类并创立一个 FormControl 的新实例,将其保留为类的属性。 import { Component } from '@angular/core';import { FormControl } from '@angular/forms';@Component({ selector: 'app-name-editor', templateUrl: './name-editor.component.html', styleUrls: ['./name-editor.component.css']})export class NameEditorComponent { name = new FormControl('');}通过在你的组件类中创立这些控件,你能够间接对表单控件的状态进行监听、批改和校验。 到当初为止,咱们只是在 Component 里创立了一个 FormControl 并把它赋给 Component 类的实例。然而,Component 的模板还感知不到这个 FormControl. 咱们须要批改 Component 模板文件,将模板里某个控件同 Component FormControl 实例绑定起来。 ...

November 7, 2021 · 2 min · jiezi

关于angular:Angular-组件生命周期

当 Angular 实例化组件类并渲染组件视图及其子视图时,组件实例的生命周期就开始了。生命周期始终随同着变更检测,Angular 会检查数据绑定属性何时发生变化,并按需更新视图和组件实例。当 Angular 销毁组件实例并从 DOM 中移除它渲染的模板时,生命周期就完结了。当 Angular 在执行过程中创立、更新和销毁实例时,指令就有了相似的生命周期 官网文档链接 响应生命周期事件能够通过实现一个或多个 Angular core 库中定义的生命周期钩子接口来响应组件或指令生命周期中的事件。这些钩子让你有机会在适当的时候对组件或指令实例进行操作。比方,OnInit 接口的钩子办法叫做 ngOnInit()。如果你在组件或指令类中实现了这个办法,Angular 就会在首次查看完组件或指令的输出属性后,紧接着调用它。 生命周期的程序ngOnChanges()->ngOnInit()->ngDoCheck()->ngAfterContentInit()->ngAfterContentChecked()->ngAfterViewInit()->ngAfterViewChecked()->ngOnDestroy() 1.变更检测钩子 ngOnChanges()当 Angular 设置或从新设置数据绑定的输出属性时响应。 该办法承受以后和上一属性值的 SimpleChanges 对象,一旦检测到该组件或指令的输出属性产生了变动,Angular 就会调用它的 ngOnChanges() 办法 ngOnChanges(changes: SimpleChanges){ console.log(changes); }即便@input()不应用set办法也能够屡次检测到变动 2.初始化组件或指令 ngOnInit()在 Angular 第一次显示数据绑定和设置指令/组件的输出属性之后,初始化指令/组件。在构造函数内部执行简单的初始化。组件的结构应该既便宜又平安。比方,你不应该在组件构造函数中获取数据。当在测试中创立组件时或者决定显示它之前,你不应该放心新组件会尝试分割近程服务器。Angular 会在调用 ngOnInit() 之前调用 ngOnChanges(),而且之后还会调用屡次。但它只调用一次 ngOnInit() 3.自定义变更检测 ngOnCheck()ngDoCheck执行机会 以下四种状况,Angular 是会为这个组件或者它的子组件执行变化检测1.组件的 @Input() 援用发生变化。2.组件的 DOM 事件,包含它子组件的 DOM 事件,比方 click、submit、mouse down 等事件。3.Observable 订阅事件,同时设置 Async pipe。4.ChangeDetectorRef.detectChanges(),ChangeDetectorRef.markForCheck()、ApplicationRef.tick(),手动调用这三种形式触发变化检测。ngOnCheck()检测,并在产生 Angular 无奈或不违心本人检测的变动时作出反应紧跟在每次执行变更检测时的 ngOnChanges() 和 首次执行变更检测时的 ngOnInit() 后调用能够解决某些无奈自动检测到变更的问题,如传入数组的状况能够在ngOnCheck()办法中实现本人的变更查看逻辑,如 ngDoCheck(){ if(this.oldName !== this.currentName){ // 执行操作 } }通过检测currentName和oldName是否雷同判断数据是否发生变化留神: 不倡议与ngOnChanges同时应用如果在ngOnCheck中不定义本人的检测逻辑,也能够当做自动检测变更的办法来应用,能够在外面间接执行须要的操作。 ...

November 6, 2021 · 1 min · jiezi

关于angular:angular-依赖注入

前言问题起因是因为在GriderService中遗记给根模块提供注入。即没有写 @Injectable({ providedIn: 'root'})手动注入过后想着,能不能单纯在组件中应用provide GriderService使这个组件失常用service提供的服务.就像上面这样: providers: [ { provide: GriderService } ]但事实上可行性不大。因为GriderService的函数中同样须要依赖注入HttpClient,如下 constructor(protected httpClient: HttpClient) { }所以,手动注入GriderService须要先手动注入HttpClient。 所以我尝试着手动注入了HttpClient。并且如果new一个HttpClient的话,须要先new一个HttpHandler,这是Httpclient的构造函数所须要的。或者我应该这么做: let httpHandler = new HttpHandler(); let httpClient = new HttpClient(httpHandler);然而,实际上HttpHandler是抽象类,并不能被实例化。 最初没胜利,并且在google上查找也没有比依赖注入更不便的了。我的确应该应用依赖注入。DI是 Angular 中的一个基本概念,也是使该框架如此有用的起因之一。 依赖注入 @Injectable() 装璜器标识服务类。该 providedIn 属性配置指定的 ModuleInjector,这里的 root 会把让该服务在 root ModuleInjector 上可用 长处用 @Injectable() 的 providedIn 属性优于 @NgModule() 的 providers 数组,不仅在于它方便快捷,还因为应用 @Injectable() 的 providedIn 时,优化工具能够进行摇树优化(Tree Shaking)简略说就说通过Tree Shaking,不该留在代码中的多余代码,则会被摇掉。 rootroot 实际上是 AppModule 的别名,示意根模块,咱们因而不须要额定导入 AppModule,所有的模块原则上都属于根模块的子模块,所以所有的子模块都能够无条件的应用root根模块上的资源 对于注射器在 root ModuleInjector 之上还有两个注入器,一个是额定的 ModuleInjector,一个是 NullInjector() ...

November 6, 2021 · 1 min · jiezi

关于angular:Spread-Operator

序言本周在写分页查问的时候遇到了对于...运算符的问题,想到之前例会提过这个运算符,然而当本人独立应用时还是会遇到问题。 分页查问查问条件:nameexport class TeacherIndexComponent implements OnInit { pageable = new Pageable(); loading = true; isOkLoading = false; teachers = new Array<Teacher>(); teacherPage = new Page<Teacher>(); params: Params; nameFormControl = new FormControl(''); constructor(private teacherService: TeacherService, private systemConfig: SystemConfigService, private route: ActivatedRoute) { } // 初始化 ngOnInit() { this.route.queryParams.subscribe((params: {page?: string, size?: string}) => { this.nameFormControl.setValue(params['name']); this.reload(params); }); } onSubmit(): void { const name = this.nameFormControl.value; this.reload({...this.params, ...{name}}); } /** * 查问 * @param params page: 当前页 size: 每页大小 */ reload(params: Params): void { // 发动查问params this.params = params; this.teacherService.pageByName( // 调用stringToIntegerNumber将查问的字符串转为number getDefaultWhenValueIsInValid(params['page'], '0'), getDefaultWhenValueIsInValid(params['size'], this.systemConfig.getPageSize()), { name: params['name'], }, ).subscribe(page => { this.teacherPage = page; }); } /** * 点击分页 * @param page 当前页 */ onPageChange(page: number): void { this.reload({...this.params, ...{page}}); } /** * 点击扭转每页大小 * @param size 每页大小 */ onSizeChange(size: number): void { this.reload({...this.params, ...{size}}); }}这是正确代码,然而当我将 onSubmit()办法改成如下: ...

November 6, 2021 · 2 min · jiezi

关于angular:Async-Pipe

前言之前在写我的项目的时候援用某个管道的时候 <td>{{ house | housePlace }} 发现成果不是想要的, 而是如下图的成果,并没有显示出正确的地址! 参考我的项目中的代码发现须要加上async管道 <td>{{ house | housePlace | async}} 为了了解该管道作用,于是有了本文,并便于当前查看。 Async Pipe在代码正文中AsyncPipe是这样形容的: The async pipe subscribes to an Observable or Promise and returns the latest value it has emitted. When a new value is emitted, the async pipe marks the component to be checked for changes. When the component gets destroyed, the async pipe unsubscribes automatically to avoid potential memory leaks.也就是说这个Async异步管道会订阅一个Observable或Promise,并返回它收回的最新值。当收回新值时,Async管道会标记组件以进行更改。当组件被销毁时,Async管道会主动勾销订阅,以防止潜在的内存透露。 并且它有几个特点:(起源google)1.Async Pipe makes the rendering of data from observable and promise easier.2.For promises, it automatically calls the then method.3.For observables, it automatically calls subscribe and unsubscribe.也就是说async管道使得从Observable的和Observable或Promise的数据更容易出现,并且对于observables来说,它会主动调用subscribe和unsubscribe; 对于Promise来说,它会主动调用then办法。 ...

October 30, 2021 · 1 min · jiezi

关于angular:Angular中的Subject与Observable

前言咱们在平时用的某些组件传入的值为数组,组件只能检测数组长度变动,而数组的值发生变化时组件不能检测到变动,也就无奈从新加载,这时能够应用Subject解决此类问题。 Subject官网文档 Sbject是一种非凡的Observable,区别在于Subject能够将值传递给多个观察者,而Observable个别状况下是一对一的。Subject的作用是当咱们须要通知其余组件以后组件的值产生了变动时,能够通过Subject被动向外发送一个值,而须要感知这个组件值变动的组件就能够订阅这个值,通过这种形式感知组件发生变化,或在不同组件之间传值。 具体应用办法上面以我的项目中的实例介绍具体用法以后我的项目中须要两个组件,抉择某一数据和抉择全副数据组件, 要求当抉择某一数据组件全被选中时,抉择全副数据组件主动被选中,如果不全副选中,全副抉择组件为未选中状态。因为抉择全副数据组件传入的是数组,无奈检测到值的变动。 v层代码 <tr> <td> 全选 <app-check-all [singleChange$]="singleChange$" [checkboxes]="items" (beChange)="onAllChange($event)"></app-check-all> </td> </tr> <tr *ngFor="let object of items"> <td> <app-check-single [checked]="object._checked" (beChange)="onSingleChange($event, object)"></app-check-single> </td> </tr>在父组件定义singleChangeSubject singleChangeSubject = new Subject<void>();抉择某一数据组件的onSinglesChange()中通过singleChangeSubject发送一个null值 onSingleChange($event: boolean, object: Checkbox) { object._checked = $event; this.singleChangeSubject.next(null); }留神:如果只想发送一次数据能够在执行完.next后执行.complete() 同样定义一个用于全副抉择组件监听的Observable singleChange$ = this.singleChangeSubject.asObservable();将singleChange$传入抉择全副组件,在抉择全副组件中订阅这个值就能够实现当抉择某一数据组件值变动时抉择全副组件能够检测到变动 this.singleChange$ .subscribe(() => { // 数据变动后须要执行的操作 })Subject变体BehaviorSubject它有一个“以后值”的概念。它保留了发送给订阅者的最新值。并且当有新的观察者订阅时,会立刻从 BehaviorSubject 那接管到“以后值”。 ReplaySubjectReplaySubject 相似于 BehaviorSubject,它能够发送旧值给新的订阅者,但它还能够记录 Observable 执行的一部分。ReplaySubject 记录 Observable 执行中的多个值并将其回放给新的订阅者。当创立 ReplaySubject 时,你能够指定回放多少个值: AsyncSubjectAsyncSubject 是另一个 Subject 变体,只有当 Observable 执行实现时(执行 complete()),它才会将执行的最初一个值发送给观察者。 ...

October 30, 2021 · 1 min · jiezi

关于angular:前端开发框架之Angular自定义组件学习分享

创立组件在components文件夹下创立一个数据库下载的专用组件。关上命令行(应用vscode编辑器的小伙能够间接应用Ctrl+` 快捷键关上终端,而后一路跳转到components文件夹:cd src\app\components在此目录下执行指令:ng g c es-download下面指令的意思是创立一个名为es-download的组件,应用下面的指令创立的组件是会前端培训被主动援用到components这个模块中的。components.module.tsimport { EsDownloadComponent } from './es-download/es-download.component'; //引入组件@NgModule({ declarations: [..., EsDownloadComponent],//申明组件})下面是在应用ng g c es-download指令时主动实现的但若是想在其它的模块中应用这个es-download组件,还得将其导出。导出的形式是将这个组件增加至components.module.ts文件的exports中:@NgModule({ declarations: [..., EsDownloadComponent], imports: [...], exports: [..., EsDownloadComponent],})export class ComponentsModule { }组件的根底概念查看es-download.component.tsimport { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-es-download', templateUrl: './es-download.component.html', styleUrls: ['./es-download.component.css']})export class EsDownloadComponent implements OnInit { constructor() { } ngOnInit(): void { }}能够看到此处从@angular/core中引入Component装璜器;并且建设了一个类,用@Component润饰它;在@Component中,设置了selector自定义标签和template模板。组件的几个要害知识点如下:组件与模块模块是在组件之上的一层形象,组件以及指令、管道、服务、路由等都能通过模块去组织。Angular提供了@NgModule装璜器来创立模块,一个利用能够有多个模块,有且只有一个根模块(Root Module),其余模块叫做个性模块(Feature Module)根模块是启动利用的入口模块,根模块必须通过bootstrap元数据来指定利用的根组件,而后通过bootstrapModule()办法来启动利用。建设一个根模块,命名为AppModule,并将它保留为app,module.ts。app.module.ts中通过@NgModule的bootstrap元数据指定AppComponent组件import { NgModule } from '@angular/core';import { AppComponent } from './app.component'; @NgModule({ declarations: [...], imports: [...], providers: [...], bootstrap: [AppComponent]})export class AppModule { }AppComponent组件即为根组件。再创立一个main.ts,利用platformBrowserDynamic().bootstrapModule()办法来启动根模块,并将AppComponent组件的内容展现到页面上。import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';import { AppModule } from './app/app.module'; ...

October 29, 2021 · 1 min · jiezi

关于angular:如何将Angular单项目升级为多项目

有时候在开发的过程中发现一个Angular我的项目不太够用,两个独立的我的项目又不太好复用。比方以后咱们须要一个新的运行于微信小程序端的H5我的项目,但却在想在新的H5我的项目中利用原WEB我的项目中实体、Share、Serivce以及MockApi等模块。此时,便须要将原来的Angular我的项目简略做个降级。 情景: 以后曾经有了一个运行于浏览器端的web我的项目。在以后我的项目的根底上新增一个wechat我的项目。将web我的项目中的一些私有的货色抽离进去组成一个公共库原web我的项目、新的wechat我的项目均能调用其公共库开发环境本文开发环境如下: panjie@panjies-iMac web % ng --version _ _ ____ _ ___ / \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _| / △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | | / ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | | /_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___| |___/ Angular CLI: 12.1.4Node: 14.16.0Package Manager: npm 6.14.11OS: darwin x64Angular: 12.1.5... animations, common, compiler, compiler-cli, core, forms... platform-browser, platform-browser-dynamic, routerPackage Version---------------------------------------------------------@angular-devkit/architect 0.1201.4@angular-devkit/build-angular 12.1.4@angular-devkit/core 12.1.4@angular-devkit/schematics 12.1.4@angular/cli 12.1.4@schematics/angular 12.1.4rxjs 6.6.7typescript 4.3.5生成新工程咱们进入原web我的项目的根文件夹,执行ng generate application wechat。 ...

October 26, 2021 · 2 min · jiezi

关于angular:如何使用-Visual-Studio-Code-调试-Angular-Schematics-实现

How to Debug an Angular Schematic using Visual Studio Code 可能在代码执行时调试和遍历代码是咱们开发工作流程的根底。这是一项必不可少的性能,使咱们可能调试和确定代码在做什么。它能够是咱们测试工作流程的一部分——附加调试器并在特定场景中执行测试。 调试还提供了一个机会来理解代码(咱们没有实现的)是如何工作的。原理图当然就是这种状况。作为 Angular 开发人员,咱们在应用 Angular CLI 时必定每天都在应用原理图。 咱们应用 CLI 创立新的工作区、我的项目、服务、组件、模块、类等 - 咱们很少思考幕后产生的事件。 然而,理解原理图的工作原理使咱们可能观赏为咱们提供如此多功能的工具。但它也有助于学习如何创立咱们本人的原理图。 当您运行/执行 Schematic 时,您正在运行一个节点程序。 咱们运行的具体程序是原理图。 因而,要开始,咱们须要一个原理图我的项目。 Tooling and Prerequisites之前,咱们能够应用 schema-cli 创立一个原理图我的项目,咱们须要确保咱们的开发环境中有以下包可用。 应用 -g 装置以下软件包以使其全局可用。 npm install -g @angular-devkit/schematicsnpm install -g @angular-devkit/schematics-cli该工具容许咱们应用原理图汇合创立新的原理图我的项目。 在终端中运行 schematics 命令。 输入是命令的选项列表。 应用汇合中的原理图我的项目创立带有 (3) 个原理图的示例原理图汇合 - 这些示例将帮忙咱们相熟原理图的工作原理。 schematics schematic --name=schematics-debugged 主动创立了三个文件夹: 咱们的新原理图我的项目是 (3) 个原理图的汇合。 示例我的项目中的每个 (3) 原理图都展现了原理图能够做什么以及它们如何协同工作(可组合原理图)的各种性能。 本文不会具体介绍这些原理图——它会专一于设置仅用于调试的环境。 当初您有了一个新的原理图我的项目。 您能够应用以下命令构建和测试项目: npm run buildnpm run test ...

October 18, 2021 · 1 min · jiezi

关于angular:如何运行-Angular-library-的原理图-Schematics

Jerry 的前一篇文章Angular 原理图 Schematics 学习 - 入手开发一个理论的例子,曾经开发好了一个能够运行的 Angular library Schematics. 本文介绍具体的运行步骤。 在工作区的根目录下,运行库的 ng build 命令。 ng build my-lib确保 build 通过: 如果遇到谬误,能够参考我的代码仓库的代码。 以及这篇文章:解决 Angular 官网下载的 library Schematics build 出错的方法 而后,进入库目录,构建原理图 cd projects/my-libnpm run build确保命令胜利运行。 链接这个库这些库和原理图都已打包好了,就放在你工作区根目录下的 dist/my-lib 文件夹中。 要运行这个原理图,你须要把这个库链接到 node_modules 文件夹中。在工作区的根目录下,运行 npm link 命令,并把你的可散发库的门路作为参数。 执行完之后: 发现我本人的库也呈现在工作区根目录的 node_modules 文件夹之下了: 同时,在我 Node.js 装置目录的 node_modules 文件夹下,也多了一个快捷方式: 最初,终于能够开始运行原理图了。 运行命令行: ng generate my-lib:my-service --name my-data传入的 name 参数值为 my-data. 从命令行打印的后果,发现 my-data.service.ts 曾经创立胜利了: ...

October 18, 2021 · 1 min · jiezi

关于angular:解决-Angular-官网下载的库-Schematics-执行-npm-run-build-时遇到的编译错误

我在 Angular 官网下载的 library Schematics 例子,运行命令行 npm run build 时,遇到如下谬误: npm run build my-lib@0.0.1 build c:\Code\SPA\schematics-for-libraries\projects\my-libtsc -p tsconfig.schematics.json schematics/my-service/index.ts:39:7 - error TS2322: Type 'string | number | boolean | JsonArray | JsonObject | null | undefined' is not assignable to type 'string | undefined'. Type 'null' is not assignable to type 'string | undefined'. 39 options.project = workspace.extensions.defaultProject; schematics/my-service/index.ts:42:44 - error TS2345: Argument of type 'string | undefined' is not assignable to parameter of type 'string'. Type 'undefined' is not assignable to type 'string'.![](https://img-blog.csdnimg.cn/img_convert/f2e94d1451031e232d6f55e81ce12e91.png)![](https://img-blog.csdnimg.cn/img_convert/8ce2f806cf11004fc7b34d1da6022012.png)# 解决办法这个 project 的数据类型是咱们本人在 schema.ts 里定义的,故调整成和 workspace.extensions.defaultProject 统一即可。批改之前:![](https://img-blog.csdnimg.cn/img_convert/fa25d1a0ef69801022fa2568faa46f96.png)批改之后,又遇到了新问题:Cannot find name 'JsonArray'![](https://img-blog.csdnimg.cn/img_convert/6a070732664af2f6255f8fc25f738c58.png)其实间接在原始代码地位加上 as string 即可,意思是通知编译器,程序员十分分明,在这个上下文里,workspace.extensions.defaultProject 的类型,`肯定`是 string.![](https://img-blog.csdnimg.cn/img_convert/9361a16756a57a9b969ec52665b77b7f.png)修复之后遇到另一个谬误:> 'options' is declared but its value is never read.ts(6133)![](https://img-blog.csdnimg.cn/img_convert/f7efb0f461417469bb9f76eb0a121bad.png)这个谬误的解决方案比较简单:参数名前加上一个下划线即可。![](https://img-blog.csdnimg.cn/img_convert/adc984e2bfe260baacc8793cd902ef90.png)最初,npm run build 胜利执行:![](https://img-blog.csdnimg.cn/img_convert/2ef3bb413e40355fe9cb71b7eceba3be.png)更多Jerry的原创文章,尽在:"汪子熙":

October 18, 2021 · 1 min · jiezi

关于angular:解决-Angular-官网下载的-library-Schematics-build-出错的办法

从 angular 官网下载 Schematics 的例子,在工作区的根目录下,运行库的 ng build 命令。 ng build my-lib谬误音讯: An unhandled exception occurred: Cannot find module '@angular-devkit/build-ng-packagr/package.json'Require stack:C:\app\node-v12.18.3-win-x64\node_modules\@angular\cli\node_modules\@angular-devkit\architect\node\node-modules-architect-host.jsC:\app\node-v12.18.3-win-x64\node_modules\@angular\cli\node_modules\@angular-devkit\architect\node\index.jsC:\app\node-v12.18.3-win-x64\node_modules\@angular\cli\models\architect-command.jsC:\app\node-v12.18.3-win-x64\node_modules\@angular\cli\commands\build-impl.jsC:\app\node-v12.18.3-win-x64\node_modules\@angular\cli\node_modules\@angular-devkit\schematics\tools\export-ref.js npm install 之后,问题仍旧: 然而,我本人的 lib github repo 没有这个问题: https://github.com/wangzixi-d... ng build my-lib 很顺利: 比拟两个工作区的 angular.json, 果然还是发现了一些区别: 留神下列标注的差异: 把 2 替换成 1 之后,又遇到如下的谬误音讯: Cannot find module ng-packagr 在我可能工作的仓库下的 ng version 输入: 而 Angular 官网: 做一个表格比拟一下: 这一行: "ng-packagr": "^12.1.1", 官网的 package.json 没有。增加到官网我的项目的 package.json 之后,问题隐没,build 胜利: ...

October 18, 2021 · 1 min · jiezi

关于angular:Angular-原理图-Schematics-学习-动手开发一个实际的例子

当 ng add 命令向我的项目中增加某个库时,就会运行原理图。ng generate 命令则会运行原理图,来创立利用、库和 Angular 代码块。 一些术语: 规定在原理图 中,是指一个在文件树上运行的函数,用于以指定形式创立、删除或批改文件,并返回一个新的 Tree 对象。 文件树在 schematics 中,一个用 Tree 类示意的虚构文件系统。 Schematic 规定以一个 tree 对象作为输出,对它们进行操作,并且返回一个新的 tree 对象。 开发人员能够创立下列三种原理图: 装置原理图,以便 ng add 能够把你的库增加到我的项目中。生成原理图,以便 ng generate 能够为我的项目中的已定义工件(组件,服务,测试等)提供反对。更新原理图,以便 ng update 能够更新你的库的依赖,并提供一些迁徙来毁坏新版本中的更改。上面咱们动手做一个例子。 在库的根文件夹中,创立一个 schematics/ 文件夹。 在 schematics/ 文件夹中,为你的第一个原理图创立一个 ng-add/ 文件夹。 在 schematics/ 文件夹的根级,创立一个 collection.json 文件。 编辑 collection.json 文件来定义你的汇合的初始模式定义。 如下图所示: collection.json 文件内容如下: { "$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json", "schematics": { "ng-add": { "description": "Add my library to the project.", "factory": "./ng-add/index#ngAdd" }, "my-service": { "description": "Generate a service in the project.", "factory": "./my-service/index#myService", "schema": "./my-service/schema.json" } }}下图高亮行的意思是:执行 ng add 时,调用文件夹 ng-add 上面的 index.ts 文件。 ...

October 18, 2021 · 4 min · jiezi