Coder Social home page Coder Social logo

blog-angular's People

Contributors

deepthan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

blog-angular's Issues

Angular transition()用法

transition:状态之间转换处理函数

function transition(
            stateChangeExpr: string, 
            steps: AnimationMetadata | AnimationMetadata[], 
            options: AnimationOptions | null = null
         ):AnimationTransitionMetadata;
  • stateChangeExpr : string

    A=>B,状态转换表达式,即从哪个状态切换到哪个状态。支持以下写法:

    1. 状态改变时启动动画
     transition("void => *", animate(500))
    
    1. 可以在两个状态变化上运行相同动画
    transition("void <=> *", animate(500)),
    
    1. 也可以定义几对状态切换执行同一动画
     transition("on => off, off => void", animate(500)),
    
  • steps :AnimationMetadata

    animate(),动画执行步骤,即几秒执行完,执行曲线是怎样的。
    animate('100ms ease-in') 或是一个数组 [animate('100ms ease-in'),animate(600)]

  • options: AnimationOptions

    可以传入动画的延迟值和动画输入参数,以更改计时数据和样式。详见 AnimationOptions函数。

用法

其实状态 transition("void => *", animate(500))表示的是进入,在这里可以用 :enter 表示:

transition(":enter", animate(500))

同理 transition(" *=> void", animate(500))离开可以这样写:

transition(":leave", animate(500)) 

报错处理之 -- 关于找不到模块

RROR Error: Uncaught (in promise): Error: Cannot find 'TableModule' in '../table/table.module'
Error: Cannot find 'TableModule' in '../table/table.module'

解决办法:

重新 ng serve 即可。

报错处理之 -- 关于polyfills.ts、'es5-shim'、'es6-shim'

  • 服务器上启动项目报了polyfills.ts、'es5-shim'、'es6-shim'相关的错

�[1m�[31mERROR in ./src/polyfills.ts
Module not found: Error: Can't resolve 'es5-shim' in '/root/.jenkins/workspace/Platform-Service-StaticResource/src'
 @ ./src/polyfills.ts 2:0-18
 @ multi ./src/polyfills.ts�[39m�[22m

�[1m�[31mERROR in ./src/polyfills.ts
Module not found: Error: Can't resolve 'es6-shim' in '/root/.jenkins/workspace/Platform-Service-StaticResource/src'
 @ ./src/polyfills.ts 3:0-18
 @ multi ./src/polyfills.ts�[39m�[22m
Build step 'Execute shell' marked build as failure
Finished: FAILURE
  • 解决办法:升级@angular/cli

npm uninstall -g @angular/cli
npm uninstall --save-dev @angular/cli
npm cache clean
npm install -g @angular/cli@latest
npm install --save -dev @angular/cli@latest

强制清除缓存

npm cache clean --force

或者

npm cache verify

白话Angular词汇

预 (ahead-of-time, AoT) 编译

在打包项目的时候提前编译好应用,打包好之后可以直接启动,而不是把编译器打包在应用中用的时候再编译。生产环境使用。

即时 (just-in-time, JiT) 编译

浏览器中启动并编译所有的组件和模块,动态运行应用程序。开发过程中使用。

指令 (directive)

告诉Angular怎么创建或改变HTML 元素。
分为三类:

  • 属性型指令
  • 结构性指令
  • 组件

属性型指令

监听或修改其它 HTML 元素、特性 (attribute)、属性 (property)、组件的行为的命令,通常用作修改 HTML 属性(样式等)。
如ngClass、ngStyle。

结构性指令

监听或者修改元素的结构,删除或者增加dom。如ngIf这个“条件化元素”指令,ngFor这个“重复器”指令。

组件 (component)

一个网页中一切皆可以视为组件。
一个按钮或者一个表格都可以是一个组件,其实组件就相当于汽车零件,一个零件由各种材料(html、css、js等构成),它只维护自身的逻辑。

封装桶

就是把一个组件的部分文件放在一个index.ts一起抛出去供别的地方引用。

├─login
│ ├─login.component.ts
│ ├─login.service.ts
│ ├─login.directive.ts
│ ├─ index.ts
...
index.ts里面引入 login.component.ts等文件

export * from './login.component.ts'; 
export * from './login.service.ts'; 

别的地方引用

import { loginComponent, LoginService } from '../login';

直接写组件的文件夹会默认寻找下面的index.ts

Angular 模块同样可以把组件、服务指令等放在一起抛出去。

注解 ?

实际上,是装饰 (decoration) 的同义词。

绑定(binding) ?

几乎都是指的数据绑定 (data binding) 和将 HTML 对象属性绑定到数据对象属性的行为。

有时也会指在“令牌”(也称为键)和依赖提供商 (provider) 之间的依赖注入 (dependency injection) 绑定。 这种用法很少,而且一般都会在上下文中写清楚。

数据绑定(data binding)

把后台数据展示出来,把用户操作转换为数据获取到。

启动/引导 (bootstrap) ?

You launch an Angular application by "bootstrapping" it using the application root NgModule (AppModule).

通过应用程序根 Angular 模块来启动 Angular 应用程序。 启动过程标识应用的顶级“根”组件 (component),也就是应用加载的第一个组件。 更多信息,见设置。

你可以在同一个index.html中引导多个应用,每个应用都有它自己的顶级根组件。

驼峰式命名法 (camelCase)

首字母小写,其他字母或缩写首字母大写。又叫小写驼峰式命名法 (lower camel case) 。
函数、属性和方法命名一般用在这个写法。

Pascal 命名法 (PascalCase)

每个单词或缩写都以大写开头,也称大写驼峰式命名法。
类名一般用这个写法。

中线命名法 (dash-case)

使用中线 (-) 分隔每个单词,也称为烤串命名法 kebab-case。
指令的选择器(例如my-app)和文件名(例如hero-list.component.ts)通常是用中线命名法来命名。

蛇形命名法

在多个词之间用下划线(_)分隔。也叫下划线命名法。

装饰器(decorator | decoration)

用一个不恰当的词语:人模狗样。
其实就是把一个未知的东西打扮一下让Angular知道它是什么。
@component告诉Angular它是组件,@input告诉Angular它是输入数据,@Injectable告诉Angular它是服务。

依赖注入(dependency injection)

需要的东西(依赖)直接从提供者(providers)那里拿过来用,不需要就不带提供者玩。

注入器 (injector) ?

Angular 依赖注入系统 (Dependency Injection System)中的一个对象, 它可以在自己的缓存中找到一个命名的“依赖”或者利用已注册的提供商 (provider) 创建这样一个依赖。

提供商 (provider) ?

依赖注入系统依靠提供商来创建依赖的实例。 它把一个查找令牌和代码(有时也叫“配方”)关联到一起,以便创建依赖值。

ECMAScript 语言

JavaScript统称,有多个JavaScript版本。最新批准的 JavaScript 版本是ECMAScript 2016(也称“ES2016”或“ES7”)。

ECMAScript 2015

简写: ES6 语言
缩写: ES2015 语言

ES5 语言

“ECMAScript 5”的简写,大部分现代浏览器使用的 JavaScript 版本。

输入属性(input)

别的组件传过来的数据,数据值会从模板表达式等号右侧的数据源流入这个属性 。它和输出属性一般用作父子组件传递信息。
别人(父组件)眼里的我:

// 等号右侧往左侧流入
<me [receiver]='别的传来的'></me>

其实我(子组件)是这样的

@Component({
  selector: 'me'
  ...
})
..
@input()  receiver : string;

ngOnChanges(){
    console.log("传过来的数据",this.receiver); // 打印出来‘别的传来的’
}

输出属性

通过触发父组件的事件进行传递数据。
事件流从这个属性流出到模板表达式等号的右边的接收者。
子组件ts

@Output() sendHero: EventEmitter<any> = new EventEmitter();

ngOnInit() {
    this.sendHero.emit('timo');
}

父组件:

html:
<me (sendHero)="getHero($event)"></me>
ts:
getHero(hero :string){
   console.log("传递过来的是哪个召唤师",hero); //传递过来的是提莫
}

插值表达式 (interpolation)

把变量插入html中。
ts:

public me = '万年青桐五';

html:

<div>我是{{me}},求带飞。</div> // 我是万年青桐五,求带飞。

生命周期钩子 (lifecycle hook)

不同时候可以做什么事情。比如20分钟才可以打大龙。

  • ngOnChanges - 在输入属性 (input)/输出属性 (output)的绑定值发生变化时调用。
  • ngOnInit - 在第一次ngOnChanges完成后调用。
  • ngDoCheck - 开发者自定义变更检测。
  • ngAfterContentInit - 在组件内容初始化后调用。
  • ngAfterContentChecked - 在组件内容每次检查后调用。
  • ngAfterViewInit - 在组件视图初始化后调用。
  • ngAfterViewChecked - 在组件视图每次检查后调用。
  • ngOnDestroy - 在指令销毁前调用。

模块 (module)

模块是一个内聚的代码块,具有单一用途。就像前端和后端是两个模块,如果想要交流的话通过接口(Angular里面是引用)。

可观察对象 (observable)

在接口请求的时候会用到,将异步数据转化为数据流,自身也可以生成一些自定义的数据流。它是引自的RxJS(Reactive Extensions for JavaScript),一个第三方包。

管道

一个可以把米做成米饭的函数,管道起到一个转换的作用。
Angular内置一些管道:DatePipe、UpperCasePipe、LowerCasePipe、CurrencyPipe和PercentPipe。
也可以自定义管道。
如:
假设

ts:
birthday = 1510156800000;
html:
<p> {{ birthday | date:"yy/MM/dd" }} </p>
转换后:
<p> 2017/11/9 </p>

响应式表单 (reactive forms) ?

通过组件中代码构建 Angular 表单的一种技术。 另一种技术是模板驱动表单。
构建响应式表单时:

  • 组件是“真理之源”。表单验证在组件代码中定义。
  • 在组件类中,使用new FormControl()或者FormBuilder显性地创建每个控件。
  • 模板中的input元素不使用ngModel。
  • 相关联的 Angular 指令全部以Form开头,例如FormGroup、FormControl和FormControlName。

动态表单非常强大、灵活,它在复杂数据输入的场景下尤其好用,例如动态的生成表单控制器。

路由器 (router)

通过配置不同的路由,加载不同的组件或模块,组合成不同的页面。

路由组件 (routing component)

一个带有路由插座 ( RouterOutlet ) 的 Angular 组件。
那什么是路由插座?其实可以理解为我们平时用的插排,每个孔都是一个路由,插上这个孔的电器是路由对应的组件。这个插座加上插上的电器才是我们想要的组件。

路由插座怎么用?里面的过程是什么样的?看下面。
文件形如:
├─list
│ ├─list.component.ts
│ ├─list.routes.ts
│ ├─lol
│ │ ├─ lol.component.ts // 里面包含了html,css等

list.component.ts

@Component({
  ..
  template: '
    <h1>拥有路由插座的组件</h1>
    <router-outlet></router-outlet>
  '
})

list.routes.ts


import { ListComponent } from './list.component';
import { LolComponent } from './lol/lol.component';

export const listRoutes = [{
     path: 'list',
        component: ListComponent,
        children: [
            {
                path: 'lol', 
                component: LolComponent
            }
}]

首先是匹配到list路由,这时候发现list的html里面有路由插座(router-outlet),就去找子路由,根据子路由把对应的组件插入到路由插座的位置。

范围化包 (scoped package)

是指Angular的框架源码,它根据不同功能分成不同模块的包,每个包都有一定的作用范围。以@angular开头。

  • @angular/core:核心模块,包含变化监测、依赖注入、渲染等核心功能的代码;
  • @angular/common:通用模块,包含一些常用的内置指令的代码;
  • @angular/compiler:编译相关功能;
  • @angular/platform-browser 和 @angular/platform-browser-dynamic 是跟平台相关的,这两个模块支持 Angular应用运行在浏览器里,对应的还有 @angular/platform-server,用于服务器端渲染;
  • @angular/forms: 引入表单相关;
  • @angular/http: 网络请求相关;
  • @angular/router : 路由相关;
  • @angular/animations: 动画相关。

分成模块的好处是不用的话就不需要引入这个包,比如我不写动画就不用引入@angular/animations这个包。
题外话:和本主题无关的其他几个包的作用:

  • core-js: 它是一个polyfill(填充库:不同的浏览器对Web标准的支持程度也不同,填充库(polyfill)能帮我们把这些不同点进行标准化 ),用于兼容一些高级的语言特性以兼容更多浏览器平台。
  • rxjs:用于解决异步和事件组合问题,多用于管理接口请求到的数据。
  • zone.js: 用来帮助处理浏览器异步事件的工具库,Angular的变化检测机制基于这个库实现的,这是必须引入的。

服务 (service)

服务用于封装不与任何特定视图相关的数据和逻辑,或者用于在组件之间共享数据和逻辑。一般用于接口请求或传递数据。

模板 (template)

其实就是个html

@Component({
  template: `<div>其实就是个html</div>`
})

模板表达式 (template expression)

html里面的表达式。Angular会 执行这个表达式得到值,并把它赋值给绑定目标的属性,这个绑定目标可能是 HTML 元素、组件或指令。

<div [property]="1+1"></div>

转译(transpile)

把一种形式的 JavaScript(例如 TypeScript)转换成另一种形式的 JavaScript(例如 ES5)的过程。

视图 (view)

视图是屏幕中一小块,用来显示信息并响应用户动作,例如点击、移动鼠标和按键。

Angular如何操作DOM?

Angular不建议直接操作DOM元素,正如 ElementRef 函数的源码中介绍的一样:

export declare class ElementRef {
    /**
     * The underlying native element or `null` if direct access to native elements is not supported
     * (e.g. when the application runs in a web worker).
     *
     * <div class="callout is-critical">
     *   <header>Use with caution</header>
     *   <p>
     *    Use this API as the last resort when direct access to DOM is needed. Use templating and
     *    data-binding provided by Angular instead. Alternatively you take a look at {@link Renderer}
     *    which provides API that can safely be used even when direct access to native elements is not
     *    supported.
     *   </p>
     *   <p>
     *    Relying on direct DOM access creates tight coupling between your application and rendering
     *    layers which will make it impossible to separate the two and deploy your application into a
     *    web worker.
     *   </p>
     * </div>
     * @stable
     */
    nativeElement: any;
    constructor(nativeElement: any);
}
export declare class ElementRef {
    /**
     * 如果不支持原生元素直接访问底层的本地元素或`null`
     * (e.g. 当应用程序在一个网络工作者运行).
     *
     * <div class="callout is-critical">
     *   <header>请谨慎使用</header>
     *   <p>
     *  当需要直接访问DOM时,使用此API作为最后的手段。 使用模板和
 数据绑定由Angular提供。 或者,您可以查看{@link Renderer}
 它提供的API即使在直接访问本地元素时也可以安全地使用
支持的。
     *   </p>
     *   <p>
     *   依靠直接的DOM访问可以在应用程序和呈现之间建立紧密的耦合
     *  层将使它不可能分开两个和部署你的应用程序到一个
     *  网络工作者。
     *   </p>
     * </div>
     * @stable
     */
    nativeElement: any;
    constructor(nativeElement: any);
}

ElementRef 获取DOM (ref:参考)

在应用层直接操作 DOM,就会造成应用层与渲染层之间强耦合,导致我们的应用无法运行在不同环境,所以不能直接操作 DOM 元素。
在浏览器中,通过 ElementRef 我们就可以封装不同平台下视图层中的 DOM 元素,最后借助于 Angular 提供的强大的依赖注入特性,我们就可以轻松地访问到 DOM 元素。

// HTML
<div class='deep'>蛮子</div>
// TS 
import { Component, ElementRef, ngAfterViewInit } from '@angular/core';
export class AppComponent {
  constructor(private elementRef: ElementRef) { }
  ngAfterViewInit(){
    let divEle = this.elementRef.nativeElement.querySelector('deep');
    console.log(divEle); ->打印出 '<div class='deep'>蛮子</div>'
  }
}

内置属性装饰器 @ViewChild 改变其样式

// HTML
<div #deep>蛮子</div>
// TS 
import { Component, ElementRef, ViewChild,  ngAfterViewInit } from '@angular/core';
export class AppComponent {
  @ViewChild('deep')  willChangeDiv: ElementRef;
  constructor(private elementRef: ElementRef) {}
  ngAfterViewInit(){
    this.willChangeDiv.nativeElement.style.backgroundColor = 'red';
  }
}

renderer2 对象提供的 API 优雅改变样式

// HTML
<div #deep>蛮子</div>
// TS 
import { Component, ElementRef, ViewChild,  ngAfterViewInit, Renderer  } from '@angular/core';
export class AppComponent {
  @ViewChild('deep')  willChangeDiv: ElementRef;
  constructor(private elementRef: ElementRef) {}
  ngAfterViewInit(){
    this.renderer.setStyle(this.willChangeDiv.nativeElement, 'backgroundColor', 'red');
  }
}

renderer2 提供了哪些方法?


 export declare abstract class render2 { 
    /**
     * This field can be used to store arbitrary data on this renderer instance.
     * This is useful for renderers that delegate to other renderers.
     */
    readonly abstract data: {
        [key: string]: any;
    };
    abstract destroy(): void;
    abstract createElement(name: string, namespace?: string | null): any;
    abstract createComment(value: string): any;
    abstract createText(value: string): any;
    /**
     * This property is allowed to be null / undefined,
     * in which case the view engine won't call it.
     * This is used as a performance optimization for production mode.
     */
    destroyNode: ((node: any) => void) | null;
    abstract appendChild(parent: any, newChild: any): void;
    abstract insertBefore(parent: any, newChild: any, refChild: any): void;
    abstract removeChild(parent: any, oldChild: any): void;
    abstract selectRootElement(selectorOrNode: string | any): any;
    /**
     * Attention: On WebWorkers, this will always return a value,
     * as we are asking for a result synchronously. I.e.
     * the caller can't rely on checking whether this is null or not.
     */
    abstract parentNode(node: any): any;
    /**
     * Attention: On WebWorkers, this will always return a value,
     * as we are asking for a result synchronously. I.e.
     * the caller can't rely on checking whether this is null or not.
     */
    abstract nextSibling(node: any): any;
    abstract setAttribute(el: any, name: string, value: string, namespace?: string | null): void;
    abstract removeAttribute(el: any, name: string, namespace?: string | null): void;
    abstract addClass(el: any, name: string): void;
    abstract removeClass(el: any, name: string): void;
    abstract setStyle(el: any, style: string, value: any, flags?: RendererStyleFlags2): void;
    abstract removeStyle(el: any, style: string, flags?: RendererStyleFlags2): void;
    abstract setProperty(el: any, name: string, value: any): void;
    abstract setValue(node: any, value: string): void;
    abstract listen(target: 'window' | 'document' | 'body' | any, eventName: string, callback: (event: any) => boolean | void): () => void;
}

动态添加 class :ngClass用法

ngClass用法,用于不通过情况更改不同的class

1. 绑定一个字符串,就像绑定一个class一样

html

  <p  [ngClass] = " class1 class2 " class = " class3 " >

css


.class1 {  color : red }
.class2 {  width : 10px }
.class3 {  height : 10px }

2. 绑定一个数组

html

 <p  [ ngClass] = [" class1", "class2 "]  class = " class3 " >

css


.class1 {  color : red }
.class2 {  width : 10px }
.class3 {  height : 10px }

3. 绑定一个对象:

html

 <div (click)=changeP()''>更改p的样式</div>
 <p  [ ngClass] = "{ 'class1' : true ,  'class2' : false , 'class3' :showThree }" >

css


.class1 {  color : red }
.class2 {  width : 10px }
.class3 {  height : 10px }

ts

public showThree :boolean = true ;


class1为 true所以样式是显示的, class2 为false 所以样式是没有的, class3 在ts里面赋值为 true 所以也是显示的。

4. 根据布尔值选择添加哪个class

ts

  private showDet : boolean = false;
  clickTit(){
    this.showDet = !this.showDet;
  }

html

[ngClass]="showDet ? 'show-det' : 'hide-det'"
//多个class
[ngClass]="[showDet ? 'show-det' : 'hide-det', showtit ? 'show-tit' : 'hide-tit' ]"

routerLink和routerLinkActive动态传参

假设个场景:做一个菜单,有的节点是点击出现子节点,有的节点是点击跳转路由

想要实现是连接则跳转,不是连接则不跳转,可以这样做。

ts

isLink : boolean = false;

html

<div
    [routerLink]="isLink ?   data.url : null"
    routerLinkActive="node-active">
</div>

css

.node-active{
    background: #006189;
}

可以正常跳转,但是路由只要是Null的话就会处于激活状态,所以还需对routerLinkActive进行判断。

html

<div
    [routerLink]="isLink ?  null : data.url"
    routerLinkActive="{{isLink ? 'node-active' : null }}">
</div>

Angular trigger()用法

trigger : 由它触发动画效果,称动画触发器。

function trigger(
            name: string, 
            definitions: AnimationMetadata[]  
         ):AnimationTriggerMetadata;
  • name :

    动画触发器名字, 如 growWidth

  • definitions :

    要执行的动画, 如

    [ transition("void => *", [style({opacity: 0}), animate(600, style({ opacity: 1}))] )],

angular路由高亮RouterLinkActive

路由高亮是什么?有什么好处?

当你在做一个后台管理系统,左边是一排路由导航,点击可以进入不同的页面,那么这个路由所在dom元素会添加上样式表示当前是位置。
但是一刷新你会发现,这个样式没了...
怎么办?

采用路由高亮:当路由被激活时允许你添加一个class在你添加路由的dom元素上,只有url变化时才会移除此样式。

当前路由被激活或者当前路由处于激活状态表示页面的url中路由和当前dom标签里的路由想匹配。

// 用法概览
@Directive({
    selector: '[routerLinkActive]',
    exportAs: 'routerLinkActive'
})
class RouterLinkActive implements OnChanges, OnDestroy, AfterContentInit {
  constructor(router: Router, element: ElementRef, renderer: Renderer2, cdr: ChangeDetectorRef)
  links: QueryList<RouterLink>
  linksWithHrefs: QueryList<RouterLinkWithHref>
  get isActive: boolean
  routerLinkActiveOptions: {...}
  set routerLinkActive: string[] | string
  ngAfterContentInit(): void
  ngOnChanges(changes: SimpleChanges): void
  ngOnDestroy(): void
}

示例

.red{
    color: red;
}

<a routerLink="/user/login" routerLinkActive="red">login</a>

当url是user或者/user/login的时候,a标签将会被加上classred。当url变化为别的时,class将会被移除。

如何添加两个class?

<a routerLink="/user/login" routerLinkActive="class1 class2">login</a>

routerLinkActive的两种写法


<a routerLink="/user/login" routerLinkActive="class1 class2">login</a>
<a routerLink="/user/login" [routerLinkActive]="['class1', 'class2']">login</a>

也可以给routerLinkActive进行配置参数

传递 exact: true表示路由完全匹配时才高亮,如

<a routerLink="/user/login" routerLinkActive="red" [routerLinkActiveOptions]="{exact:
true}">login</a>

你还可以使用isActive检查当前是否路由处于激活状态

<a routerLink="/user/login" routerLinkActive #rla="routerLinkActive">
  login {{ rla.isActive ? '激活' : '未激活'}}
</a>

如果当前路由处于激活状态,则会显示:

login 激活

非激活状态

login 未激活

上述的 rla 为routerLinkActive缩写,它可以随便定义。

重点来了:子菜单被选中,父级菜单也想高亮显示怎么办?

你可以在使用routerLink元素的父元素上使用RouterLinkActive指令

<div routerLinkActive="red" [routerLinkActiveOptions]="{exact: true}">
  <a routerLink="/user/login">login</a>
  <a routerLink="/user/reset">reset</a>
</div>
  • 只要给a标签的父元素div添加上routerLinkActiverouterLinkActiveOptions
    当路由是/user/login/user/reset时其父元素div会被添加上red样式。
  • 这里需要注意的是routerLinkActiveOptions指完全匹配,不然会出现url为/user时父元素也会被添加了red样式。

如果routerLink的值是变量的话

routerLink={{temp}}

temp就是你的变量名

Angular state()用法

state :定义目标处于某一状态时的样式

function state(
            name: string, 
            styles: AnimationStyleMetadata,
            options?: {params: {[name: string]: any}}
         ):AnimationStateMetadata;
  • name

    当前状态的名字,可以有多个名字,用逗号隔开,如:'active,clicked'
    这个name还可以用 void*表示: void代表动画执行前组件的状态; *代表动画执行后组件的状态,即组件的默认状态。

  • styles :

    处于这个状态时的样式,如style({width: 0})

  • options : 见AnimationStateMetadata函数

报错处理之 -- 关于RangeError: Maximum call stack size exceeded: 最大调用堆栈大小超出

core.es5.js:1020 ERROR Error: Uncaught (in promise): RangeError: Maximum call stack size exceeded
RangeError: Maximum call stack size exceeded
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.preloadConfig (vendor.bundle.js:1)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5802)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5806)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5798)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5806)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5798)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5806)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5798)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5806)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5798)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.preloadConfig (vendor.bundle.js:1)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5802)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5806)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5798)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5806)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5798)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5806)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5798)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5806)
    at RouterPreloader.webpackJsonp.../../../router/@angular/router.es5.js.RouterPreloader.processRoutes (router.es5.js:5798)
    at resolvePromise (zone.js:824)
    at zone.js:876
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:425)
    at Object.onInvokeTask (core.es5.js:3881)
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:424)
    at Zone.webpackJsonp.../../../../zone.js/dist/zone.js.Zone.runTask (zone.js:192)
    at drainMicroTaskQueue (zone.js:602)
    at <anonymous>

解决办法: 是不是路由没有在模块里面引入

import { PreviewLoadRouting } from './preview-load.routing';

@NgModule({
  imports: [
    ...
    RouterModule.forChild(PreviewLoadRouting) // 这里要引入,不然会报这个错误
  ]
 
})

HTML里面写变量方法汇总

ts 里面定义变量

private temp;

img里

  <img src="{{temp}}" alt="">

div里

 {{ temp }}

ngClass里

[ngClass]="temp ? 'class1 : 'class2' "

父组件往子组件传递

[getData]="temp"

如何用Angular搭建githubPages或码云page

1. 管理github仓库

在github上建立一个仓库,并且在本地新建一个ng的项目并关联起来。

2. 设置静态资源路径

下载angular-cli-ghpages用于提交Angular包

 npm i -g angular-cli-ghpages

在本地项目

ng build --prod --base-href “ https://USERNAME.github.io/REPOSITORY/ ”
或
ng build --prod --base-href 

3. 提交

ngh

4. github/码云配置

github:

graph LR
进入仓库 --> setting
setting--> GitHub&nbspPages
GitHub&nbspPages-->Source
Source-->gh-page&nbspbranch

gitee(码云):

graph LR
进入仓库 --> 管理
管理--> 默认分支
默认分支-->gn-pages

graph LR
进入仓库 --> 服务
服务--> Pages
Pages-->部署

访问地址

https://用户名.github.io/仓库名/
https://用户名.gitee.io/仓库名/

码云

https://deepthan.gitee.io/poetry/
https://deepthan.gitee.io/angular-demo/home

github仓库

https://github.com/deepthan/poetry/tree/gh-pages

githubpage 和 码云page区别

githubpage build的时候需要设置basehref

ng build --prod --aot --base-href “ https://USERNAME.github.io/REPOSITORY/ ” 

而后者默认是以仓库为基础路径不需要设置basehref.

参考地址

// 提交Angular应用到github page
https://github.com/angular-schule/angular-cli-ghpages

// 建立教程
https://jeneser.github.io/blog/2017/08/08/angular-deploying-app-github-pages/

遇到的问题

1. EPERM: operation not permitted, unlink ....
C:\Program Files\nodejs\node_global\node_modules\angular-cli-ghpages\node_modules\rimraf\rimraf.js:196
      throw er
      ^

Error: EPERM: operation not permitted, unlink 'C:\Program Files\nodejs\node_global\node_modules\angular-cli-ghpages\node_modules\gh-pages\.cache\.git\COMMIT_EDITMSG'
    at Object.fs.unlinkSync (fs.js:1080:18)
    at rimrafSync (C:\Program Files\nodejs\node_global\node_modules\angular-cli-ghpages\node_modules\rimraf\rimraf.js:306:17)
    at C:\Program Files\nodejs\node_global\node_modules\angular-cli-ghpages\node_modules\rimraf\rimraf.js:342:5
    at Array.forEach (native)
    at rmkidsSync (C:\Program Files\nodejs\node_global\node_modules\angular-cli-ghpages\node_modules\rimraf\rimraf.js:341:26)
    at rmdirSync (C:\Program Files\nodejs\node_global\node_modules\angular-cli-ghpages\node_modules\rimraf\rimraf.js:334:7)
    at rimrafSync (C:\Program Files\nodejs\node_global\node_modules\angular-cli-ghpages\node_modules\rimraf\rimraf.js:304:9)
    at C:\Program Files\nodejs\node_global\node_modules\angular-cli-ghpages\node_modules\rimraf\rimraf.js:342:5
    at Array.forEach (native)
    at rmkidsSync (C:\Program Files\nodejs\node_global\node_modules\angular-cli-ghpages\node_modules\rimraf\rimraf.js:341:26)

重新build或者全部打开的编辑器关了再开重新ngh即可.

group()

group : 组

function group(
            steps: AnimationMetadata[],
            options: AnimationOptions | null = null
         ): AnimationGroupMetadata;
  • steps : 动画步骤数据
    它可以由样式或动画函数调用组成。在一个组中,每个样式或动画函数的调用都会立即同时执行,当然你可以使用关键帧或者动画的延迟函数来延迟执行动画。
  • options: 参见 AnimationOptions
group([
  animate("1s", { background: "black" }))
  animate("2s", { color: "white" }))
])
什么是组呢?

组是一个并行运行的动画步骤列表。当存在很多样式在不同时间段开始或结束动画,我们需要对它统一进行管理的时候作用非常明显。利用组我们可以轻易控制它们同时开始动画或者同时结束动画。
group函数既可以在序列(sequence)中使用,也可以在转换(transition)中使用,它只会在所有内部动画步骤完成后继续执行下一条指令。

group官网介绍

AOT编译

1. ahead-of time 预编译 AOT

开发者可以在构造时(build-time)编译angular应用。通过compiler-cli、ngc编译应用程序,应用可以从一个模块工厂直接启动,意味着不再需要把angular编译器添加到JavaScript包中,预编译的应用程序加载加速,具有更高的性能。(compiler :编译器)

为什么要进行预编译?

编译可以让Angular应用达到更高层度的运行效率,主要是指的性能提升,但也包括电池节能和节省流量。
Angular采用了一个不同的方式。在给每个组件做渲染和变化检测的时候,它不再使用同一套逻辑,框架在运行时或者编译时会生成对js虚拟机友好的代码。这些友好的代码可以让js虚拟机在属性访问的缓存,执行变化检查,进行渲染的逻辑执行的快的多。

什么东西会被编译?

把组件的模板编译成一个JS类,这些类包含了在绑定的数据中检测变化和渲染UI的逻辑。

JiT编译模式的流程

非AoT应用的开发流程大概是:

  • 使用TypeScript开发Angular应用
  • 使用tsc来编译这个应用的ts代码
  • 打包
  • 压缩
  • 部署

部署好,在页面打开这个app:

  • 浏览器下载js代码
  • Angular启动
  • Angular在浏览器中开始JiT编译的过程,例如生成app中各个组件的js代码
  • 应用页面得以渲染

AoT编译模式的流程

使用AoT模式的应用的开发流程是:

  • 使用TypeScript开发Angular应用
  • 使用ngc来编译应用
    • 使用Angular编译器对模板进行编译,生成TypeScript代码
    • TypesScript代码编译为JavaScript代码
  • 打包
  • 压缩
  • 部署
    虽然前面的过程稍稍复杂,但是用户这一侧的事情就变简单了:
  • 下载所有代码
  • Angular启动
  • 页面渲染

Jit和AoT的主要区别

  • 编译过程发生的时机
  • JiT生成的是JS代码,而AoT生成的是TS代码。这主要是因为JiT是在浏览器中进行的,它完全没必要生成TS代码,而是直接生产了JS代码。

深入AoT编译

如果你对编译器的词法分析过程,解析和生成代码过程等感兴趣,你可以读一读Tobias Bosch的《Angular2编译器》一文,或者它的胶片。

《Angular2编译器》一文链接 https://www.youtube.com/watch?v=kW9cJsvcsGo

它的胶片链接 https://speakerdeck.com/mgechev/angular-toolset-support?slide=69

Angular模板编译器收到一个组件和它的上下文作为输入,并产生了如下文件:

  • *.ngfactory.ts

  • *.css.shim.ts : 样式作用范围被隔离后的css文件,根据组件所设置的ViewEncapsulation模式不同而会有不同

  • *.metadata.json :当前组件/模块的装饰器元数据信息,这些数据可以被想象成以json格式传递给 @component @NgModule 装饰器的信息。

    '*'是一个文件名占位符,例如对于hero.component.ts这样的组件,编译器生成的文件是 hero.component.ngfactory.ts, hero.component.css.shim.ts 和 hero.component.metadata.json。*.css.shim.ts和我们讨论的主题关系不大,因此不会对它详细描述。

*.ngfactory.ts 的内部结构

它包含了如下的定义:

  • _View_{COMPONENT}_Host{COUNTER} 我们称之为internal host component
  • _View_{COMPONENT}{COUNTER} 我们称之为 internal component

以及下面两个函数

  • viewFactory_{COMPONENT}_Host{COUNTER}
  • viewFactory_{COMPONENT}{COUNTER}
    其中的 {COMPONENT} 是组件的控制器名字,而 {COUNTER} 是一个无符号整数。他们都继承了 AppView,并且实现了下面的方法:
  • createInternal 组件的渲染器
  • destroyInternal 执行事件监听器等的清理
  • detectChangesInternal 以内联缓存优化后的逻辑执行变化检测

上述这些工厂函数只在生成的AppView实例中才存在。

detectChangesInternal中的代码是JS虚拟机友好的。

<div>{{newName}}</div>
<input type="text" [(ngModel)]="newName">

我们来看看编译后这个模板的代码,detectChangesInternal方法的代码看起来像是这样的:

// ...
var currVal_6 = this.context.newName;
if (import4.checkBinding(throwOnChange, this._expr_6, currVal_6)) {
    this._NgModel_5_5.model = currVal_6;
    if ((changes === null)) {
        (changes = {});
    }
    changes['model'] = new import7.SimpleChange(this._expr_6, currVal_6);
    this._expr_6 = currVal_6;
}
this.detectContentChildrenChanges(throwOnChange);
// ...

假设currVal_6的值是3,this_expr_6的值是1,我们来跟踪看看这个方法的执行。对于这样的一个调用 import4.checkBinding(1, 3),在生产环境下,checkBinding 执行的是下面的检查:

1 === 3 || typeof 1 === 'number' && typeof 3 === 'number' && isNaN(1) && isNaN(3);

上述表达式返回false,因此我们将把变化保持下来,以及直接更新 NgModel 的属性 model 的值,在这之后,detectContentChildrenChanges 方法会被调用,它将为整个模板内容的子级调用 detectChangesInternal。一旦 NgModel 指令发现了 model 属性发生了变化,它就会(几乎)直接调用渲染器来更新对应的DOM元素。

目前为止,我们还没有碰到任何特殊的,或者特别复杂的逻辑。

context 属性

也许你已经注意到了在internal component内部访问了 this.context 属性。
internal component中的 context 是这个组件的控制器的实例,例如这样的一个组件:

@Component({
  selector: 'hero-app',
  template: '<h1>{{ hero.name }}</h1>'
})
class HeroComponent {
  hero: Hero;
}

this.context 就是 new HeroComponent(),这意味着如果在 detectChangesInternal 中我们需要访问 this.context.name 的话,就带来了一个问题: 如果我们使用AoT模式编译组件的模板,由于这个模式会生成TypeScript代码,因此我们要确保在组件的模板中只访问 this.context 中的public成员。 这是为何?由于TypeScript的类属性有访问控制,强制类外部只能访问类(及其父类)中的public成员,因此在internal component内部我们无法访问 this.context 的任何私有成员。因此,下面这个组件:

@Component({
  selector: 'hero-app',
  template: '<h1>{{ hero.name }}</h1>'
})
class HeroComponent {
  private hero: Hero;
}

以及这个组件

class Hero {
  private name: string;
}

@Component({
  selector: 'hero-app',
  template: '<h1>{{ hero.name }}</h1>'
})
class HeroComponent {
  hero: Hero;
}

在生成出来的 *.ngfactory.ts 中,都会抛出编译错误。第一个组件代码,internal component无法访问到在 HeroComponent 类中被声明为 private 的 hero 属性。第二个组件代码中,internal component无法访问到 hero.name 属性,因为它在 Hero 类中被声明为private。

AoT与封装

在Angular的源码中,我们可以找到解决的办法,使用TypeScript的 /** @internal */ 注释声明,就能够达到既保证组件代码对AoT友好,又能够确保组件的封装良好的目的。

// component.ts
@Component({
  selector: 'third-party',
  template: `
    {{ initials }}
  `
})
class ThirdPartyComponent {
  /** @internal */
  initials: string;
  private _name: string;

  @Input()
  set name(name: string) {...}
}

initials 属性仍然是public的。我们在使用 tsc 编译这个组件时,设置 --stripInternal 和 --declarations 参数,initials 属性就会从组件的类型定义文件(即 .d.ts 文件)中被删掉。这样我们就可以做到在我们的类库内部使用它,但是我们的组件使用者无法使用它。

参考地址

https://mp.weixin.qq.com/s?__biz=MzIwMTYyMDEyMg%3D%3D&mid=2247483745&idx=1&sn=3fcb189b1d6b06b1a3311f3d9d532262&scene=45#wechat_redirect

Angular开发必备技巧(持续更新)

在页面打印出json对象

{{ temp | json}}

//ts:
lol = {
    region: '电一', // 大区
    grade: '黄铜五' // 段位
}
//html:
{{lol}} => [object,Object]
{{lol | json}} => {'region': '电一', 'grade': '黄铜五'}

很多公用的样式重复写好费事,该怎么处理?

  • 跟app文件夹有个同级文件 style.css/style.scss ,可以把公共样式写在这里,
    这里也可以封装一些常用样式供使用。
  • 也可以自定义全局样式文件,

想要更改第三方组件内的样式该怎么办?

在需要更改的样式前面加上 :host ::ng-deep ,如有个第三方包里有个class为 deepthan的元素,要修改其:

:host ::ng-deep .deepthan{
    color:#00        
}

项目中引入图片之类

html中引入,可以直接用绝对路径

<img src="assets/images/smrz.png" alt="smrz">

css中引入,用相对路径

../../assets/images/smrz.png

怎么换个端口启动项目?

我想在3201端口启动项目

  • 方法一:在启动的时候添加参数
ng server --port 3201  
  • 方法二:修改配置文件,然后直接 ng server
    在依赖包中找到@angular/cli/lib/config/schema.json,在523行或者直接搜索'port'在它键为default的地方填入 3201
 "serve": {
        ...
        "port": {
          "description": "The port the application will be served on.",
          "type": "number",
          "default": 3201
        }
}

如果你是用脚手架自动生成的项目,并且要用jasmine做单元测试的话,还需要做一步:找到protractor.conf.js文件把‘baseUrl’也改成新端口,让测试代码在改过的端口运行。

DecimalPipe

使用方法:
number_expression(value) | number[:digitInfo[:locale]]
{{e  | number:'3.1-5'}}
{{数字 | number:'限定的范围'}}
描述

根据给定的范围,将数字转换为符合格式的文本。

  • value值是一个数字类型

  • digitInfo 是一个字符串形如:
    {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}

    • minIntegerDigits: 设置整数的最小位数。默认是1位。
    • minFractionDigits: 设置小数点后的最小位数。默认值为0。
    • maxFractionDigits: 设置小数点后的最大位数。默认为3。
  • locale 是定义要使用的语言环境的字符串(默认使用当前的LOCALE_ID)。

示例
@Component({
  selector: 'number-pipe',
  template: `<div>
    <!--  num1 = 3.14,num2 = 2.718281828459045; -->
    
    <!-- 没有做转换,使用默认的转换系数 ‘1.3-0’,即整数最少为1位,小数点为0-3位 -->
    <p>{{num1 | number}}</p> <!-- 输出 '3.14'-->
    <p>{{num2 | number}}</p> <!-- 输出 '2.718'-->
    
    <!-- 整数最少3位,小数1到5位-->
    <p> {{num1 | number:'3.1-5'}}</p>  <!-- 输出 '003.14 '-->
    <p> {{num2 | number:'3.1-5'}}</p> <!--输出 '002.71828'-->
    
    <!-- 整数最少4位,小数5位-->
    <p>e (4.5-5): {{num1 | number:'4.5-5'}}</p><!--输出 '0,003.14000'-->
    <p>pi (3.5-5): {{num2 | number:'4.5-5'}}</p><!--输出 '0,002.71828'-->
    <!-- 'fr'是 LOCALE_ID -->
    <p>e (french): {{num2 | number:'4.5-5':'fr'}}</p><!--输出 '0 002,71828'-->
  </div>`
})
export class NumberPipeComponent {
  num1: number = 3.14;
  num2: number = 2.718281828459045;
}

路由预加载--预先加载延迟模块

  • 使用懒加载才会存在预加载的概念
  • 预加载是自己加载完了再去加载其他模块
  • 注意,是自己加载完也就是闲时再去加载

预加载指定模块

参考了很多例子,虽然实现了预加载但却是在本模块内容没加载完就会去加载延迟模块,这样就等于没有配置懒加载,所以得进行一些优化。

1. 虽然配置了预加载但却在本模块没加载完就去加载延迟模块了(bad)

├─app.module.ts
├─app.routes.ts
├─shared
       └─service
           └─preview-load.ts

preview-load.ts

import { Observable } from 'rxjs/Rx';
import { PreloadingStrategy, Route } from '@angular/router';

export class PreloadModules implements PreloadingStrategy {
    preload(route: Route, load: Function): Observable<any> {
        return route.data && route.data.preload ? load() : Observable.of(null);
    }
}

app.module.ts

import { ROUTER_CONFIG } from './app.routes';
import { PreloadModules } from './share/service/preview-load';
...
@NgModule({
 imports: [
    RouterModule.forRoot(ROUTER_CONFIG, {preloadingStrategy:  PreloadModules} )
 ],
 providers: [
    PreloadModules
  ],

app.routes.ts

...

export const ROUTER_CONFIG: Routes = [
  ...
  {
    path: 'login',
    component: RoleLoginComponent
  },
  {
    path: 'lists',
    loadChildren: './tables/tables.module#TablesModule', 
    data: { preload: true } 
  }
];

实测:

  • 在250ms的时候页面没有渲染好并且没有加载延迟加载的table模块

image

  • 在2.55s的时候文件加载完成并开始渲染页面,这时候已经加载了table模块的js了,这就违背了预先加载延迟模块的初衷了。所以得治~

image

2. 闲时再加载延迟模块(good)

部分文件、结构和上面一样,只对preview-load.ts文件做一些更改

import { Observable } from 'rxjs/Rx';
import { PreloadingStrategy, Route } from '@angular/router';
import { Injectable, NgZone } from '@angular/core';

export function requestIdle(zone: NgZone) {
  const win: any = window;
  if (win.requestIdleCallback) {
    return (fn) => win.requestIdleCallback(fn);
  }
  return (fn) => zone.runOutsideAngular(() => win.setTimeout(fn, 10));
}

@Injectable()
export class PreloadModules implements PreloadingStrategy {

  constructor(
    private zone: NgZone
  ) { }

  preload(route: Route, fn: () => Observable<any>): Observable<any> {
    requestIdle(this.zone)(fn);
    return Observable.of(null);
  }

}

看结果

  • 在2.92s的时候页面渲染完成那一刻还是没有加载table模块

image

  • 在所有加载完之后开始加载table模块

image

Angular cli用法

1. 什么是Angular cli

它是一个命令行界面工具,可用于初始化、开发、构建和维护 Angular 应用。

2. 如何安装

无npm先去下载nodejs

npm install -g @angular/cli

3. 如何使用

new|n指n是new的缩写,效果相同,下同。

3.1 创建项目(new | n)

ng new my-project

它会创建一个angular基础项目并且下载依赖运行项目,默认端口是4200。

可能需要用到的自定义内容

用法形如:

 ng new my-project --xxx=x

以下不特殊注明默认值均为false

参数 意义
`--force=true false`
`--routing=true false`
`--skipInstall=true false`
`--skipTests=true false`
`--force=true false`
`--style=css scss
`--packageManager=npm yarn
--prefix=prefix 指定选择器的前缀(组件、指令),如传入--prefix=dep则组件的selector会成为dep-xxx

呀,创建的时候没有自定义,怎么补救呢? 直接在angular.json中改~

3.2 新建文件(generate | g)

新建一个基础文件,里面有预设的代码片段。如ng generate service demo,则会在当前文件夹新建一个demo.service.ts

命令 作用 简写
ng generate module xx 新建模块 ng g m xx
ng generate component xx 新建组件 ng g c xx
ng generate directive xx 新建指令 ng g d xx
ng generate service xx 新建服务 ng g s xx
ng generate pipe xx 新建管道 ng g p xx

还有个更简单的方法:
vscode 中下载Angular Files插件,搜索alexiv.vscode-angular2-files即可找到。想在哪里创建点哪里。用起来爽歪歪。后面会写一个vscode开发angular好用的插件~

3.3 运行项目(serve | s)

配置 意义
--host=xx 设置应用的主机地址,别人可以根据这个地址访问你启动的应用。xx可以是你的ip或者0.0.0.0
`--open=true false`
--port 设置启动的端口号,避免启动多个项目占用同一个端口启动不起来(有人知道怎么让它检测到端口被占用自动加1?)
--proxyConfig=xx 设置代理文件
`--watch=true false`
`--aot=true false`

3.4 打包项目(build | b

配置 意义
--baseHref=xx index.html访问其他静态资源文件的相对路径。也可以在index.html的<base href="xx">中配置,还可以在.angular.json中的baseHref配置。
`--aot=true false`
`--optimization=true false`
--configuration=xx 指定打包环境的配置
`--prod=true false`
--configuration详解
当我需要打成不同的环境包时,可以使用以下方法:

在angular.json中做了如下配置,:

{
    projects: {
        project-name: {
            architect: {
                build: {
                    configurations: {
                        production: {
                            "fileReplacements": [
                                {
                                  "replace": "src/environments/environment.ts",
                                  "with": "src/environments/environment.prod.ts"
                                }
                            ],
                            ...
                        },
                        qa: {
                             "fileReplacements": [
                                {
                                  "replace": "src/environments/environment.ts",
                                  "with": "src/environments/environment.qa.ts"
                                }
                            ],
                            ...
                        },
                        sit: {
                             "fileReplacements": [
                                {
                                  "replace": "src/environments/environment.ts",
                                  "with": "src/environments/environment.sit.ts"
                                }
                            ],
                            ...
                        }
                    },
                    
                }
            }
        }
    }
}

之后可以进行不同的环境打包:

  • qa: ng build --c=qa
  • sit: ng build --c=sit
  • prod: ng build --c=production

--c 是 --configuration的缩写

3.5 更新项目(update

angular半年更新一个大版本,及时更新版本是非常重要的事情,如果落后高于1个版本以上,后续可能升级会很麻烦(别问我怎么知道,再问跳楼)。

从一个主版本升级到另外一个主版本

ng update @angular/cli@^<major_version>  @angular/core@^<major_version>

升级之前最好看下官方的升级指南

3.6 其他

ERROR in Error: Debug Failure. False expression.

ERROR in Error: Debug Failure. False expression.
    at tryReuseStructureFromOldProgram (D:\per\Angular-demo\node_modules\@angular\cli\node_modules\typescript\lib\typescript.js:70007:22)
    at Object.createProgram (D:\per\Angular-demo\node_modules\@angular\cli\node_modules\typescript\lib\typescript.js:69763:34)
    at _donePromise.Promise.resolve.then.then (D:\per\Angular-demo\node_modules\@angular\cli\node_modules\@ngtools\webpack\src\plugin.js:412:32)
    at process._tickCallback (internal/process/next_tick.js:109:7)

像这样莫名的错误,直接重启项目即可,或者重启编辑器,再不行就重启电脑...

报错处理之 -- 关于'Http' is not assignable to parameter of type 'Http'

Argument of type 'Http' is not assignable to parameter of type 'Http'
或
Argument of type 'HttpClient' is not assignable to parameter of type 'Http'.   Property '_backend' is missing in type 'HttpClient'.

解决办法:下载新版本的 @angular/http和 @ngx-translate/http-loader:

npm install @angular/http@latest --save
npm install  @ngx-translate/[email protected] --save

把原来用cnpm下载的包卸载了用npm下载

angularjs与angular概念区分

  1. 谷歌于 2009 年发布的 JavaScript 框架叫做 AngularJS,官网为 angularjs.org,代码库angular/angular.js
    而 2016 年发布的 JavaScript 开发平台叫做 Angular,官网为 angular.io,代码库为 angular/angular。目前发布的angualr2.0、angualr4.0、angualr5.0都只是版本号。
    关于两者名称的使用可以参考 Branding Guidelines for Angular and AngularJS

  2. Angular 的定位为开发平台而非 Web 框架,例如 Angular 也可用于移动端应用的开发等,可以参考 NativeScriptionic 等。

父子传递信息

1. 父传子: 父组件定义一个需要传递数据的变量

父组件 ts

或者为一个字符串:
public sendData = "我是要传递过去的数据";

父组件 html

<app-demo [getData]="data"></app-demo>

// 这里 [getData]="data" 是直接写 'data'不是 '{{data}}',也不是willCheckdata="data"

子组件 ts

import { Component,  Input,  OnInit } from '@angular/core';
@Component({
  selector: 'app-Demo',
  ...
})
export class DemoComponent implements OnInit {
    @Input() public getData: string;
    
    ngOnInit(){
        console.log("传递过来的值",this.getData);
    }
}
2. 子传父: 触发父元素事件

子元素 demo.component.ts:



import { Component, OnInit, Output, EventEmitter  } from '@angular/core';

@Component({
  selector: 'app-demo',
  ...
})

export class DemoComponent implements OnInit {

  @Output() sendVal: EventEmitter<any> = new EventEmitter();
  private val : string = '我是要传过去的字符串';
  ngOnInit() {
     this.sendVal.emit(this.val);
  }
  
}

父元素 html

 <app-demo (sendVal)="getVal($event)"></app-demo>

父元素ts

 getVal(value){
     console.log("value",value);
 }

Angular sequence()用法

sequence : 序列

function sequence(
            steps: AnimationMetadata[], 
            options: AnimationOptions | null = null
         ): AnimationSequenceMetadata;
  • steps : 动画序列的步骤

它可以由样式或动画函数调用组成。对style()的调用将立即应用提供的样式数据,而对animate()的调用将在它延迟时间后应用它的样式数据。

  • options: 参见 AnimationOptions
// 这是一个动画序列
sequence([
  style({ opacity: 0 })),
  animate("1s", { opacity: 1 }))
])
那到底什么是序列呢?

它是一个动画列表[A,B,C],里面的动画挨个执行:执行完A再执行B,B执行完再执行C。
它可以应用在 grouptransition里面,它只会在每个内部动画步骤完成后再继续执行下一条指令。

将一个数组作为动画数据传递到transition时,默认使用序列。如下面的[animate(500, style({...})), animate(500)]就是序列。

 animations: [trigger(
      'demo',
      [
        state('void', style({...})),
        state('*', style({...})),
        transition(
            'void => *', [animate(500, style({...})), animate(500)])
      ])],
它和组(group)的异同:

都是动画列表,序列是一个一个执行,组是并行执行。

Angular2+去除url中的#

1. 为什么要去除?

  • Angular官方指出:如果没有足够使用hash风格(#)的理由,还是尽量使用HTML5模式的路由风格;

  • 如果配置了hash风格,在微信支付或是Angular的深路径依然会出404的问题;

  • 当你需要使用GA等工具时,由于无法获取#号后的URL,导致每次路由切换都给其发送一个路径;

  • '#'有点丑。

2. 怎样才能去除?

有四个方法:

  • 前端 + ngx
  • 前端 + Apache
  • 前端 + Tomcat
  • GithubPages + 404 页面
2.1 前端

index.html的head里加

<base href="/">

app.module.ts

import { ROUTER_CONFIG } from './app.routes.ts';
@NgModule({
   imports: [
    ...
    RouterModule.forRoot(ROUTER_CONFIG) 
   //  RouterModule.forRoot(ROUTER_CONFIG, { useHash: true } )   这样写是带#的
  ],  
})

app.routes.ts:

import { NgModule } from '@angular/core';
import { Routes } from '@angular/router';

export const ROUTER_CONFIG: Routes = [
  {
   ...
  }
];

如果只配置前端会怎么样?

如果只配置前端虽然会去掉'#'但是一刷新页面就404,路径解析上出错了。
Angular是单页应用,它实现了前端路由功能,后台可以不再控制路由的跳转,将原本属于后端的业务逻辑全部丢给前端。

那么我们让WebServer把属于Angular管理的路由URL,都转发到index.html就可以解决404的问题了,也就是后面介绍的配置信息。

思考:hash模式为什么不会404?

2.2 ngx配置

带'***'的是需要自己配置 nginx.conf 文件内容

server {
    listen 80;  #监听的端口号 
    server_name  my_server_name; # 服务器名称  ***
    root   /projects/angular/myproject/dist;  #相对于nginx的位置 ***
    index index.html; #如果index.html存在,就结束查找过程,把这个文件附加到请求的request_uri后面,并且发起一个内部的redirect。

    location / {  # / 是匹配所有的uri后执行下面操作
        try_files $uri $uri/ /index.html; #try_files先寻找名为 $uri 文件,没有则寻找 $uri/ 文件,再没有就寻找/index.html
    }
}

try_files 详细解释:
如请求的是https://deepthan.gitee.io/poetry/life, $uri则是‘/life’,如果‘$uri’‘$uri/’都找不到,就会 fall back 到 try_files 的最后一个选项 /index.html发起一个内部 “子请求”,也就是相当于 nginx 发起一个 HTTP 请求到https://deepthan.gitee.io/poetry/index.html。这个请求会被 location ~ .php$ { ... } catch 住,也就是进入 FastCGI 的处理程序。而具体的 URI 及参数是在 REQUEST_URI 中传递给 FastCGI 和 WordPress 程序的,因此不受 URI 变化的影响。

2.3 Apache

Apache的根目录新建一个.htaccess文件

RewriteEngine On  
# 如果请求的是现有资源,则按原样执行
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]  
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d  
RewriteRule ^ - [L]

# 如果请求的资源不存在,则使用index.html
RewriteRule ^ /index.html  
2.4 Tomcat配置

Tomcat/conf/web.xml文件上添加

<error-page>
    <error-code>404</error-code>
    <location>/</location>
</error-page>
2.5 GithubPages + 404 页面

对于github pages来说,我们没办法直接配置Github pages,但可以在commit时添加一个404页。简单的解决方案如下:
我们在项目的根目录新建404.html,把index.html中的内容完全复制到404.html中就可以了。这样做github pages仍然会在恰当的时候给出一个404响应,浏览器将会正确处理该页,并正常加载我们的应用。

关于这方面的hack: S(GH)PA: The Single-Page App Hack for GitHub Pages

3. 带‘#’和不带‘#’原理上有什么区别呢?

3.1 这个得先说下什么是前端路由:

以前路由都是后台做的,通过用户请求的url导航到具体的html页面,现在我们在前端可以利用 Angular、vue、react等通过配置文件,达到前端控制路由跳转的功能。

前端路由的实现方法:

  1. 通过hash实现
    当url的hash发生改变时,触发hashchange注册的回调(低版本没有hashchange事件,通过轮回检测url实现),回调中去进行不同的操作,进行不同的内容展示。
    使用hash来实现的话,URI规则中要带上#,路由中#后边的内容就是hash,我们常说的锚点严格来说应该是页面中的a[name]等元素。
2.  HTML5的history api操作浏览器的session history实现  
    基于history实现的路由中不带#,就是原始的路由 
3.2 Angular中的路由策略

angular2提供的路由策略也是基于上面两个原理实现的,可以在@NgModule中通过providers配置或RouterModule.forRoot()配置:
1) 路由中有#

@NgModule({
  imports:[RouterModule.forRoot(routes,{useHash:true})]
})

@NgModule({
  imports:[RouterModule.forRoot(routes)],
  providers:[
     {provide: LocationStrategy, useClass: HashLocationStrategy} 
  ]
})

HashLocationStragegy
适用于基于锚点标记的路径,比如/#/**,后端只需要配置一个根路由即可。

2) html5路由(无#)
改用 PathLocationStrategy(angular2的默认策略,也就是HTML5路由),使用这个路由的常规路径不带#,这种策略需要后台配置支持,因为我们的应用是单页面应用,如果后台没有正确的配置,当用户在浏览器从一个路由跳往另外一个路由或者刷新时就会返回404,需要在服务端里面覆盖所有的路由情况(后端可以通过nginx或者apache等配置)。

@NgModule({
  imports:[RouterModule.forRoot(routes)],
  providers:[
    {provide: LocationStrategy, useClass: PathLocationStrategy} 
    // 这一行是可选的,因为默认的LocationStrategy是PathLocationStrategy
  ]
})

更改index.html中的base href属性,Angular将通过这个属性来处理路由跳转

<base href="/app/">

在后端的服务器上,用下面的正则去匹配所有的页面请求导向index.html页面。

we must render the index.html file for any request coming with below pattern

index.html

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>My App</title>
  <base href="/app/">
  <body>
    <app-root>Loading...</app-root>
    <script type="text/javascript" src="vendor.bundle.js"></script>
    <script type="text/javascript" src="main.bundle.js"></script>
  </body>
</html>

hash方案:路径比较丑,但是兼容老的浏览器,如果应用需要兼容低版本浏览器推荐使用
纯h5方案:只支持高版本浏览器,不过ng2+目前只支持高版本浏览器...

3) 定制自己的路由策略LocationStragegy

https://angular.io/api/common/LocationStrategy

4) Angular路由复用策略

http://m.itboth.com/d/7nQFni/typescript-angular2-angular4
3.3 前端路由优缺点
  • 优点:
    1.从性能和用户体验的层面来比较的话,后端路由每次访问一个新页面的时候都要向服务器发送请求,然后服务器再响应请求,这个过程肯定会有延迟。而前端路由在访问一个新页面的时候仅仅是变换了一下路径而已,没有了网络延迟,对于用户体验来说会有相当大的提升。
    2.在某些场合中,用ajax请求,可以让页面无刷新,页面变了但Url没有变化,用户就不能复制到想要的地址,用前端路由做单页面网页就很好的解决了这个问题

  • 缺点:
    使用浏览器的前进,后退键的时候会重新发送请求,没有合理地利用缓存,

参考文章

angular2 路由策略LocationStrategy
RouterModule
Angular 2 Remove Hash (#) from the URL
angular2 配合后端实现去除#
Nginx Angular2 html5mode PathLocationStrategy
Angular开启html5模式,配合nginx配置去除路由的#号

AnimationOptions()用法

AnimationOptions :动画的可选项

import { AnimationOptions } from '@angular/animations';
interface AnimationOptions { 
  delay?: number|string
  params?: {[name: string]: any}
}
  • delay: number|string
    动画的延迟值。
  • params
    在启动动画时传入参数,以更改样式。
以下动画函数可配置 AnimationOptions:
  • transition()
  • sequence()
  • group()
  • query()
  • animation()
  • useAnimation()
  • animateChild()
  • 使用AnimationBuilder服务构建的动画
子接口
  • AnimateChildOptions
  • AnimationQueryOptions

Angular keyframes()用法

keyframes:关键帧

function keyframes(
             steps: AnimationStyleMetadata[]
         ): AnimationKeyframesSequenceMetadata;

它的参数是一个style()的数组,表示步骤。

animate("5s", keyframes([
  style({ backgroundColor: "red", offset: 0 }),  // 0%
  style({ backgroundColor: "blue", offset: 0.2 }), // 20%
  style({ backgroundColor: "orange", offset: 0.3 }), // 30%
  style({ backgroundColor: "black", offset: 1 })   // 100%
]))

这里的 offset 和css3 keyframes里面的百分比一样,是时间的偏移量。
offset: 0.2表示动画在 20%的时候的样式。

此外,如果没有设置偏移量,那么偏移量将自动计算

animate("5s", keyframes([
  style({ backgroundColor: "red" }) // offset = 0
  style({ backgroundColor: "blue" }) // offset = 0.33
  style({ backgroundColor: "orange" }) // offset = 0.66
  style({ backgroundColor: "black" }) // offset = 1
]))

Angular动画怎么写

动画

1. 开场即有的动画

npm install @angular/animations
"@angular/animations": "^4.0.3"

先定义一个动画函数:fade-in.ts

import { trigger, state, style, transition, animate, keyframes } from '@angular/animations';

export const flyIn = trigger('flyIn', [
  state('in', style({transform: 'translateX(0)'})),
  transition('void => *', [
       animate(1000, keyframes([
        style({opacity: 0, transform: 'translateX(-100%)', offset: 0}),
        style({opacity: 1, transform: 'translateX(25px)',  offset: 0.3}),
        style({opacity: 1, transform: 'translateX(0)',     offset: 1.0})
      ]))
  ]),
  transition('* => void', [
        animate(3000, keyframes([
        style({opacity: 1, transform: 'translateX(0)',     offset: 0}),
        style({opacity: 1, transform: 'translateX(-25px)', offset: 0.7}),
        style({opacity: 0, transform: 'translateX(100%)',  offset: 1.0})
      ]))
  ])
]);

再在要用的地方的ts里

import { flyIn } from "../../animations/fly-in";
@Component({
  ...
  animations: [flyIn]
})

html里

<div [@flyIn]>
    我会飞 你呢
</div>

2. 主动触发动画

根据布尔值在两个状态之间切换

定义一个toggle.ts

import { trigger, state, style, transition, animate, keyframes } from '@angular/animations';
export const toggle = trigger('toggle', [
  state('in', style({ opacity: '1'})),
  state('out', style({ opacity: '0'})),
  
  transition('in => out', 
    animate(1000, keyframes([
      style({  opacity: '1'}),
      style({  opacity : '0'})
    ]))
  ),
  transition('out => in', 
    animate(1000, keyframes([
      style({   opacity : '0' }),
      style({  opacity : '1' })
    ])))
]);

主ts里面引用

import { toggle } from "../../../animations/fly-toggle";
@Component({
  selector: '...',
  templateUrl: '...',
  styleUrls: ['...'],
  animations: [toggle]
})
export class Deep {
    isActive: boolean;      
    changeState() {   
       this.isActive = !this.isActive;   
    }  
}   

html里

<button (click)="changeState()">修改状态</button>

<div [@flyToggle]="isActive ? 'in' : 'out'">
</div>
 
<div [@flyToggle]="isActive ? 'out' : 'in'">
</div>

点击按钮触发 changeState()函数,函数里再进行运算给 isActive赋值,传回 [@flyToggle]中触发动画效果。

**

<div [@flyToggle]="isActive ? 'in' : 'out'"
     (@expandState.start)="transitionStart($event)" (@expandState.done)="transitionDone($event)" >
</div>

@triggerName.start动画开始事件,@triggerName.done动画结束事件。
$event事件包含6个参数:

  • fromState: 动画初始状态
  • toState: 动画过渡状态
  • totalTime: 过渡时间
  • phaseName: 事件start或done
  • element: 过渡元素
  • triggerName: 过渡动画名称

3. 触发一个动画只有一个状态

有个动画 flyIn和布尔值 showAnimation

ts

...
[flyIn]
...
private  showAnimation : boolean = false;
...
// 在某个情况下改变布尔值开始动画
this.showAnimation = true;

html

<div [flyIn] *ngIf='showAnimation'>
    开始动画
</div>

4.

*代表其自适应到本来的数值
比如说我的width为80%,给它写一个动画

import { trigger, state, animate, transition, style, keyframes } from '@angular/animations';

export const autoWidth = trigger('autoWidth', [
    transition('void => *', [
        animate(1000, keyframes([
            style({
               width: 0
            }),
            style({
               width: '*'
            })
        ]))
    ])
])

它就可以自动从width为0 在1s内到 80%的宽度,而不是原宽度的 80%

Angular group()用法

group : 组

function group(
            steps: AnimationMetadata[],
            options: AnimationOptions | null = null
         ): AnimationGroupMetadata;
  • steps : 动画步骤数据
    它可以由样式或动画函数调用组成。在一个组中,每个样式或动画函数的调用都会立即同时执行,当然你可以使用关键帧或者动画的延迟函数来延迟执行动画。
  • options: 参见 AnimationOptions
group([
  animate("1s", { background: "black" }))
  animate("2s", { color: "white" }))
])
什么是组呢?

组是一个并行运行的动画步骤列表。当存在很多样式在不同时间段开始或结束动画,我们需要对它统一进行管理的时候作用非常明显。利用组我们可以轻易控制它们同时开始动画或者同时结束动画。
group函数既可以在序列(sequence)中使用,也可以在转换(transition)中使用,它只会在所有内部动画步骤完成后继续执行下一条指令。

group官网介绍

报错处理之 -- 关于Observable、map的问题

Property 'map' does not exist on type 'Observable<Response>'
或
Argument of type 'Observable<Response>' is not assignable to parameter of type 'Observable<Response>'

先把cnpm下载的rxjs删除

npm uninstall  --save rxjs

再用npm下载rxjs:

npm install --save rxjs@latest

这里推荐使用 yarn替代npm下载依赖包,地址 :yarn用法
npm 的版本是4.1.2
@ angualr/cli 版本是 1.3.2
node版本是 7.7.1

ng-content在一个节点中插入另外的节点

我们知道,父子组件传递信息可以传递字符串、对象等,但是你有想过如果传递的是dom元素怎么传递吗?
这时候就得用到 ng-content了。

  • 在同一个组件里:
<div dashen class='gailun'> 盖伦 </div>
<div  class='manwang'>
      <ng-content select=' [dashen] ' ></ng-content>
</div>

渲染后:


<div  class='manwang'>
     <div dashen class='gailun'> 盖伦 </div>
</div>

class为盖伦(gailun)的div的自定义属性为 dashen,在class为蛮王(manwang)的div里有个ng-content容器,它有个属性为 select,值为 [dashen],这样写的话就可以关联起来了。

不会 "产生" 内容,它只是投影现有的内容。你可以认为它等价于 node.appendChild(el) 或 jQuery 中的 $(node).append(el) 方法:使用这些方法,节点不被克隆,它被简单地移动到它的新位置。因此,投影内容的生命周期将被绑定到它被声明的地方,而不是显示在地方。

这种行为有两个原因:期望一致性和性能。什么 "期望的一致性" 意味着作为开发人员,可以基于应用程序的代码,猜测其行为。

  • 父子组件

子组件 heros.component.html,它ts里的selector为 'hero'

<p> 寒冰 </p>
<div  class='manwang'>
      <ng-content select=' [dashen] ' ></ng-content>
</div>
<p>木木</p>

父组件

<p>带我飞</p>
<hero>
    <div dashen class='gailun'> 盖伦 </div>
</hero>

渲染后

<p> 寒冰 </p>
<div  class='manwang'>
      <div dashen class='gailun'> 盖伦 </div>
</div>
<p>木木</p>

绑定方法

其实和jq的$("")选取dom元素差不多。

  • 属性选取
    属性为dashen,select里面为[dashen]
<div dashen> 盖伦 </div>
 <ng-content select=' [dashen] ' ></ng-content>
  • class选取
    class为dashen,select里面为 .dashen
<div class='dashen'> 盖伦 </div>
 <ng-content select=' .dashen ' ></ng-content>
  • id选取
    id为dashen,select里面为 #dashen
<div id='dashen'> 盖伦 </div>
 <ng-content select=' #dashen ' ></ng-content>
  • 节点选取
    tag为dashen,select里面为 dashen
 <dashen> 盖伦 </dashen>
 <ng-content select=' dashen ' ></ng-content>

angular路由跳转RouterLink

routerLink本身支持两种写法:

<a routerLink="detail">
</a>

<a [routerLink]="['detail']">
</a>

routerLink的值有哪些写法,又有什么区别呢?

假设当前路由为

`http://localhost:4200/#/doc/license`
  • 写法1 : 绝对路径 / + 路由名字
 <!--跳到 http://localhost:4200/#/doc/license -->
 <a  [routerLink]="['/doc/demo']" >跳呀跳</a>
 
  <!--跳到 http://localhost:4200/#/demo -->
  <a  [routerLink]="['/demo']" >跳呀跳</a>

那么url是
http://localhost:4200/#/doc/demo上跳转,即 http://localhost:4200/#/ + 你要跳转的绝对路径

  • 写法2 : 一个路由名字 路由名字
 <a  [routerLink]="['demo']" >跳呀跳</a>

那么url是http://localhost:4200/#/doc/license/(demo),即http://localhost:4200/#/doc/license + 你要跳转的绝对路径,这时候它会给你的路由加些东西变成 /(demo),跳转不了。

  • 写法3 :相对路径 ../路由名字
 <a  [routerLink]="['../demo']" >跳呀跳</a>

那么url是
http://localhost:4200/#/doc/demo,即 http://localhost:4200/#/doc + 你要跳转的相对路径。它会从上一级开始寻找。

  • 写法4 : ./路由名字, 即现在的路由+你写的要跳去的路由
 <a  [routerLink]="['./demo']" >跳呀跳</a>

那么url是
http://localhost:4200/#/doc/license/demo上,即 http://localhost:4200/#/doc/license + 你要跳转的相对路径。它会从当前路由的下一级开始寻找此匹配的路由进行跳转。

Angular配置文件之环境配置

假设有三个环境:开发环境(dev)、测试环境(test)、生产环境(prod)。
当我们构建参数时会用到 --environment来指定应用执行环境。脚手架会根据指定的值加载对应的环境配置文件。如:ng build --env=dev 就是build开发环境的包。那么我们就从这里开始看看Angular项目里环境该怎么配置。

  1. 首先要找到.angular-cli.json文件的environments关键词进行如下配置:
  "environmentSource": "environments/environment.ts",  // 基础环境配置
  "environments": { // 子环境配置文件
          "dev": "environments/environment.ts",
          "test": "environments/environment.test.ts",
          "prod": "environments/environment.prod.ts"
  }
       
  1. 再进入environments文件夹
    文件目录:
    ├─ environments
    │ ├─ common.json
    │ ├─ environment.ts
    │ ├─ development.json
    │ ├─ environment.test.ts
    │ ├─ test.json
    │ ├─ environment.prod.ts
    │ └─ production.json
  • 这里可以创建一个 common.json文件以存储三套环境公共的配置:
{
  "title": "",
  "question": {
    "url": ""
  },
  "list": {
    "pageSize": 10
  }
}

开发环境 :

  • environment.ts
export const environment = Object.assign({}, require('./common.json'), require('./development.json'), {
  production: false,
  envName: 'dev'
});
  • development.json
{
  "baseUrl": "/",
  "env": "dev",
  "api": {
    "host": "http://localhost:4200"
  }
}

测试环境:

  • environment.test.ts
export const environment = Object.assign({}, require('./common.json'), require('./test.json'), {
  production: false,
  envName: 'test'
});
  • test.json
{
  "baseUrl": "/",
  "env": "test",
  "api": {
    "host": "testurl"
  }
}

生产环境:

  • environment.prod.ts
export const environment = Object.assign({}, require('./common.json'), require('./production.json'), {
  production: true,
  envName: 'prod'
});

  • production.json
{
  "baseUrl": "/",
  "env": "prod",
  "api": {
    "host": "produrl"
  }
}
  1. 在项目的service中相对路径引入环境再请求接口,伪代码如下:
...
@Injectable()
export class LoginService {
  public Login(): Observable<any> {
    return this.http.get("/login").map(
      response => {
        return response.json();
      }
    )
   }
}

(完)

Angular animate()用法

animate : 动画

function animate(
            timings: string | number,
            styles: AnimationStyleMetadata | AnimationKeyframesSequenceMetadata |null = null
          ): AnimationAnimateMetadata;
  • timings : duration delay easing
    它是一个字符串,有三个参数:
    • duration : 动画持续时间
    • delay : 动画延迟几秒后开始
    • easing : 动画的缓和程度
      如果只有一个数字,那么会被认为是 duration 的值。如:
    animate("200 ease-in", ...)
    // 被编译成  duration=200, easing=ease-out
    
    animate("200 1000 ease-in", ...)
    // 被编译成  duration=200, delay=1000, easing=ease-out
    
  • styles:
    它可以是 style(样式) 或 keyframes(关键帧),具体用法看下面详细介绍。
    animate(timings, style({ background: "red" }))
    animate(timings, keyframes([
      style({ background: "blue", offset: 0.2})),  // 20%
      style({ background: "red", offset: 1}))   // 100%
    ])
    

Can't bind to 'formGroup' since it isn't a known property of 'form'

Can't bind to 'formGroup' since it isn't a known property of 'form'

在你用表单的模块得引入ReactiveFormsModule FormsModule即可解决

...
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

@NgModule({
    imports: [
        ...
        FormsModule,
        ReactiveFormsModule
    ]
    ...
})

...

Angular配置文件之.angular-cli.json介绍

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "project": {
    "name": "seczone",
    "ejected": false // 标记该应用是否已经执行过eject命令把webpack配置释放出来
  },
  "apps": [
    {
      "root": "src", // 源码根目录
      "outDir": "dist",  //编译后的输出目录,默认是dist
      "assets": [  // 记录资源文件夹,构建时复制到 outDir 指定的目录
        "assets"
      ],
      "index": "index.html", // 指定的首页文件,默认是 'index.html'
      "main": "main.ts", // 指定应用的入口文件
      "polyfills": "polyfills.ts", // 指定 polyfill 文件
      "test": "test.ts", // 指定测试入口文件
      "tsconfig": "tsconfig.app.json",  // 指定tsconfig文件
      "testTsconfig": "tsconfig.spec.json", //  指定TypeScript单测脚本的tsconfig文件
      "prefix": "", // 使用`ng generate`命令时,自动为selector元数据的值添加的前缀名
      "deployUrl": "", // 指定站点的部署地址,该值最终会赋给webpack的output.publicPath,常用于CDN部署
      "styles": [ // 引入全局样式,构建时会打包进来,常用于第三方库引入的样式
        "styles.scss"
      ],
      "scripts": [ // 引入全局脚本,构建时会打包进来,常用语第三方库引入的脚本
        "../node_modules/tinymce/tinymce.js",
        "../node_modules/tinymce/themes/modern/theme.js",
        "../node_modules/tinymce/plugins/link/plugin.js",
        "../node_modules/tinymce/plugins/paste/plugin.js",
        "../node_modules/tinymce/plugins/table/plugin.js",
        "../node_modules/tinymce/plugins/preview/plugin.js",
        "../node_modules/tinymce/plugins/fullscreen/plugin.js",
        "../node_modules/tinymce/plugins/textcolor/plugin.js",
        "../node_modules/tinymce/plugins/image/plugin.js",
        "../node_modules/tinymce/plugins/imagetools/plugin.js",
        "../node_modules/tinymce/plugins/code/plugin.js"
      ],
      "environmentSource": "environments/environment.ts", // 基础环境配置
      "environments": { // 子环境配置文件
        "dev": "environments/environment.ts",
        "demo": "environments/environment.demo.ts",
        "prod": "environments/environment.prod.ts",
        "test": "environments/environment.test.ts",
        "github": "environments/environment.github.ts"
      }
    }
  ],
  "e2e": {
    "protractor": {
      "config": "./protractor.conf.js"
    }
  },
  "lint": [
    {
      "project": "src/tsconfig.app.json"
    },
    {
      "project": "src/tsconfig.spec.json"
    },
    {
      "project": "e2e/tsconfig.e2e.json"
    }
  ],
  "test": {
    "karma": {
      "config": "./karma.conf.js"
    }
  },
  "defaults": { // 执行`ng generate`命令时的一些默认值
    "styleExt": "scss ", // 默认生成的样式文件后缀名
    "component": {
      "flat": false, // 生成组件时是否新建文件夹包装组件文件,默认为false(即新建文件夹)
      "inlineStyle": false, // 新建时是否使用内联样式,默认为false
      "inlineTemplate": false, // 新建时是否使用内联模板,默认为false
    //  "viewEncapsulation": "Emulated", // 指定生成的组件的元数据viewEncapsulation的默认值
    //  "changeDetection": "OnPush", // 指定生成的组件的元数据changeDetection的默认值
      "spec": false // 是否生成spec文件,默认为true
    }
  }
}

Angular中其实你不需要直接操作DOM

需求: 有一个div,我要给他动态删除和增加样式

<div class='old-style'></div>
.old-style{
    width: 100px;
    height: 100px;
    background: #000;
}

我想给它改变它的样式为

.new-style{
    width: 200px;
    background: #069;
}

法一: 操作DOM,删除和增加class达到效果

获得DOM元素的引用移除旧的class old-style添加class new-style

  1. 给原div加上 #demo,这个可以随意加任何字段只要是和第二步@ViewChild里一样就可以了。
<div class='old-style' #demo></div>
  1. 获取DOM引用并删除和增加class
import { Component, ElementRef, ViewChild, Renderer2 } from '@angular/core';
...
export class DemoComponent implements OnInit {

    constructor( private renderer2 : Renderer2) { }
    
    @ViewChild('demo')  demo: ElementRef; 
    
    ngOnInit(){
        this.renderer2.removeClass(this.demo.nativeElement, "old-style"); 
        this.renderer2.addClass(this.demo.nativeElement, "new-style"); 
    }
    
}

法二: 操作DOM,修改其样式达到效果

获得DOM元素的引用设置新的 style

  1. 给原div加上 #demo
<div class='old-style' #demo></div>
  1. 获取DOM引用并设置新的style
import { Component, ElementRef, ViewChild, Renderer2 } from '@angular/core';
...
export class DemoComponent implements OnInit {

    constructor( private renderer2 : Renderer2) { }
    
    @ViewChild('demo')  demo: ElementRef; 
    
    ngOnInit(){
        this.renderer2.setAttribute(this.demo.nativeElement, "style","width: 200px;background: #006699;"); 
    }
}

这里是修改多个style所以用setAttribute,如果是修改单个style则使用 this.renderer2.setStyle(ele,name,value)

法三: 不操作DOM使用 ngClass修改class

定义一个布尔值,通过布尔值的改变来改变其class

ts:

private changeClass : boolean = false;

// 想要更改样式时直接更改 changeClass的值。

<div [ngClass]="changeClass ? 'new-class' : 'old-class' "></div>

当changeClass为false时 div class为old-class 为true时calss为 new-class

法四: 不操作DOM使用 ngStyle修改style

定义两个变量,通过变量改变来改变其style

ts:

private widthTemp : string = "100px";
private backgroundTemp : string = '#000'; 

// 想要更改样式时直接更改 changeClass的值。
this.widthTemp = '200px;';
this.backgroundTemp = '#069'


html

<div  [ngStyle]="{'background-color':backgroundTemp,'width': widthTemp}">></div>

Angular必知八大概念

Angular必知八大概念

  • 模块
    模块有两层含义:

    1. 框架代码以模块形式组织(物理模块)
    2. 功能单元以模块形式组织(逻辑模块)
      物理模块是TS/ES6提供的文件模块特征
      逻辑模块是对应应用内零散的组件、指令、服务按功能进行分类包装,其关系示意图如下:
      image

    首先,Angular 要能成功运行,至少需要定义一个模块,因为需要有一个模块作为应用启动的入口,这样的模块就称为根模块。
    然后,我们的应用会不断的添加新的功能。这些新增的功能可以封装到一个新的模块里。这些新增加的模块在 angular 里称为特性模块。有了特性模块之后,根模块原来承载在功能逻辑也可以抽离出来,放到某个特性模块里,使根模块保持简洁。
    接下来,我们添加的特性模块越来越多,他们之间可以抽出一些相似功能的组件或指令,这些公共的部分也可以封装成一个独立的模块,这样的模块在逻辑意义上不能称为特性模块,Angular 把他称为为共享模块。
    最后,还有核心模块,我们知道,一个应用里总有一些全局的组件或服务等,他们只需要在应用启动时候初始化一次即可,例如,维护登录信息的服务,或者是,公共的头部和尾部组件等。虽然我们可以把他们放到根模块里,但更好的设计是把这些逻辑也抽离出来,放到一个独立的模块,这个模块即为核心模块。核心模块要求只导入到根模块里,而尽量不要导入到特性模块或者共享模块里,这是为了在协同工作时候避免出现一些不可预料的结果。

    Angular 已经封装了不少常用的模块, 如:

    ApplicationModule:封装一些启动相关的工具;
    CommonModule:封装一些常用的内置指令和内置管道等;
    BrowserModule:封装在浏览器平台运行时的一些工具库,同时将 CommonModule 和 ApplicationModule 打包导出,所以通常在使用时引入 BrowserModule 就可以了;
    FormsModule 和 ReactiveFormsModule:封装表单相关的组件指令等;
    RouterModule:封装路由相关的组件指令等;
    HttpModule:封装网络请求相关的服务等。

    所以,如果你想使用 ngIf 和 ngStyle 等这些内置指令,记得先导入 CommonModule,其他的模块使用方法一致。

  • 组件

    组件由两部分组成的: @component和 类。
    image
    如果只是定义一个类,angular不知道怎么解释这个类,当往这个类里注入组件元数据后,angular才知道把这个类解释为组件。

    如果想了解元数据是如何注入到类里,可深入了解 reflect-metadata 这个 polyfill。

    数据绑定适用于层级相隔不远的组件,层级太深或者不同分支的组件通讯通常采用其他方式,例如利用服务作为中介。

  • 模板
    angular模板基于HTML,普通的html亦可作为模板输入。

@Component({
  template: `<p>deepthan</p>`
})
  • 元数据
    @component是装饰器,元数据主要以装饰器的函数参数指定。
    selector和 template都是元数据。
    装饰器实际上是一个自定义函数,angular的各种装饰器处理函数在modules/@angular/core/src/util/decorators.ts 中。

  • 数据绑定
    属性绑定和数据绑定均称为数据绑定。

    1. 插值
    <p>你好,{{data.name}}</p>
    
    1. 属性绑定
    [deep] = 'data[0]'
    
    1. 事件绑定
    (sendData) = getData(value) 
    
    1. 双向绑定
    <input [(ngModel)='value']>
    

    [()]是实现双向绑定的语法糖,ngmodel是辅助实现双向绑定的内置指令。 input框和value之间形成数据关联, input值发生变更时会自动赋值到 value,而value值被组件类改变时也可更新input的值。

  • 指令
    指令可以通过与dom进行灵活交互,改变样式或改变布局。

    1. 结构指令: 增加、删除、修改DOM,如 ngIf、 ngFor
    <p *ngIf='bol==true'></p>
    
    1. 属性指令 : 改变元素的外观或者行为,如ngStyle 、 ngClass
    html:
    <p> [ngStyle]='setStyles()' </p>
    ts:
    setStyles(){
        return {
            'color' : 'red'
        }
    }
    
  • 服务
    服务是封装单一功能的单元,类似于工具库,常被引用于组件内部,作为组件的功能拓展。它可以是一个简单的字符串或json数据,也可以是一个函数甚至是一个类,几乎所有的对象都可以封装成服务。
    一个简单的日志服务:

    // import statement
    @Injectable()
    export class LoggerService {
      private level: string;
      setLevel(level: string) { 
        this.level = level;
      }
      
      debug(msg: string) { }
      warn(msg: string) {  }
      error(msg: string) { }
    }
    
  • 依赖注入
    通过依赖注入机制,服务等模块可以被引入到任何一个组件(或模块或服务)中,而开发者无须关系这些模块是如何被初始化的。
    可以说依赖注入是一种帮助开发者管理模块依赖的设计模式。
    一个依赖注入例子:

    import {LoggerService} from './logger-service';
    // other import statement
    @Component({
      selector: 'contact',
      template: '...'
      providers: [LoggerService]
    })
    export class ContactListComponent {
      constructor(logger: LoggerService) { 
        logger.debug('xxx');
      }
    }
    

    @component 装饰器中的 providers元元数据是依赖注入操作的关键,它会为该组件创建一个注入器对象,并新建 LoggerService实例存储到这个注入器里。组件需要引入 LoggerService实例时,只需要在构造函数声明 LoggerService类型的参数即可,Angular自动地通过类型匹配,找出注入器里预先实例化好的 loggerService对象,在组件实例化时作为参数传入,这样组件便获得了 LoggerService的实例引用。

  • 其他概念:

Angular 是以适当的时机去检验对象的值是否被改动,这个适当的时机并不是以固定某个频率去执行,而通常是在用户操作事件(如点击),setTimeout 或 XHR 回调等这些异步事件触发之后。Angular 捕获这些异步事件的工作是通过 Zones 库实现的.

import { LoginModule } from './login/login.module'
const routes: Route: [
  { 
    path: 'role', 
    children: [
      { path: "login", loadChildren: () => LoginModule }
    ]
  }
]
  1. 子路由下面有个特性模块,懒加载:
const routes: Route: [
  { 
    path: 'role', 
    children: [
      { path: "login", () => import('./login/login.module').then((m) => m.LoginModule)}
    ]
  }
]

报错处理之 -- 关于function 、lambda、not supported等

  • 错误信息

ERROR in Error: Error encountered resolving symbol values statically. Function calls are not supported. Consider replacing the function or lambda with a reference to an exported function (position 194:50 in
 the original .ts file), resolving symbol NgModule in D:/plateform/project-test/node_modules/@angular/platform-browser/node_modules/@angular/core/core.d.ts, resolving symbol BrowserModule in D:/plateform/pr
oject-test/node_modules/@angular/platform-browser/platform-browser.d.ts, resolving symbol BrowserModule in D:/plateform/project-test/node_modules/@angular/platform-browser/platform-browser.d.ts
    at positionalError (D:\plateform\project-test\node_modules\.4.4.3@@angular\compiler\bundles\compiler.umd.js:25266:35)
    at simplifyInContext (D:\plateform\project-test\node_modules\.4.4.3@@angular\compiler\bundles\compiler.umd.js:25109:27)
    at StaticReflector.simplify (D:\plateform\project-test\node_modules\.4.4.3@@angular\compiler\bundles\compiler.umd.js:25123:13)
    at StaticReflector.annotations (D:\plateform\project-test\node_modules\.4.4.3@@angular\compiler\bundles\compiler.umd.js:24553:41)
    at _getNgModuleMetadata (D:\plateform\project-test\node_modules\.4.4.3@@angular\compiler-cli\src\ngtools_impl.js:138:31)
    at _extractLazyRoutesFromStaticModule (D:\plateform\project-test\node_modules\.4.4.3@@angular\compiler-cli\src\ngtools_impl.js:109:26)
    at D:\plateform\project-test\node_modules\.4.4.3@@angular\compiler-cli\src\ngtools_impl.js:129:27
    at Array.reduce (native)
    at _extractLazyRoutesFromStaticModule (D:\plateform\project-test\node_modules\.4.4.3@@angular\compiler-cli\src\ngtools_impl.js:128:10)
    at Object.listLazyRoutesOfModule (D:\plateform\project-test\node_modules\.4.4.3@@angular\compiler-cli\src\ngtools_impl.js:53:22)
    at Function.NgTools_InternalApi_NG_2.listLazyRoutes (D:\plateform\project-test\node_modules\.4.4.3@@angular\compiler-cli\src\ngtools_api.js:91:39)
    at AotPlugin._getLazyRoutesFromNgtools (D:\plateform\project-test\node_modules\.1.7.1@@ngtools\webpack\src\plugin.js:207:44)
    at _donePromise.Promise.resolve.then.then.then.then.then (D:\plateform\project-test\node_modules\.1.7.1@@ngtools\webpack\src\plugin.js:443:24)
    at process._tickCallback (internal/process/next_tick.js:109:7)
  • 解决办法:

它是typescript的依赖关系问题,在 tsconfig.json文件中中加

"paths": {
      "@angular/common": ["../node_modules/@angular/common"],
      "@angular/compiler": ["../node_modules/@angular/compiler"],
      "@angular/core": ["../node_modules/@angular/core"],
      "@angular/forms": ["../node_modules/@angular/forms"],
      "@angular/platform-browser": ["../node_modules/@angular/platform-browser"],
      "@angular/platform-browser-dynamic": ["../node_modules/@angular/platform-browser-dynamic"],
      "@angular/router": ["../node_modules/@angular/router"],
      "@angular/http": ["../node_modules/@angular/http"]
    }

加后形如这样:

{
  "compilerOptions": {
    "baseUrl": "",
    "declaration": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": ["es6", "dom"],
    "mapRoot": "./",
    "module": "es6",
    "moduleResolution": "node",
    "outDir": "../dist/out-tsc",
    "sourceMap": true,
    "target": "es5",
    "typeRoots": [
      "../node_modules/@types"
    ],
    "paths": {
      "@angular/common": ["../node_modules/@angular/common"],
      "@angular/compiler": ["../node_modules/@angular/compiler"],
      "@angular/core": ["../node_modules/@angular/core"],
      "@angular/forms": ["../node_modules/@angular/forms"],
      "@angular/platform-browser": ["../node_modules/@angular/platform-browser"],
      "@angular/platform-browser-dynamic": ["../node_modules/@angular/platform-browser-dynamic"],
      "@angular/router": ["../node_modules/@angular/router"],
      "@angular/http": ["../node_modules/@angular/http"]
    }
  }
}

报错处理之 -- 关于node-sass里面 vendor 缺失

ERROR in ./node_modules/css-loader?{"sourceMap":false,"importLoaders":1}!./node_modules/postcss-loader?{"ident":"postcss"}!./node_modules/@angular/cli/node_modules/sass-loader/lib/loader.js?{
"sourceMap":false,"precision":8,"includePaths":[]}!./src/styles.scss
Module build failed: Error: ENOENT: no such file or directory, scandir 'D:\个人\gitee-poetry\node_modules\@angular\cli\node_modules\node-sass\vendor'
    at Object.fs.readdirSync (fs.js:913:18)
    at Object.getInstalledBinaries (D:\个人\gitee-poetry\node_modules\@angular\cli\node_modules\node-sass\lib\extensions.js:128:13)
    at foundBinariesList (D:\个人\gitee-poetry\node_modules\@angular\cli\node_modules\node-sass\lib\errors.js:20:15)
    at foundBinaries (D:\个人\gitee-poetry\node_modules\@angular\cli\node_modules\node-sass\lib\errors.js:15:5)
    at Object.module.exports.missingBinary (D:\个人\gitee-poetry\node_modules\@angular\cli\node_modules\node-sass\lib\errors.js:45:5)
    at module.exports (D:\个人\gitee-poetry\node_modules\@angular\cli\node_modules\node-sass\lib\binding.js:15:30)
    at Object.<anonymous> (D:\个人\gitee-poetry\node_modules\@angular\cli\node_modules\node-sass\lib\index.js:14:35)
    at Module._compile (module.js:571:32)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (D:\个人\gitee-poetry\node_modules\@angular\cli\node_modules\sass-loader\lib\loader.js:3:14)
    at Module._compile (module.js:571:32)
 @ ./src/styles.scss 4:14-213
 @ multi ./src/styles.scss
  • 法一 : 找到缺失的文件直接下载

首先看开头的错误信息,

  1. ERROR in...在什么地方报错了,
  2. Module build failed后面跟的是模块打包错误原因及地址,
  3. no such file or directory, scandir 'D:\个人\gitee-poetry\node_modules\@angular\cli\node_modules\node-sass\vendor',在D:\个人\gitee-poetry\node_modules\@angular\cli\node_modules\node-sass\vendor'中vendor文件不存在。

那么我们找到这个文件位置,从node-sass官网下载一个vendor文件放进去即可。

  • 法二 : 重新构建 node-sass
npm rebuild node-sass

或者删除 node_modules 重新下载

package.json里‘’^ ~“符号的意思

符号 依赖 版本 更新内容 解释
插入符号(^) ^3.9.2 3.. 1.向后兼容的新功能2.旧的功能被弃用,但是可以操作3.大的重构4.bug修改 主版本是固定的,匹配任何次要版本,匹配任何版本号。
波浪符号(~) ~3.9.2 3.9.* 修改bug 主版本是固定的,小版本是固定的,匹配任何版本号

Angular proxy 配置

Angular proxy 配置

本文将介绍如何基于脚手架配置 Angular 代理

为何做?

写一个代理文件,将匹配的请求代理到其他地址,解决本地开发跨域问题。

如何配置?

  • 在根目录中创建一个proxy.config.js
  • 可以在这个文件中做如下配置
  • 在 package.json的运行项目命令中加入 --proxy-config proxy.config.js

配置介绍

const PROXY_CONFIG = [
  {
    context: ['/api'],
    target: 'http://xxx',
    secure: false,
    changeOrigin: true,
    pathRewrite: {
      '^/api': '',
    },
  },
];
module.exports = PROXY_CONFIG;
  • context: 需要匹配的path
  • target: 代理到的地址
  • pathRewrite: 将请求的部分path重写,它是一个对象,键是^+要重写的path, 值是替换的path。
  • secure: 安全设置
  • changeOrigin: 改变源

配置实例


http://localhost:4208/auth/login

想要代理到

http://www.baidu.com/news/login

可以这样配置

const PROXY_CONFIG = [
  {
    context: ['/auth/login'],
    target: 'http://www.baidu.com',
    pathRewrite: {
        '^/auth/login': '/news/login',
    },
  },
  
]

module.exports = PROXY_CONFIG;

Q: 如果有两个接口,一个api/v1/1,另外一个api/v1/cer/login,我该如何将两个接口代理到不同的地址?

{
    context: ['api/v1/cer/login'],
    target: 'xxx1',
    secure: false,
    changeOrigin: true,
},
{
    context: ['/api'],
    target: 'xxx2',
    secure: false,
},

使用/api,只要是匹配到这个的都会走它的代理,不过如果在它前面加了个更加精确的api/v1/cer/login,会匹配到它,走这个代理。

production环境中去除console信息

  • 去除原因:
    发布到生产环境可能会有console函数忘记注释,这里面可能会包含敏感数据导致不安全,所以需要在全局进行禁止打印信息。

  • 去除方法:
    在入口文件main.ts里面加个判断,把打印函数重写。

if (environment.production) {
  window.console.log = function (){};
  window.console.info = function (){};   
  window.console.warn = function (){};   
  window.console.error = function (){};   
  window.console.debug = function (){};  
}

vscode插件

1. *Angular Files

在项目里面快速创建文件

2. *Angular 8 Snippets

为typescript和HTML添加了一些代码片段。

3. Auto Import

自动导入包和组件等。

4. JavaScript(ES6) code snippets , by charalampos karypidis,

提供Javascript的Snippet, 花一点时间记住一些片段,之后可以省下很多时间。

5. Git History

查看git 提交历史

6. GitLens — Git supercharged

查看代码里面提示每段代码是谁什么时候提交修改的

7. npm

集成npm

8. npm Intellisense

自动补全npm下载的包

9. *Path Intellisense

输入路径会自动提供输入建议或自动完成功能

10 View In Browser

在vs中打开浏览器

11 Bracket Pair Colorizer

对括号进行着色

12. Markdown PDF

将markdowm文件转换为pdf

13. *CSS Peek

html中点击css名字跳转到定义的位置

14. *Prettier

格式化css和js代码

15. Color Info

只需在css颜色上悬停光标,就可以预览色块中色彩模型的(HEX、 RGB、HSL 和 CMYK)相关信息了

16. angular support

可以在html里面查看ts中的变量、函数等

17. *koroFileHeader

顶部注释模板,可定义作者、时间等信息,并会自动更新最后修改时间

18. Debugger for Chrome

让 vscode 映射 chrome 的 debug功能,静态页面都可以用 vscode 来打断点调试

19. Chinese (Simplified) Language Pack for Visual Studio Code

编辑器中文包

Angular热替换(cli & webpack)

webpack体系原生支持热替换功能,设置也非常简单,只须在webpack-dev-server命令下添加--hot参数:

webpack-dev-server --port=4200 --hot

加上这个hot参数会添加 HotModuleReplacementPlugin 插件到webapck配置里,并启动热替换功能。

如果你用的是 Angular-CLI 搭建,则添加 --hmr 即可开发服务器端的热替换功能:

ng serve --hmr

但是仅仅在开发服务器端开启热替换还不够,Angular 项目仍需要添加热替换处理逻辑,修改 Angular 的启动命令如下如下:

declare var module: any;
if(module.hot) { // 如果webpack启用了热替换功能
  // 接受模块更新的事件,同时阻止这个事件继续冒泡
  // 参考这文章:https://github.com/webpack/docs/wiki/hot-module-replacement-with-webpack
  module.hot.accept();
}
platformBrowserDynamic().bootstrapModule(AppModule)

这时候修改组件代码后,页面不再刷新了,而是把整颗组件树重新渲染,替换DOM节点。

但这样随之而来的一个问题是,在组件里保存的一些数据状态也会丢失,这样的体验显然不好。一个解决办法将组件的数据状态抽取出来外界管理,在热更新渲染前保存数据,渲染后还原数据,示例代码如下:

if(module.hot) {
	module.hot.accept();
	restoreState(module.hot.data.state); // 还原数据状态
	// 模块销毁前触发
	module.hot.dispose(() => {
		module.hot.data.state = getState(); // 保存数据状态
	});
}

webpack的热更新功能打开后,module.hot.data 是一个对象,可以用来在旧模块和新模块之间传递数据

这种有点类似于Redux的做法,Angular世界里其实也有这样工具库,如ngrx。
还有一个问题就是,重新渲染的过程中,一些老的样式不会被清理,导致header里的style标签堆积很多,所以在在重新渲染之前可先清理原来的style标签,示例代码如下:

if(module.hot) {
	module.hot.accept();
	module.hot.dispose(() => {
		let styles = document.head.querySelectorAll('style');
		for(let i = 0, len = styles.length; i++; i < len) {
			if(styles[i].innerText.indexOf('_ng') >= 0) {
				styles[i].remove();
				styles[i] = null;
			}
		}
	});
}

热替换功能不依赖ngrx。
热替换后的组件内数据会丢失,用ngrx来管理组件数据可以避免这个问题。

Angular query()用法

query: 选取元素,并添加动画

function query(
            selector: string, 
            animation: AnimationMetadata | AnimationMetadata[], 
            options: AnimationQueryOptions | null = null
         ): AnimationQueryMetadata;
  • selector : 要选取的元素,选取方式和原生的一样。
  • animation : 要进行的动画序列,一个或多个。
作用:

在处于动画序列的元素内部查找一个或多个元素,这些元素也会被加入当前动画序列中,不过一般会重新写一个数组来重新定义选取元素的动画序列。

用法:

1) 选取元素并可以限制数量
query()函数源码中使用了element.querySelectorAll因此他可以选取多个元素,所以我们在选取元素的时候可以加上一个 limit 来限制选取的数量。

// 在class为 demo的div里找一个div,找到的是 demo1,如果 limit为2 的话找到的是 [demo1, demo2]。
template: `
  <div [@queryDemo] class='demo'>
    <div class='demo1'></div>
    <div class='demo2'></div>
  </div>
`,
animations: [
   trigger('queryDemo', [
     transition('void => *', [
          query( 'div', animate(...), {limit: 1} )
     ])
   ]
]

2) 开/ 关报错功能
默认情况下如果选取的元素找不到则 query()函数会报错,设置optional选项为 true 则或忽略错误。

query('.demo-not-be-there', [
  animate(...),
  animate(...)
], { optional: true })
选择器的特殊值

query()函数里面用伪选择器可以选出特定的元素:

  • query(":enter")/query(":leave") : 选取新插入 / 移除的元素
  • query(":animating") : 选取所有正在进行动画的元素
  • query("@triggerName") : 选取有特定触发器的元素
  • query("@*") : 选取所有具有触发器的元素
  • query(":self") : 把当前元素增加到动画序列中

多个伪选择器可以合在一起组成选择器查询字符串:

query(':self, .record:enter, .record:leave, @subTrigger', [...])
demo
@Component({
  selector: 'inner',
  template: `
    <div [@queryAnimation]="exp">
      <h1>Title</h1>
      <div class="content">
        Blah blah blah
      </div>
    </div>
  `,
  animations: [
   trigger('queryAnimation', [
     transition('* => goAnimate', [
       // 隐藏里面的元素
       query('h1', style({ opacity: 0 })),
       query('.content', style({ opacity: 0 })),
 
       // 一个一个地执行里面元素的动画
       query('h1', animate(1000, style({ opacity: 1 })),
       query('.content', animate(1000, style({ opacity: 1 })),
     ])
   ])
 ]
})
class Cmp {
  exp = '';
 
  goAnimate() {
    this.exp = 'goAnimate';
  }
}

Angular style()用法

style : 样式设置

function style(
            tokens: '*' |
            {[key: string]: string | number} |
            Array<'*'|{[key: string]: string | number}>
         ): AnimationStyleMetadata;

style声明一个包含CSS属性/样式的键值对。

style({ width: 100, height: 0 })

Auto-styles(自适应样式): style值可以用 '*' 来表示,自动达到其原本的样式,举个例子你就明白作用了:

如果一个div它实际宽度是100px,高度为100px,让它高度从0 到100px变化

<div class='demo'></div>
...
.demo{
    width: 100px;
    height: 100px;
}

这时候用 '*'来写

 animations: [trigger(
      'autoHeight',
      [
        state('void', style({height: '0px'})),
        state('*', style({height: '*'})),
        transition('void => *',  animate(500))
      ])],

它就会在 500ms内 高度从 0 搭配100px。咦,似乎没感觉到什么作用...
在高度为动态获取的值时候就看到其强大了:data为动态获取的{ height: xxx }

<div class='demo' [ngStyle]="{'height':data.height}">
</div>
...
.demo{
    width: 100px;
}

 animations: [trigger(
      'autoHeight',
      [
        state('void', style({height: '0px'})),
        state('*', style({height: '*'})),
        transition('void => *',  animate(500))
      ])],

这样在 500ms 高度自动到达设定的值。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.