基于angular中的重要指令详解($eval,$parse和$compile)

在angular的服务中,有一些服务你不得不去了解,因为他可以说是ng的核心,而今天,我要介绍的就是ng的两个核心服务,$parse和$compile。其实这两个服务讲的人已经很多了,但是100个读者就有100个哈姆雷特,我在这里讲讲自己对于他们两个服务的理解。

大家可能会疑问,$eval呢,其实他并不是一个服务,他是scope里面的一个方法,并不能算服务,而且它也基于parse的,所以只能算是$parse的另一种写法而已,我们看一下ng源码中$eval的定义是怎样的就知道了  

$eval: function(expr, locals) {
    return $parse(expr)(this, locals);
   },

相信看完源码大家就明白了吧,好了,现在就开始两种核心服务的讲解了,如果感觉我说的不对的话,欢迎在评论区或者私聊指出,免得祸害其他读者。

再讲这两个服务的时候,我要先讲一个在本贴的概念:上下文

我相信,很多人都听过这个“上下文”,但是可能有点模糊,在我这里给大家解释解释看看大家接不接受这个说法。

还记得angular的数据绑定吗?比如:我现在有个有个叫TestCtrl的控制器,他的内容如下:

.controller('TestCtrl', function($scope) {
      $scope.test = "Boo!!!"
  })

而在html中我们的代码是这样的

<body ng-controller="TestCtrl">
  {{test}}
</body>

那么,大家不用想都知道结果了,页面上肯定会显示 Boo!!!的字样。

但是如果我删掉ng-controller的指令呢?也就是我没有在html申明控制器,你直接绑定{{test}}会如何呢?

结果只有一个,那就是页面啥都没有(ps:因为你申明了ng-app)。讲到这里大家明白了吗?

控制器就相当于一个上下文的容器,真正的上下文其实是$scope,当页面绑定test,如果申明了控制器,当前上下文就是控制器里面的$scope,ng会去找一下你这个控制器的上下文$scope有没有test,如果有,他当然就显示出来了,但是你不申明控制器的时候呢?他的上下文容器就是ng-app了,那么他真正的上下文就是$rootScope,这个时候他就会寻找$rootScope有没有test。

好了,上下文的概念已经讲完了,其实挺容易理解的,基本上和this非常相似

那么言归正传,我们开始讲$parse,首先我们要看的是ng的API文档

var getter = $parse('user.name');
var setter = getter.assign;
var context = {user:{name:'angular'}};
var locals = {user:{name:'local'}};

expect(getter(context)).toEqual('angular');
setter(context, 'newValue');
expect(context.user.name).toEqual('newValue');
expect(getter(context, locals)).toEqual('local');

大家看到的是ng文档里面对于$parse服务性价比最高的几行代码,

getter和setter就是大家所熟知的get方法和set方法了,context和locals仅仅是json对象而已,目的就是模拟上下文关系

大家看到的下面四个语句最终都能通过测试,现在我们一个个来分析,分析之前我要解释一遍什么叫$parse

$parse服务其实就是一种解析表达式的功能,就像ng-model=“test”,你在html中写这个东西谁知道你ng-model=“test”中,其实你想绑定的是当前控制器(上下文容器)中scope(上下文)中的test里面的值,ng就是通过$parse服务去帮助你解析这个表达式的,所以在调用$parse服务的时候你需要传递上下文对象,让ng知道你是要去哪里的scope(上下文)去找你这个test。

所以我们看到第一行测试代码是这样的:

getter(context)).toEqual('angular') //实际上就是 $parse('user.name')(context)

在这个context就是上下文,他能返回“angular“这个字符串的原理就是经过这三步的:

1.获取当前的表达式user.name

2.获取当前的上下文对象{user:{name:'angular'}}

3.在上下问对象中寻找表达式,最终获得“angular“这个字符创

所以这句测试代码是成功的。

我们看第二个方法setter方法

setter(context, 'newValue');//实际上就是 $parse('user.name').assign(context, 'newValue')
expect(context.user.name).toEqual('newValue');//测试数据上下文的值是否被改变

这里的setter方法其实是改变值得方法

1.获取当前的表达式user.name

2.获取当前的上下文对象{user:{name:'angular'}}

3.改变表达式中的值,将上下文对象编程{user:{name:'newValue'}}

于是上下文对象发生了改变,重新用getter方法去获取表达式的时候,上下文已经从{user:{name:'angular'}} --> {user:{name:'newValue'}},最后获取的表达式的值自然就是“newValue”了,所以测试代码也是通过的。

expect(getter(context, locals)).toEqual('local');//实际上就是$parse('user.name')(context, locals)

这里要表现的其实是上下文的替换功能。

在getter的方法中我们不仅可以选择第一个上下文,但是如果我们传递了第二个参数,那么第一个上下文就会被第二个上下文覆盖,注意是覆盖.

1.获取当前的表达式user.name

2.获取当前的上下文对象{user:{name:'angular'}}

3.覆盖当前的上下文{user:{name:'local'}}

4.获取解析之后表达式的值

重新回到$eval这个地方,我们看待$eval源码中可以看出$eval只有get功能,而没有set功能,但是有些时候我们可以选择传递第二个上下文,来达到修改值得效果。

在这里$parse服务就已将说完了,接下来就是$compile

--------------------------------------------------

如果你了解了$parse的概念之后,我想$compile也差不多理解了,其实和$parse很像。但是他是解析一段html代码的,他的功能就是将死模板变成活模板,也是指令的核心服务。

比如你有一段html代码 <h1>{{test}}</h1>,如果你将这段代码直接放在html代码里面,它所呈现的内容是怎样的我不说大家也应该懂。这就是死模板了,而所谓的活模板,就是这里面的数据全部经过了数据的绑定 {{test}}会自动找到当前的上下文,来绑定数据。最后显示出来的 就是活模板,也就是经过数据绑定的模板。

$compile('死模板')(上下文对象),这样就将死模板编程了活模板,你就可以对这段活的html代码做操作了,例如增加到当前节点,等等。

但是在指令中,她会返回两个函数pre-link和post-link

第一个执行的是pre-link,它对于同一个指令的遍历顺序是从父节点到子节点的遍历,在这个阶段,dom节点还没有稳定下来,无法做一些绑定事件的操作,但是我们可以在这里进行一些初始化数据的处理。

第二个执行的是post-link,也就是我们常说的link函数,他是从子节点到父节点遍历的,在这个阶段,DOM节点已经稳定下来了,我们一般会在这里进行很多的操作。

以上就是小编为大家带来的基于angular中的重要指令详解($eval,$parse和$compile)全部内容了,希望大家多多支持我们~

(0)

相关推荐

  • angular.js指令中的controller、compile与link函数的不同之处

    前言 算了算用angualrjs去做开发也有两个月了,但做为一个菜鸟,难免会被大神吊打(这里有一个悲伤的故事...).某天一位前端大神问我:你知道angular指令中的controller,compile,link函数有什么不同?然后我就一脸懵逼了....于是决定深入的去探究下. 今天我们来一起了解一下它们有什么不同的地方: 先看一段示例代码 var ag = angular.module("myApp",[]); ag.controller("myCtrl",[&

  • Angular的Bootstrap(引导)和Compiler(编译)机制

    在上节简单介绍了Angular js框架,在这节将继续Angular的Bootstrap(引导)和Compiler(编译)机制. 一:Bootstrap:Angular的初始化 1:Angular推荐的自动化初始如下: <!doctype html> <html xmlns:ng="http://angularjs.org" ng-app> <body> ... <script src="angular.js"> &l

  • Angular中$compile源码分析

    $compile,在Angular中即"编译"服务,它涉及到Angular应用的"编译"和"链接"两个阶段,根据从DOM树遍历Angular的根节点(ng-app)和已构造完毕的 \$rootScope对象,依次解析根节点后代,根据多种条件查找指令,并完成每个指令相关的操作(如指令的作用域,控制器绑定以及transclude等),最终返回每个指令的链接函数,并将所有指令的链接函数合成为一个处理后的链接函数,返回给Angluar的bootstrap

  • 浅谈Angularjs link和compile的使用区别

    compile 想在dom渲染前对它进行变形,并且不需要scope参数 想在所有相同directive里共享某些方法,这时应该定义在compile里,性能会比较好 返回值就是link的function,这时就是共同使用的时候 link 对特定的元素注册事件 需要用到scope参数来实现dom元素的一些行为 以上就是小编为大家带来的浅谈Angularjs link和compile的使用区别全部内容了,希望大家多多支持我们~

  • AngularJs html compiler详解及示例代码

    原文再续,书接上回...依旧参考http://code.angularjs.org/1.0.2/docs/guide/compiler 一.总括 Angular的HTML compiler允许开发者自定义新的HTML语法.compiler允许我们对任意HTML元素或属性,甚至是新的HTML标签.属性(如<beautiful girl="cf"></beautiful >)附加行为.Angular将这些附加行为称为directives. HTML有很多专门格式化静

  • angularjs指令中的compile与link函数详解

    通常大家在使用ng中的指令的时候,用的链接函数最多的是link属性,下面这篇文章将告诉大家complie,pre-link,post-link的用法与区别. angularjs里的指令非常神奇,允许你创建非常语义化以及高度重用的组件,可以理解为web components的先驱者. 网上已经有很多介绍怎么使用指令的文章以及相关书籍,相互比较的话,很少有介绍compile与link的区别,更别说pre-link与post-link了. 大部分教程只是简单的说下compile会在ng内部用到,而且建

  • 基于angular中的重要指令详解($eval,$parse和$compile)

    在angular的服务中,有一些服务你不得不去了解,因为他可以说是ng的核心,而今天,我要介绍的就是ng的两个核心服务,$parse和$compile.其实这两个服务讲的人已经很多了,但是100个读者就有100个哈姆雷特,我在这里讲讲自己对于他们两个服务的理解. 大家可能会疑问,$eval呢,其实他并不是一个服务,他是scope里面的一个方法,并不能算服务,而且它也基于parse的,所以只能算是$parse的另一种写法而已,我们看一下ng源码中$eval的定义是怎样的就知道了 $eval: fu

  • 基于C++中setiosflags()的用法详解

    cout<<setiosflags(ios::fixed)<<setiosflags(ios::right)<<setprecision(2); setiosflags 是包含在命名空间iomanip 中的C++ 操作符,该操作符的作用是执行由有参数指定区域内的动作:   iso::fixed 是操作符setiosflags 的参数之一,该参数指定的动作是以带小数点的形式表示浮点数,并且在允许的精度范围内尽可能的把数字移向小数点右侧:   iso::right 也是se

  • Angular中的$watch方法详解

    在$apply方法中提到过脏检查,首先apply方法会触发evel方法,当evel方法解析成功后,会去触发digest方法,digest方法会触发watch方法. (1)$watch简介 在digest执行时,如果watch观察的的value与上一次执行时不一样时,就会被触发. AngularJS内部的watch实现了页面随model的及时更新. $watch方法在用的时候主要是手动的监听一个对象,但对象发生变化时触发某个事件. (2)watch方法用法 $watch(watchFn,watch

  • iOS逆向工程之Hopper中的ARM指令详解

    虽然前段时间ARM被日本软银收购了,但是科技是无国界的,所以呢ARM相关知识该学的学.现在看ARM指令集还是倍感亲切的,毕竟大学里开了ARM这门课,并且做了不少的实验,当时自我感觉ARM这门课学的还是可以的.虽然当时感觉学这门课以后似乎不怎么用的上,可曾想这不就用上了吗,不过之前学的都差不多忘了,还得捡起来呢.ARM指令集是精简指令集,从名字我们就能看出指令的个数比那些负责指令集要少一些.当然本篇所涉及的ARM指令集是冰山一角,不过也算是基础,可以阅读Hopper中的汇编了,实践出真知,看多了自

  • 基于Java中字符串内存位置详解

    前言 之前写过一篇关于JVM内存区域划分的文章,但是昨天接到蚂蚁金服的面试,问到JVM相关的内容,解释一下JVM的内存区域划分,这部分答得还不错,但是后来又问了Java里面String存放的位置,之前只记得String是一个不变的量,应该是要存放在常量池里面的,但是后来问到new一个String出来应该是放到哪里的,这个应该是放到堆里面的,后来又问到String的引用是放在什么地方的,当时傻逼的说也是放在堆里面的,现在总结一下:基本类型的变量数据和对象的引用都是放在栈里面的,对象本身放在堆里面,

  • 基于java中集合的概念(详解)

    1.集合是储存对象的,长度可变,可以封装不同的对象 2.迭代器: 其实就是取出元素的方式(只能判断,取出,移除,无法增加) 就是把取出方式定义在集合内部,这样取出方式就可以直接访问集合内部的元素,那么取出方式就被定义成了内部类. 二每一个容器的数据结构不同,所以取出的动作细节也不一样.但是都有共性内容判断和取出,那么可以将共性提取,这些内部类都符合一个规则Iterator Iterator it = list.iterator(); while(it.hasNext()){ System.out

  • 基于pandas中expand的作用详解

    expand表示是否把series类型转化为DataFrame类型 下面代码中的n表示去掉下划线"_"的数量 代码如下: import numpy as np import pandas as pd s2 = pd.Series(['a_b_c_f_j', 'c_d_e_f_h', np.nan, 'f_g_h_x_g']) print("-----------------------------------") print(s2.str.split('_')) p

  • Linux中的info指令详解

    Info 是什么?info是一种文档格式,也是阅读此格式文档的阅读器:我们常用它来查看Linux命令的info文档.它以主题的形式把几个命令组织在一起,以便于我们阅读:在主题内以node(节点)的形式把本主题的几个命令串联在一起. 页面最上方的一行信息分别是:本node(节点,以下统称节点)所在文件的文件名.本节点的名称.下一节点的名称.前一节点的名称.上一节点的名称. Menu下方就是本主题内的命令,敲M键,然后输入命令的名称就可以查看该命令的info帮助文档了. 下面介绍一下它的几个常用快捷

  • 基于header的一些常用指令详解

    header常用指令header分为三部分:第一部分为HTTP协议的版本(HTTP-Version):第二部分为状态代码(Status):第三部分为原因短语(Reason-Phrase). // fix 404 pages:   用这个header指令来解决URL重写产生的404 headerheader('HTTP/1.1 200 OK'); // set 404 header:   页面没找到header('HTTP/1.1 404 Not Found'); //页面被永久删除,可以告诉搜索

  • 基于JPA中的@Basic注解详解

    今天我们来聊聊JPA @Basic注解的使用 1.介绍: @Basic 是实体类与数据库字段映射时最简单的类型.在该注解的介绍中: The <code>Basic</code> annotation can be applied to a persistent * property or instance variable of any of the following types: Java * primitive types, wrappers of the primitive

随机推荐