首頁  >  文章  >  php教程  >  基於angular中的重要指令詳解($eval,$parse和$compile)

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

高洛峰
高洛峰原創
2016-12-09 13:59:081092瀏覽

在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(&#39;user.name&#39;);
var setter = getter.assign;
var context = {user:{name:&#39;angular&#39;}};
var locals = {user:{name:&#39;local&#39;}};
 
expect(getter(context)).toEqual(&#39;angular&#39;);
setter(context, &#39;newValue&#39;);
expect(context.user.name).toEqual(&#39;newValue&#39;);
expect(getter(context, locals)).toEqual(&#39;local&#39;);

   

大家看到的是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(&#39;angular&#39;) //实际上就是 $parse(&#39;user.name&#39;)(context)

   

在這個context就是上下文,他能回傳「angular「這個字串的原理就是經過這三步的:

1.取得目前的表達式user.name

2.取得目前的上下文物件{user:{name:'angular'}}

3.在上下問物件中尋找表達式,最終獲得「angular「這個字元創

所以這句測試程式碼是成功的。

我們看第二個方法setter方法

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

   

這裡的setter方法其實是改變值得方法

1.取得目前的表達式user.nameuser

2. {name:'angular'}}

3.改變表達式中的值,將上下文物件編程{user:{name:'newValue'}}

於是上下文物件發生了改變,重新用getter方法去獲取表達式的時候,上下文已經從{user:{name:'angular'}} --> {user:{name:'newValue'}},最後取得的表達式的值自然就是「newValue」了,所以測試程式碼也是通過的。

expect(getter(context, locals)).toEqual(&#39;local&#39;);//实际上就是$parse(&#39;user.name&#39;)(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程式碼

{{test}}

,如果你將這段程式碼直接放在html程式碼裡面,它所呈現的內容是怎樣的我不說大家也應該懂。這就是死模板了,而所謂的活模板,就是這裡面的資料全部經過了資料的綁定 {{test}}會自動找到目前的上下文,來綁定資料。最後顯示出來的 就是活模板,也就是經過資料綁定的模板。

$compile('死模板')(上下文物件),這樣就將死模板編程了活模板,你就可以對這段活的html程式碼做操作了,例如增加到當前節點,等等。

但是在指令中,她會回傳兩個函數pre-link和post-link

第一個執行的是pre-link,它對於同一個指令的遍歷順序是從父節點到子節點的遍歷,在這個階段,dom節點還沒有穩定下來,無法做一些綁定事件的操作,但是我們可以在這裡進行一些初始化資料的處理。

第二個執行的是post-link,也就是我們常說的link函數,他是從子節點到父節點遍歷的,在這個階段,DOM節點已經穩定下來了,我們通常會在這裡進行很多的操作。


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn