AngularJS denetleyicilerinde 'this' ve $ kapsamı


1026

Gelen angularjs ana sayfasının "Oluştur Bileşenleri" bölümünde , bu örnek vardır:

controller: function($scope, $element) {
  var panes = $scope.panes = [];
  $scope.select = function(pane) {
    angular.forEach(panes, function(pane) {
      pane.selected = false;
    });
    pane.selected = true;
  }
  this.addPane = function(pane) {
    if (panes.length == 0) $scope.select(pane);
    panes.push(pane);
  }
}

selectYöntemin nasıl eklendiğine $scope, ancak addPaneyöntemin eklendiğine dikkat edin this. Bunu olarak değiştirirsem $scope.addPanekod kırılır.

Belgeler aslında bir fark olduğunu söylüyor, ancak farkın ne olduğunu belirtmiyor:

Angular'ın (1.0 RC öncesi) önceki sürümleri this, $scopeyöntemle dönüşümlü olarak kullanmanıza izin verdi , ancak bu artık geçerli değil. Yöntemlerden İçinde kapsamına tanımlanan thisve $scopedeğiştirilebilir (açısal setleri vardır thisiçin $scope), ancak aksi takdirde denetleyici kurucu içinde.

AngularJS kontrolörlerinde nasıl çalışır thisve $scopeçalışır?


Bunu da kafa karıştırıcı buluyorum. Bir görünüm bir denetleyici belirttiğinde (örn. Ng-controller = '...'), bu denetleyiciyle ilişkilendirilmiş $ kapsamı onunla birlikte gelir, çünkü görünüm $ scope özelliklerine erişebilir. Ancak bir direktif 'gerektiren başka bir denetleyicinin (ve daha sonra bağlantı işlevinde kullandığında), diğer denetleyiciyle ilişkili $ kapsamı onunla birlikte gelmez mi?
Mark Rajcok

"Önceki sürümler ..." hakkındaki bu kafa karıştırıcı alıntı şimdiye kadar kaldırıldı mı? O zaman güncelleme olabilir mi?
Dmitri Zaitsev

Birim testi için, '$ scope' yerine 'this' kullanırsanız, denetleyiciye alaycı bir kapsamla enjekte edemezsiniz ve böylece birim testi yapamazsınız. 'Bunu' kullanmanın iyi bir uygulama olduğunu düşünmüyorum.
abentan

Yanıtlar:


999

" AngularJS kontrolörlerinde nasıl çalışır thisve $scopeçalışır?"

Kısa cevap :

  • this
    • Denetleyici yapıcı işlevi çağrıldığında thisdenetleyicidir.
    • Bir $scopenesne üzerinde tanımlanan bir fonksiyon çağrıldığında, this"fonksiyon çağrıldığında yürürlükte olan kapsam" dır. Bu $scope, işlevin tanımlandığı gibi olabilir (veya olmayabilir!) . Yani, işlevin içinde thisve $scopeolabilecek değil aynı.
  • $scope
    • Her denetleyicinin ilişkili bir $scopenesnesi vardır.
    • Bir denetleyici (yapıcı) işlevi, ilişkili model özelliklerini ve işlevlerini / davranışlarını ayarlamaktan sorumludur $scope.
    • $scopeHTML / görünümden yalnızca bu nesne üzerinde tanımlanan yöntemlere (ve prototip miras varsa üst kapsam nesnelerine) erişilebilir. Örneğin, ng-clickfiltreler vb.

Uzun cevap :

Denetleyici işlevi bir JavaScript yapıcı işlevidir. Yapıcı işlevi yürütüldüğünde (örneğin, bir görünüm yüklendiğinde) this(yani, "işlev bağlamı") denetleyici nesnesine ayarlanır. Yani "tabs" denetleyici yapıcı işlevinde, addPane işlevi oluşturulduğunda

this.addPane = function(pane) { ... }

$ scope üzerinde değil, controller nesnesinde oluşturulur. Görünümler addPane işlevini göremez - yalnızca $ kapsamında tanımlanan işlevlere erişebilir. Başka bir deyişle, HTML'de bu çalışmaz:

<a ng-click="addPane(newPane)">won't work</a>

"Sekmeler" denetleyici yapıcı işlevi yürütüldükten sonra, aşağıdakilere sahibiz:

sekmelerden sonra denetleyici yapıcı işlevi

Kesik siyah çizgi Kalıtım gösterir - bir izole kapsamı prototip devralır Kapsamı . (HTML'de direktifle karşılaşılan kapsamdan prototipik olarak miras kalmaz.)

Şimdi, bölme yönergesinin bağlantı işlevi sekmeler yönergesi ile iletişim kurmak istiyor (bu gerçekten sekmeleri etkilemek anlamına gelir $ kapsamı bir şekilde izole etmek). Olaylar kullanılabilir, ancak başka bir mekanizma bölmenin requiresekmeler denetleyicisini yönlendirmesini sağlamaktır. ( require$ Yönergelerine ilişkin sekme yönergesi için bir mekanizma yok gibi görünüyor .)

Yani, bu şu soruyu akla getiriyor: eğer sadece sekmeler denetleyicisine erişimimiz varsa, sekmelere nasıl erişebiliriz $ scope isolate (gerçekten istediğimiz budur)?

Kırmızı noktalı çizgi cevaptır. AddPane () işlevinin "kapsamı" (burada JavaScript'in işlev kapsamına / kapanışlarına atıfta bulunuyorum) işlevin sekme yalıtımı $ kapsamına erişim sağlar. Yani, addPane (), addPane () tanımlandığında oluşturulan bir kapatma nedeniyle yukarıdaki diyagramdaki "sekmeler IsolateScope" a erişebilir. (Bunun yerine, tabs $ scope nesnesinde addPane () tanımlamış olsaydık, bölme yönergesinin bu işleve erişimi olmaz ve dolayısıyla $ scope sekmeleriyle iletişim kurmanın bir yolu olmazdı.)

Sorunuzun diğer kısmını cevaplamak için how does $scope work in controllers?:

$ Scope üzerinde tanımlanan fonksiyonlar içinde, this"fonksiyonun nerede / ne zaman çağrıldığı $ scope" olarak ayarlanır. Aşağıdaki HTML koduna sahip olduğumuzu varsayalım:

<div ng-controller="ParentCtrl">
   <a ng-click="logThisAndScope()">log "this" and $scope</a> - parent scope
   <div ng-controller="ChildCtrl">
      <a ng-click="logThisAndScope()">log "this" and $scope</a> - child scope
   </div>
</div>

Ve ParentCtrl(Yalnızca)

$scope.logThisAndScope = function() {
    console.log(this, $scope)
}

İlk bağlantıya tıkladığınızda gösterecektir thisve $scope"çünkü aynı işlevi çağrıldı yürürlükte kapsamı " ile ilişkili kapsamı ParentCtrl.

İkinci bağlantıya tıkladığınızda ortaya çıkaracaktır thisve $scopevardır değil "çünkü aynı işlevi çağrıldı yürürlükte kapsamı " ile ilişkili kapsamı ChildCtrl. İşte burada, 's thisolarak ayarlanmıştır . Yöntemin içinde hala 's $ kapsamı var.ChildCtrl$scope$scopeParentCtrl

Vaktini boşa harcamak

thisHangi $ kapsamının etkilendiğini karıştırdığından, özellikle ng-repeat, ng-include, ng-switch ve direktiflerin tümünün kendi alt kapsamlarını oluşturabileceği göz önüne alındığında, $ scope üzerinde tanımlanan bir fonksiyonun içinde kullanmaya çalışmıyorum .


6
@tamakisquare, alıntıladığınız kalın metinlerin denetleyici yapıcı işlevi çağrıldığında geçerli olduğuna inanıyorum - yani, denetleyici oluşturulduğunda = bir $ kapsamıyla ilişkili. Daha sonra, rasgele JavaScript kodu $ scope nesnesinde tanımlanan bir yöntemi çağırdığında geçerli olmaz.
Mark Rajcok

79
Artık denetleyiciyi "MyController olarak myctrl" ve ardından myctrl.addPane () olarak adlandırarak addPane () işlevini doğrudan şablonda çağırmanın mümkün olduğuna dikkat edin. Bkz. Docs.angularjs.org/guide/concepts#controller
Christophe Augier

81
Çok fazla doğal karmaşıklık.
İnanç Gümüş

11
Bu çok bilgilendirici bir cevap ama pratik bir sorunla geri döndüğümde ( $ scope nasıl çağırılır . Bu hala yararlı bir cevap olsa da, "doğal karmaşıklığı" şaşırtıcı buluyorum.
dumbledad

11
Javascript - çok fazla ip [kendinizi asmak için].
AlikElzin-kilaka

55

Buna 'addPane' in atanmasının nedeni <pane>direktiftir.

paneYönergesinin yaptığı require: '^tabs'bağlantı fonksiyonuna içine, bir üst direktifi gelen sekmeler denetleyici nesne koyar.

addPaneatanan thisböylece panebağlantı fonksiyonu görebilirsiniz. Daha sonra panelink işlevinde, kontrolörün addPanesadece bir özelliği vardır tabsve sadece tabsControllerObject.addPane'dir. Böylece bölme yönergesinin bağlantı işlevi sekme denetleyicisi nesnesine ve dolayısıyla addPane yöntemine erişebilir.

Umarım açıklamam yeterince açıktır .. açıklamak biraz zor.


3
Açıklama için teşekkürler. Dokümanlar, denetleyicinin yalnızca kapsamı ayarlayan bir işlev olduğunu gösteriyor. Kapsamdaki tüm eylemler gerçekleşirse denetleyiciye neden bir nesne gibi davranılıyor? Neden ana kapsamı sadece bağlama fonksiyonuna geçirmiyorsunuz? Düzenleme: Bu soru daha iyi ifade etmek için, denetleyici yöntemleri ve kapsam yöntemleri hem aynı veri yapısı (kapsam) üzerinde çalışırsa, neden hepsini tek bir yerde koymak değil?
Alexei Boronine

Görünen o ki ana kapsam, "üst kapsamdaki verileri yanlışlıkla okumamalı veya değiştirmemeli" yeniden kullanılabilir bileşenleri destekleme arzusu nedeniyle lnk işlevine aktarılmadı. Ancak bir direktif, ana kapsamdaki ('bölme' direktifinin yaptığı gibi) BAZI ÖZEL verileri gerçekten okumak veya değiştirmek istiyorsa, biraz çaba gerektirir: denetleyiciyi istenen ana kapsamın olduğu yerde 'zorunlu kılın', ardından bir belirli verilere erişmek için bu denetleyicideki yöntemi kullanın ('bu' kapsamı değil). İstenen ebeveyn kapsamı lnk işlevine enjekte edilmediğinden, bunu yapmanın tek yolu olduğunu düşünüyorum.
Mark Rajcok

1
Hey mark, aslında direktifin kapsamını değiştirmek daha kolay. Sadece jsfiddle.net/TuNyj
Andrew Joslin

3
Keman için @Andy için teşekkürler. Kemanınızda, direktif yeni bir kapsam oluşturmuyor, bu yüzden link fonksiyonunun kontrolörün kapsamına doğrudan nasıl erişebildiğini görebiliyorum (sadece bir kapsam olduğundan). Sekmeler ve bölme yönergeleri, ayırma kapsamları kullanır (yani, üst kapsamdan prototipik olarak miras almayan yeni alt kapsamlar oluşturulur). İzole kapsam durumu için, bir denetleyicide bir yöntem tanımlamanın ('this' kullanarak) başka bir direktifin diğer (yalıtılmış) kapsama (dolaylı) erişmesine izin vermenin tek yolu olduğu görülmektedir.
Mark Rajcok

27

İkisi arasındaki fark hakkında oldukça ilginç bir açıklama ve modelleri denetleyiciye bağlamak ve modelleri görünüme bağlamak için denetleyiciyi takma tercihini giderek daha fazla tercih ediyorum. http://toddmotto.com/digging-into-angulars-controller-as-syntax/ makaledir.
Bahsetmiyor ama direktifleri tanımlarken, birden fazla direktif arasında bir şey paylaşmanız gerekiyorsa ve bir hizmet istemiyorsanız (hizmetlerin güç olduğu meşru durumlar var), daha sonra verileri üst direktifin denetleyicisine ekleyin.

$scopeHizmet yararlı şeyler bol sağlar $watchen belirgin olmak, ancak tüm şablonda düz denetleyici ve 'Farklı denetleyici' kullanarak, görünümüne bağlamak verilere gerekirse ince ve tartışmasız tercih edilir.


20

Aşağıdaki yazıyı okumanızı tavsiye ederim: AngularJS: "Controller as" veya "$ scope"?

Değişkenleri "$ scope" üzerinde göstermek için "Controller as" kullanmanın avantajlarını çok iyi açıklar.

Değişkenleri değil özellikle yöntemleri sorduğunuzu biliyorum, ancak bir tekniğe bağlı kalmanın ve onunla tutarlı olmanın daha iyi olduğunu düşünüyorum.

Bu yüzden benim görüşüme göre, postada tartışılan değişkenler nedeniyle, sadece "Denetleyici olarak" tekniğini kullanmak ve aynı zamanda yöntemlere uygulamak daha iyidir.


16

Bu derste ( https://www.codeschool.com/courses/shaping-up-with-angular-js ) "bunun" ve diğer pek çok şeyin nasıl kullanılacağını açıklarlar.

Denetleyiciye "bu" yöntemle yöntem eklerseniz, görünümünüzde denetleyicinizin adı "nokta" özelliğini kullanarak mülkünüzü veya yönteminizi çağırmanız gerekir.

Örneğin, görünümde denetleyicinizi kullanarak şu şekilde bir kodunuz olabilir:

    <div data-ng-controller="YourController as aliasOfYourController">

       Your first pane is {{aliasOfYourController.panes[0]}}

    </div>

6
Kurstan geçtikten sonra, kod kullanarak hemen kafam karıştı $scope, bu yüzden bahsettiğiniz için teşekkürler.
Matt Montag

16
Bu kurs $ kapsamından hiç bahsetmiyor, sadece kullanıyorlar asve thisbu yüzden farkı açıklamaya nasıl yardımcı olabilir?
dumbledad

10
Angular'a ilk dokunuşum söz konusu derstendi ve $scopehiç bahsedilmediğim gibi sadece thiskontrolörlerde kullanmayı öğrendim . Sorun, denetleyicinizde vaatleri yerine thisgetirmeye başladığınızda , vaat dönüş işlevi içinden var me = thismodele başvurmak gibi şeyler yapmak için çok sayıda başvuru sorununuz olması ve yapmaya başlamanız gerektiğidir this. Bu nedenle, hangi yöntemi kullanmam gerektiği konusunda hala çok kafam karıştı $scopeveya this.
Bruno Finger

@BrunoFinger Maalesef, Vaatler veya diğer kapanış ağır şeylere ihtiyacınız olacak var me = thisveya .bind(this)bunu yaptığınız zaman. Angular ile ilgisi yok.
Dzmitry Lazerka

1
Önemli olan, kontrolörün kendisini ng-controller="MyCtrl as MC"koymakla eşdeğer olduğunu bilmek $scope.MC = this- şablonda kullanım kapsamı üzerinde MyCtrl'in bir örneğini (bu) tanımlar{{ MC.foo }}
William B

3

Angular'ın önceki sürümleri (1.0 öncesi RC), bunu $ scope yöntemi ile birbirinin yerine kullanmanıza izin verdi, ancak artık durum böyle değil. Bu kapsamda tanımlanan yöntemlerin içinde bu ve $ kapsamı birbiriyle değiştirilebilir (açısal bunu $ kapsam olarak ayarlar), ancak denetleyici kurucunuzun içinde başka türlü değildir.

Bu davranışı geri getirmek için (neden değiştirildiğini bilen var mı?) Ekleyebilirsiniz:

return angular.extend($scope, this);

denetleyici işlevinizin sonunda (bu denetleyici işlevine $ scope enjekte edilmesi şartıyla).

Bu, alt öğeye erişebileceğiniz denetleyici nesnesi aracılığıyla üst kapsama erişime sahip olmanın güzel bir etkisi vardır. require: '^myParentDirective'


7
Bu makale , bu ve $ kapsamının neden farklı olduğuna dair iyi bir açıklama sağlar.
Robert Martin

1

$ scope farklı bir 'this' sonra denetleyiciye 'this' sahiptir.Bu nedenle, bir console.log (this) koyarsanız, size bir nesne (denetleyici) verir ve this.addPane (), denetleyici Nesnesine addPane Yöntemi ekler. Ancak $ kapsamının farklı kapsamı vardır ve kapsamındaki tüm yöntemin $ scope.methodName () tarafından erişilmesi gerekir. this.methodName()kontrolör içindeki kontrolör nesnesinin içine yöntem eklemek demektir. $scope.functionName()HTML içinde ve içeride

$scope.functionName(){
    this.name="Name";
    //or
    $scope.myname="myname"//are same}

Bu kodu düzenleyicinize yapıştırın ve görmek için konsolu açın ...

 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>this $sope vs controller</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular.min.js"></script>
    <script>
        var app=angular.module("myApp",[]);
app.controller("ctrlExample",function($scope){
          console.log("ctrl 'this'",this);
          //this(object) of controller different then $scope
          $scope.firstName="Andy";
          $scope.lastName="Bot";
          this.nickName="ABot";
          this.controllerMethod=function(){

            console.log("controllerMethod ",this);
          }
          $scope.show=function(){
              console.log("$scope 'this",this);
              //this of $scope
              $scope.message="Welcome User";
          }

        });
</script>
</head>
<body ng-app="myApp" >
<div ng-controller="ctrlExample">
       Comming From $SCOPE :{{firstName}}
       <br><br>
       Comming from $SCOPE:{{lastName}}
       <br><br>
       Should Come From Controller:{{nickName}}
       <p>
            Blank nickName is because nickName is attached to 
           'this' of controller.
       </p>

       <br><br>
       <button ng-click="controllerMethod()">Controller Method</button>

       <br><br>
       <button ng-click="show()">Show</button>
       <p>{{message}}</p>

   </div>

</body>
</html>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.