zoukankan      html  css  js  c++  java
  • 迈向angularjs2系列(2):angular2指令详解

    目录

    1.hello world!

    2.配置开发环境

    源代码下载

    链接: https://pan.baidu.com/s/1i5pGloT 密码: g7ub

    一:helloworld!

    注意:这一小节的内容,并非生产环境的做法,读者可以不必操作,看演示就好了。

    为了简单快速的运行ng2程序,那么引入直接angular2版本和页面的基本框架。

    index.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
         <app></app>
         <!--使用app组件-->
         <script src="https://code.angularjs.org/2.0.0-beta.9/angular2-polyfills.min.js">
    
         </script>
         <script src="https://code.angularjs.org/2.0.0-beta.9/Rx.umd.min.js">
    
         </script>
         <script src="https://code.angularjs.org/2.0.0-beta.9/angular2-all.umd.min.js">
    
         </script>
         <script src="./app.js"></script>
    
    </body>
    </html>

    app.js:

    var App=ng.core.Component({
        //定义了名称为App的组件。
        selector:"app",
        //匹配所有的app标签
        template:"<h1>hello  {{target}}</h1>"
    })
    .Class({
        //Class函数传递了一个对象字面量,只有constuctor方法
        constructor:function(){
            this.target="world";
        }
    });
    ng.platform.browser.bootstrap(App);
    //ng.platform.browser是命名空间

    直接打开index.html,那么html显示为:

    结论:并没有用到typescript,所以它不是必须的,但ng2强烈推荐使用。

    二: 配置开发环境和angular2的typescript实现

    1.通过git克隆项目

    step1:安装git

    到网站下载exe文件,https://github.com/git-for-windows/git/releases/download/v2.13.2.windows.1/Git-2.13.2-64-bit.exe。安装的过程我只是改了一下安装目录。

    安装目录:

    检测git是否安装正确:

    首先在开始菜单里找到git cmd(也可以吧快捷方式发送到桌面),然后输出命令git --version。

    step2:进入自己的项目目录,运行命令 git clone https://github.com/mgechev/switching-to-angular2.git 。

    已经克隆下来。good!

    step3:进入项目目录,模块安装,然后启动Server。

    $ npm install ;
    $ npm start;//启动server

    浏览器显示结果为:

    success

    step4:上手试玩(optional)

    内容替换,switching-to-angular2/app/ch4/ts/hello-world/app.ts:

    import {Component} from '@angular/core';
    import {bootstrap} from '@angular/platform-browser-dynamic';
    
    @Component({
      selector: 'app',
      templateUrl: './app.html'
    })
    class App {
      target:string;
      constructor() {
        this.target = 'world';
      }
    }
    
    bootstrap(App);

    浏览器显示结果为:

    2.hello-world深度解析

    注意:代码位于switching-to-angular2/app/ch4/ts/hello-world目录)

    首先,看一下一共有4个文件。

    index.html负责hello-world的首页(默认)文件的显示,app.html负责app模板的内容,meta.json是一些元信息,app.ts负责js代码逻辑。

    接下来详细看index.html。

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <title><%= TITLE %></title>
      <!--TITLE变量注入-->
      <meta name="description" content="">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <!-- inject:css -->
      <!-- endinject -->
    </head>
    <body>
    
      <app>Loading...</app>
      <!--app组件-->
      <!-- inject:js -->
      <!-- endinject -->
      <%= INIT %>
      <!--INIT是变量注入-->
    </body>
    </html>

    app标签有文本内容,"Loading"一直处于可见状态,直到应用启动好、主组件渲染完毕为止。而 <%= TITLE %> 和 <%= INIT %> 是用来注入变量的。这些变量是在哪里定义的呢?

     ,传授一个全局搜索文本的方法:

    step1:文件目录右键,选择find in path

    step2:搜索"TITLE"。

    所以,它的定义在switching-to-angular2/tools/tasks的build.index.ts文件中。

    build.index.ts:

    import {join, sep} from 'path';
    import {APP_SRC, APP_DEST, DEPENDENCIES, SYSTEM_CONFIG, ENV} from '../config';
    import {transformPath, templateLocals} from '../utils';
    
    export = function buildIndexDev(gulp, plugins) {
      return function () {
        return gulp.src(join(APP_SRC, '**', 'index.html'))
          // NOTE: There might be a way to pipe in loop.
          .pipe(inject())
          .pipe(plugins.template(
            require('merge')(templateLocals(), {
              TITLE: 'Switching to Angular 2',
              INIT: `
    <script>
      System.config(${JSON.stringify(SYSTEM_CONFIG)});
      System.import("./app")
        .catch(function () {
          console.log("Report this error to https://github.com/mgechev/switching-to-angular2/issues", e);
        });
    </script>`
            })
          ))
          .pipe(gulp.dest(APP_DEST));
      };
    
    
      function inject() {
        return plugins.inject(gulp.src(getInjectablesDependenciesRef(), { read: false }), {
          transform: transformPath(plugins, 'dev')
        });
      }
    
      function getInjectablesDependenciesRef() {
        let shims = DEPENDENCIES.filter(dep => dep['inject'] && dep['inject'] === 'shims');
        let libs = DEPENDENCIES.filter(dep => dep['inject'] && dep['inject'] === 'libs');
        let all = DEPENDENCIES.filter(dep => dep['inject'] && dep['inject'] === true);
        return shims.concat(libs).concat(all).map(mapPath);
      }
    
      function mapPath(dep) {
        let prodPath = join(dep.dest, dep.src.split(sep).pop());
        return ('prod' === ENV ? prodPath : dep.src );
      }
    };
    build.index.ts

    第三,分析一下ch4/ts/hello-world/app.ts。

    import {Component} from '@angular/core';
    //导入了component装饰器
    import {bootstrap} from '@angular/platform-browser-dynamic';
    //导入了bootstrap函数
    @Component({
      //Component装饰了class App
      selector: 'app',
      templateUrl: './app.html'
      //对象字面量参数和ES5类似,app选择器和视图内容。模板既可以template内联,也可以使用url,和ng1类似。
    })
    class App {
      target:string;
      constructor() {
        this.target = 'world';
      }
    }
    bootstrap(App);
    //启动APP

    四: angular2指令详解

    开发app组件现在开始了!

    1.基础to-do list应用

    (1)运行代码:

    进入switchingToNG2/switching-to-angular2目录,运行npm start,那么打开浏览器,进入红色框的链接。

     

    (2)进入switching-to-angular2/app/ch4/ts/ng-for/detailed-syntax目录,一共4个部分。

    app.ts:

    import {Component} from '@angular/core';
    import {bootstrap} from '@angular/platform-browser-dynamic';
    //导入
    @Component({
      //装饰器
      selector: 'app',
      templateUrl: './app.html',
    })
    class App {
      todos:string[];
      name:string;
      constructor() {
        //app类的属性定义,可以直接在app组件的代码里使用
        this.name = "爱莎";
        this.todos = ['呼风唤雪', "保护妹妹"];
      }
    }
    
    bootstrap(App);
    //启动应用

    app.html:

    <h1>你好,{{name}}!</h1>
    <h3>
      to-do清单:
    </h3>
    <ul>
      <template ngFor let-todo [ngForOf]="todos">
        <li>{{todo}}</li>
      </template>
    </ul>

    说明:template标签内部可以放HTML片段。template的作用是屏蔽了浏览器的直接渲染,交给模板引擎来处理。

    index.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <title><%= TITLE %></title>
      <meta name="description" content="">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <!-- inject:css -->
      <!-- endinject -->
    </head>
    <body>
    
      <app>Loading...</app>
      <!-- inject:js -->
      <!-- endinject -->
      <%= INIT %>
    </body>
    </html>
    默认页面类似之前DEMO的内容

    meta.json:

    {
      "title": "List of items (detailed syntax)",
      "description": "List of items using explicit template for ng-for",
      "id": 3,
      "presented": true
    }

    这里的title在index.html中有使用到。

    (3)结果显示。

    打开http://localhost:5555/dist/dev/ch4/ts/ng-for/detailed-syntax/地址,显示如下:

    2. 更加优秀的ngFor指令

     ngFor用来遍历数据,和ng1的ng-repeat类似,也更优秀。

    <template ngFor let-todo [ngForOf]="todos">
        <li>{{todo}}</li>
      </template>

    ng1的指令用法五花八门,需要对各种属性值深入去理解。而ng2引入了更加简单的约定,语义性更高。

    属性有3种用法:

    ●propertyName="value"

    第一种语法:普通字面量

    propertyName接收字符串字面量,要使用引号哦。angular不会对它进行进一步处理。

    ●[propertyName]="expression"

    第二种语法:方括号语法

    提示angular2要当做表达式来处理。属性包围在方括号里面,angular就会尝试执行这个表达式,执行上下文就是模板对应的组件。

    ●(eventName)="handlerFunc()"

    第三种语法:小括号语法

    当然是事件绑定咯。

    :) 可以看到,谁是谁,非常清晰。

    (1)模板如何使用ngFor指令

     <template ngFor let-todo [ngForOf]="todos"> [ngForOf]是遍历的数据集合,let-todo(这里是var-变量名语法)告诉ng2遍历创建的新变量名字叫todo,类型是let。

    (2)使用语法糖

    语法糖,简单的说就是更方便更简洁的写法。

    使用星号,扔掉template标签 ,指令也直接用到容器标签上。

    <ul>
      <li *ngFor="#todo of todos">{{todo}}</li>
    </ul>

    angular2会自动对上面代码进行“脱糖”处理,就能变成上面比较啰嗦的形式了。

    3.自定义angular2指令

     上一节讲了如何在DOM标签上使用内置指令,这一节讲自定义指令。自定义才是高级玩法。

    本小节将构建一个简单的tooltip自定义指令。

    (1)运行代码:

    进入switchingToNG2/switching-to-angular2目录,运行npm start,那么打开浏览器,进入红色框的链接。

    (2)进入目录switching-to-angular2/app/ch4/ts/tooltip,                                                                                                                                                                                                      

    app.html:

    <div saTooltip="Hello world!">
    </div>

    ch4/ts/tooltip/app.ts:

    一共分为导入、Overlay类、指令、常规的APP类定义。

    //导入
    import {HostListener, Input, Injectable, ElementRef, Inject, Directive, Component} from '@angular/core';
    
    import {bootstrap} from '@angular/platform-browser-dynamic';
    
    //Overlay类的定义
    class Overlay {
      private el: HTMLElement;
      constructor() {
        var el = document.createElement('div');
        el.className = 'tooltip';
        this.el = el;
      }
      close() {
        this.el.hidden = true;
      }
      open(el, text) {
        this.el.innerHTML = text;
        this.el.hidden = false;
        var rect = el.nativeElement.getBoundingClientRect();
        this.el.style.left = rect.left + 'px';
        this.el.style.top = rect.top + 'px';
      }
      attach(target) {
        target.appendChild(this.el);
      }
      detach() {
        this.el.parentNode.removeChild(this.el);
      }
    }
    //指令定义
    @Directive({
      selector: '[saTooltip]'
    })
    export class Tooltip {
      @Input()
      saTooltip:string;
    
      constructor(private el: ElementRef, private overlay: Overlay) {
        this.overlay.attach(el.nativeElement);
      }
      @HostListener('mouseenter')
      onMouseEnter() {
        this.overlay.open(this.el, this.saTooltip);
      }
      @HostListener('mouseleave')
      onMouseLeave() {
        this.overlay.close();
      }
    }
    //APP类定义,并被component装饰器修饰以及最后的启动
    @Component({
      selector: 'app',
      templateUrl: './app.html',
      providers: [Overlay],
      directives: [Tooltip]
    })
      
    class App {}
    
    bootstrap(App);

    (3)结果显示。

    打开http://localhost:5555/dist/dev/ch4/ts/tooltip/地址,那么会有如下结果:

    (2)导入的类HostListener、Directive、ElementRef

    app.ts导入了HostListener, Input, Injectable, ElementRef, Inject, Directive, Component这些类。

    ●HostListener(eventname)

    用于事件处理。指令实例化的时候,angular2会把这个装饰过的方法当成宿主标签上对应eventname的事件处理函数。

    @HostListener('mouseenter')
      //定义监听器
      onMouseEnter() {
        this.overlay.open(this.el, this.saTooltip);
      }
      //定义监听函数

    ●Directive

    用于定义指令。用来添加额外的元数据。

    //指令定义
    @Directive({
      selector: '[saTooltip]'
      //大小写敏感哦
    })

    ●ElementRef

    在宿主标签里注入其他标签的引用,不仅仅是DOM。比如这里的模板就是angular包装过的div标签,里面有tooltip属性。

    <div saTooltip="Hello world!">
    </div>

    (3)指令输入的input装饰器

    接收的参数是需要绑定的属性名。如果不传递参数,,默认绑定到同名属性上。

    注意:angular的HTML编译器对大小写敏感。

     我们来看一下input的构造器到底干了什么?

    @Input()
        //给指令定义一个saTooltip属性,属性的值是表达式。
      saTooltip:string;

    (4)constructor构造器的理解

    constructor(private el: ElementRef, private overlay: Overlay) {
        //overloay是定义的overlay类
        //我们来看一下ElementRef究竟是什么
        console.log(el);
        this.overlay.attach(el.nativeElement);
      }

    ●私有属性el

    首先,看一下el,也就是ElementRef是什么。根据打印结果,ElementRef就是app.html模板里包装好的div元素。

    再来看attach。这行代码负责刚刚的原生div元素添加内容。

    attach(target) {
        target.appendChild(this.el);
        //target是原生的div,给它添加子元素,内容是overloay定义的this.el
      }

    ●私有属性overlay

    overlay的类型为Overlay,也就是定义的class类。Overlay类实现了维护tooltip组件外观的逻辑,并可以用angular的DI依赖注入,当然要加上顶层组件Component的定义。

    ch4/ts/tooltip/app.ts:

    @Component({
      selector: 'app',
      templateUrl: './app.html',
      providers: [Overlay],//数组哦,实现Overlay的依赖注入
      directives: [Tooltip]
    })

    (5)封装指令要在顶层组件声明。

    注意:声明的是数组哦。整个组件的内部全部指令都会声明在这里。尽管显式声明有些麻烦,但能带来更好的封装性。而在ng1种所有指令都在全局命名空间中,坏处是容易命名冲突。同时我们知道了组件用了哪些指令,更好理解了。

    @Component({
    //component装饰器,传递对象字面量作为参数
      selector: 'app',
      templateUrl: './app.html',
      providers: [Overlay],
      directives: [Tooltip]//声明指令数组,angular编译器就可以发现tooltip指令了
    })
    
    class App {} 

    五: 其他,重命名指令的输入输出、语法糖的本来面目

    先回顾语法糖怎么书写的:

    @Directive(...)
    class Dir{
      @Output()outputNmae=new EventEmitter();
      @Input() inputName;
    }
    //最佳实践推荐的语法糖方式,更容易阅读和理解

    本来面目是:

    @Directive({
    outputs:['outputName:XXXX'],
    inputs:['inputs:XXXX']
    })
    class Dir{
    outputName=new EventEmitter();
    }
  • 相关阅读:
    Zabbix监控mysql配置及故障告警配置
    Tesseract-OCR 字符识别---样本训练
    Wex5案例使用JSON传输Thinkphp后端对接,以达成数据正常输出
    Linux内核分析:recv、recvfrom、recvmsg函数实现
    libevent源码分析:evmap_io_active_函数
    libevent源码分析:epoll后端实现
    监听套接字不可写?
    Linux内核分析:dup、dup2的实现
    Linux内核分析:打开文件描述符实现
    libevent源码分析:http-server例子
  • 原文地址:https://www.cnblogs.com/chenmeng2062/p/7095935.html
Copyright © 2011-2022 走看看