Hızlı cevap :
Bir alt kapsam normalde üst kapsamından prototip olarak miras alır, ancak her zaman değil. Bu kuralın bir istisnası, direktiftir scope: { ... }
- bu, prototip olarak miras almayan bir "ayrı tutma" kapsamı oluşturur. Bu yapı genellikle "yeniden kullanılabilir bileşen" yönergesi oluşturulurken kullanılır.
Nüanslara gelince, kapsam mirası normalde düzdür ... alt kapsamda 2 yönlü veri bağlama (yani, form öğeleri, ng-model) gerekene kadar . Üst kapsamda bir ilkeye (örn., Sayı, dize, boole) bağlanmaya çalışırsanız , alt kapsamın içinden ng-repeat, ng-switch ve ng-include sizi uyarabilir. Çoğu insanın çalışmasını beklediği şekilde çalışmaz. Alt kapsam, aynı adın üst özelliğini gizleyen / gölgeleyen kendi özelliğini alır. Geçici çözümleriniz
- modeliniz için üst öğedeki nesneleri tanımlayın, ardından alt öğedeki o nesnenin özelliğine başvurun: parentObj.someProp
- $ parent.parentScopeProperty kullanın (her zaman mümkün değildir, ancak mümkünse 1'den daha kolay)
- Üst kapsamda bir işlev tanımlayın ve alt işlevden çağırın (her zaman mümkün değildir)
Yeni angularjs geliştiriciler genellikle fark yoktur ng-repeat
, ng-switch
, ng-view
, ng-include
ve ng-if
bu direktifler söz konusu olduğunda sorun genellikle gösterir böylece tüm yeni alt kapsamları oluşturun. ( Sorunun hızlı bir açıklaması için bu örneğe bakın .)
Bu ilkellerle ilgili sorun her zaman bir 'sahip olmak' için "en iyi uygulama" izlenerek kolayca önlenebilir . ng modellerinizde - 3 dakika değerinde izleyin. Misko, ilkel bağlanma sorununu gösterir ng-switch
.
Bir '.' modellerinizde prototip mirasın oyunda olduğundan emin olursunuz. Yani, kullanın
<input type="text" ng-model="someObj.prop1">
<!--rather than
<input type="text" ng-model="prop1">`
-->
Uzun cevap :
JavaScript Prototypal Kalıtım
Ayrıca AngularJS wiki'sine de yerleştirilir: https://github.com/angular/angular.js/wiki/Understanding-Scopes
İlk olarak, özellikle sunucu tarafı geçmişinden geliyorsanız ve sınıfsal kalıtım hakkında daha fazla bilgi sahibi iseniz, prototippal miras hakkında sağlam bir anlayışa sahip olmanız önemlidir. Önce bunu gözden geçirelim.
ParentScope'un aString, aNumber, anArray, anObject ve aFunction özelliklerine sahip olduğunu varsayalım. ChildScope prototipik olarak parentScope'tan miras alırsa:
(Yerden tasarruf etmek için, anArray
nesneyi üç ayrı gri değişmez değerine sahip tek bir mavi nesne yerine üç değeriyle tek bir mavi nesne olarak göstereceğim .)
ParentScope üzerinde tanımlanan bir özelliğe alt kapsamdan erişmeye çalışırsak, JavaScript ilk olarak alt kapsamı arar, özelliği bulmaz, sonra devralınan kapsamı arar ve özelliği bulur. (Mülkü parentScope'ta bulamazsa, prototip zincirini ... kök kapsamına kadar devam ettirir). Yani, bunların hepsi doğrudur:
childScope.aString === 'parent string'
childScope.anArray[1] === 20
childScope.anObject.property1 === 'parent prop1'
childScope.aFunction() === 'parent output'
Diyelim ki bunu yapıyoruz:
childScope.aString = 'child string'
Prototip zincirine başvurulmaz ve childScope'a yeni bir aString özelliği eklenir. Bu yeni özellik parentScope özelliğini aynı adla gizler / gölgeler. Aşağıda tekrar ve tekrarlama konularını ele aldığımızda bu çok önemli hale gelecektir.
Diyelim ki bunu yapıyoruz:
childScope.anArray[1] = '22'
childScope.anObject.property1 = 'child prop1'
Nesneler (anArray ve anObject) childScope'ta bulunmadığından prototip zincirine danışılır. Nesneler parentScope içinde bulunur ve özellik değerleri orijinal nesnelerde güncellenir. ChildScope'a yeni özellik eklenmez; yeni nesne oluşturulmaz. (JavaScript dizilerinde ve işlevlerinde de nesne olduğunu unutmayın.)
Diyelim ki bunu yapıyoruz:
childScope.anArray = [100, 555]
childScope.anObject = { name: 'Mark', country: 'USA' }
Prototip zincirine başvurulmaz ve alt kapsam, parentScope nesne özelliklerini aynı adlarla gizleyen / gölgeleyen iki yeni nesne özelliği alır.
çıkarımlar:
- ChildScope.propertyX'i okursak ve childScope'un propertyX'i varsa, prototip zincirine başvurulmaz.
- ChildScope.propertyX ayarını yaparsak, prototip zincirine başvurulmaz.
Son bir senaryo:
delete childScope.anArray
childScope.anArray[1] === 22 // true
Önce childScope özelliğini sildik, sonra mülke tekrar erişmeye çalıştığımızda prototip zincirine danışılıyor.
Açısal Kapsam Mirası
Yarışmacılar:
- Aşağıdakiler yeni kapsamlar oluşturur ve prototip olarak miras alır: ng-repeat, ng-include, ng-switch, ng-controller, ile yönerge
scope: true
, ile yönerge transclude: true
.
- Aşağıda, prototip olarak miras alınmayan yeni bir kapsam oluşturulur: ile yönerge
scope: { ... }
. Bu, bunun yerine bir "ayrı tutma" kapsamı oluşturur.
Varsayılan olarak, direktiflerin yeni kapsam oluşturmadığını unutmayın - yani, varsayılan değerdir scope: false
.
ng içerir
Farz edelim ki denetleyicimizde:
$scope.myPrimitive = 50;
$scope.myObject = {aNumber: 11};
Ve HTML kodumuzda:
<script type="text/ng-template" id="/tpl1.html">
<input ng-model="myPrimitive">
</script>
<div ng-include src="'/tpl1.html'"></div>
<script type="text/ng-template" id="/tpl2.html">
<input ng-model="myObject.aNumber">
</script>
<div ng-include src="'/tpl2.html'"></div>
Her ng-include, prototipik olarak üst kapsamdan miras alan yeni bir alt kapsam oluşturur.
İlk girdi metin kutusuna (77 gibi) yazmak, alt kapsamın myPrimitive
, aynı adın üst kapsam özelliğini gizleyen / gölgeleyen yeni bir kapsam özelliği almasına neden olur . Bu muhtemelen istediğiniz / beklediğiniz değil.
İkinci giriş metin kutusuna (örneğin, "99") yazmak yeni bir alt öğe ile sonuçlanmaz. Tpl2.html modeli bir nesne özelliğine bağladığından, ngModel myObject nesnesini ararken prototypal mirası devreye girer - üst kapsamda bulur.
Modelimizi ilkelden bir nesneye değiştirmek istemiyorsak, ilk şablonu $ parent kullanmak için yeniden yazabiliriz:
<input ng-model="$parent.myPrimitive">
Bu giriş metin kutusuna (örneğin, "22") yazmak yeni bir alt öğe ile sonuçlanmaz. Model artık üst kapsamın bir özelliğine bağlıdır (çünkü $ üst, üst kapsama başvuran bir alt kapsam özelliğidir).
Tüm kapsamlar için (prototip veya değil), Açısal, $ parent, $$ childHead ve $$ childTail kapsam özellikleri aracılığıyla her zaman bir üst-alt ilişkisini (yani bir hiyerarşi) izler. Normalde bu kapsam özelliklerini diyagramlarda göstermem.
Form öğelerinin yer almadığı senaryolarda, başka bir çözüm, ilkel üzerinde değişiklik yapmak için üst kapsamda bir işlev tanımlamaktır. Daha sonra, çocuğun her zaman prototip mirası nedeniyle alt kapsam tarafından kullanılabilecek bu işlevi çağırdığından emin olun. Örneğin,
// in the parent scope
$scope.setMyPrimitive = function(value) {
$scope.myPrimitive = value;
}
İşte bu "ana işlev" yaklaşımını kullanan örnek bir keman . (Bu keman bu cevabın bir parçası olarak yazılmıştır: https://stackoverflow.com/a/14104318/215945 .)
Ayrıca bkz . Https://stackoverflow.com/a/13782671/215945 ve https://github.com/angular/angular.js/issues/1267 .
ng şalter
ng-switch kapsam mirası tıpkı ng-include gibi çalışır. Bu nedenle, üst kapsamdaki bir ilkeye 2 yönlü veri bağlamaya ihtiyacınız varsa, $ parent kullanın veya modeli bir nesne olarak değiştirin ve sonra bu nesnenin bir özelliğine bağlayın. Bu, alt kapsamın üst kapsam özelliklerinin gizlenmesini / gölgelenmesini önleyecektir.
Ayrıca bkz. AngularJS, bir anahtar kasasının bağlama kapsamı?
ng tekrar
Ng-repeat biraz farklı çalışır. Farz edelim ki denetleyicimizde:
$scope.myArrayOfPrimitives = [ 11, 22 ];
$scope.myArrayOfObjects = [{num: 101}, {num: 202}]
Ve HTML kodumuzda:
<ul><li ng-repeat="num in myArrayOfPrimitives">
<input ng-model="num">
</li>
<ul>
<ul><li ng-repeat="obj in myArrayOfObjects">
<input ng-model="obj.num">
</li>
<ul>
Her öğe / yineleme için ng-repeat, üst kapsamdan prototipik olarak miras alan yeni bir kapsam oluşturur, ancak aynı zamanda öğenin değerini yeni alt kapsamdaki yeni bir özelliğe atar . (Yeni özelliğin adı döngü değişkeninin adıdır.) Ng-repeat için Açısal kaynak kodunun gerçekte şudur:
childScope = scope.$new(); // child scope prototypically inherits from parent scope
...
childScope[valueIdent] = value; // creates a new childScope property
Öğe bir ilkel ise (myArrayOfPrimitives'ta olduğu gibi), esas olarak değerin bir kopyası yeni alt kapsam özelliğine atanır. (Ng-modeli, bu nedenle çocuk kapsamı kullanılarak, örneğin, çocuk kapsamı özelliğin değerini değiştirmek num
etmez) değil dizi üst kapsamı referans olarak değiştirin. Bu nedenle, yukarıdaki ilk ng yinelenmesinde, her alt kapsam num
myArrayOfPrimitives dizisinden bağımsız bir özellik alır :
Bu tekrarlama çalışmaz (istediğiniz gibi / beklediğiniz gibi). Metin kutularına yazmak, gri kutulardaki değerleri değiştirir; bunlar yalnızca alt kapsamlarda görünür. İstediğimiz girdilerin alt kapsam ilkel özelliğini değil, myArrayOfPrimitives dizisini etkilemesi. Bunu yapmak için, modeli bir nesne dizisi olarak değiştirmemiz gerekir.
Bu nedenle, öğe bir nesne ise, yeni alt etki özelliğine orijinal nesneye (kopya değil) bir başvuru atanır. Çocuk kapsamı özelliğin değerini değiştirme (ng-modeli kullanılarak, örneğin, bu nedenle obj.num
) yapar nesne üst kapsamı referans olarak değiştirin. Yani yukarıdaki ikinci tekrarda:
(Nereye gideceğini netleştirmek için bir çizgi gri renklendirdim.)
Bu beklendiği gibi çalışır. Metin kutularına yazmak, hem alt hem de üst kapsamlar tarafından görülebilen gri kutulardaki değerleri değiştirir.
Ayrıca bkz. Ng-model, ng-repeat ve girişlerle ilgili zorluk ve
https://stackoverflow.com/a/13782671/215945
ng kontrol cihazı
Ng-controller kullanan kontrolörler yuvalama, tıpkı ng-include ve ng-switch gibi normal prototip palet mirasıyla sonuçlanır, bu nedenle aynı teknikler geçerlidir. Ancak, "iki denetleyicinin $ scope miras yoluyla bilgi paylaşması kötü bir form olarak kabul edilir" - http://onehungrymind.com/angularjs-sticky-notes-pt-1-architecture/
Verileri paylaşmak için bir hizmet kullanılmalıdır yerine denetleyiciler.
(Verileri gerçekten denetleyicilerin kapsamını devralma yoluyla paylaşmak istiyorsanız, yapmanız gereken hiçbir şey yoktur. Alt kapsamın tüm üst kapsam özelliklerine erişimi olacaktır. Ayrıca bkz. Denetleyici yükleme sırası yükleme veya gezinme sırasında farklılık gösterir )
direktifler
- default (
scope: false
) - yönerge yeni bir kapsam oluşturmaz, bu nedenle burada kalıtım yoktur. Bu kolay, ama aynı zamanda tehlikelidir, örneğin, örneğin, bir direktif, aslında mevcut bir mülkü gizlerken, kapsamda yeni bir mülk yarattığını düşünebilir. Bu, yeniden kullanılabilir bileşenler olarak tasarlanan yönergeleri yazmak için iyi bir seçim değildir.
scope: true
- yönerge, üst kapsamdan prototipik olarak miras kalan yeni bir alt kapsam oluşturur. Birden fazla yönerge (aynı DOM öğesinde) yeni bir kapsam isterse, yalnızca bir yeni alt kapsam oluşturulur. "Normal" prototip kalıtımımız olduğundan, bu ng-include ve ng-switch gibidir, bu nedenle ana kapsam ilkellerine 2 yönlü veri bağlama ve ana kapsam özelliklerinin alt kapsam gizleme / gölgelemesi konusunda dikkatli olun.
scope: { ... }
- yönerge yeni bir izolat / yalıtılmış kapsam oluşturur. Prototip olarak miras almaz. Bu, yeniden kullanılabilir bileşenler oluştururken genellikle en iyi seçimdir, çünkü yönerge yanlışlıkla üst kapsamı okuyamaz veya değiştiremez. Ancak, bu tür direktiflerin genellikle birkaç üst kapsam özelliğine erişmesi gerekir. Nesne karması, ana kapsam ile ayırma kapsamı arasında iki yönlü ('=' kullanarak) veya tek yönlü ('@' kullanarak) bağlayıcı ayarlamak için kullanılır. Üst kapsam ifadelerine bağlanmak için '&' de vardır. Böylece, bunların tümü üst kapsamdan türetilen yerel kapsam özellikleri oluşturur. Özniteliklerin bağlayıcıyı ayarlamaya yardımcı olması için kullanıldığını unutmayın; yalnızca nesne karmasındaki üst kapsam özellik adlarına başvuramazsınız, bir öznitelik kullanmanız gerekir. Örneğin, üst mülke bağlanmak istiyorsanız bu işe yaramazparentProp
izole kapsamda: <div my-directive>
ve scope: { localProp: '@parentProp' }
. Direktifin bağlamak istediği her bir üst özelliği belirtmek için bir öznitelik kullanılmalıdır: <div my-directive the-Parent-Prop=parentProp>
ve scope: { localProp: '@theParentProp' }
.
Kapsamın __proto__
referanslarını izole edin Object. İzolasyon kapsamının $ üst öğesi üst kapsama başvurur, bu nedenle yalıtılmış ve üst kapsamdan prototip olarak miras almasa da, yine de bir alt kapsamdır.
Aşağıdaki resim için
<my-directive interpolated="{{parentProp1}}" twowayBinding="parentProp2">
ve
scope: { interpolatedProp: '@interpolated', twowayBindingProp: '=twowayBinding' }
ayrıca, direktifin bunu bağlantı işlevinde yaptığını varsayalım: scope.someIsolateProp = "I'm isolated"
İzole kapsamlar hakkında daha fazla bilgi için bkz. Http://onehungrymind.com/angularjs-sticky-notes-pt-2-isolated-scope/
transclude: true
- yönerge, prototipik olarak üst kapsamdan miras alan yeni bir "aktarılan" alt kapsam oluşturur. Aktarılan ve yalıtılmış kapsam (varsa) kardeşlerdir - her bir kapsamın $ parent özelliği aynı üst kapsamı belirtir. Kopyalanan ve ayrılan kapsamın her ikisi de mevcutsa, yalıtımlı kapsam özelliği $$ nextSibling, aktarılan kapsamı referans alır. Kopyalanan kapsamdaki herhangi bir nüansın farkında değilim.
Aşağıdaki resim için, bu ilavelerle yukarıdaki talimatın aynısını varsayalım:transclude: true
Bu kemanınshowScope()
bir izolat ve transkripsiyon kapsamını incelemek için kullanılabilecek bir işlevi vardır. Kemandaki yorumlardaki talimatlara bakın.
özet
Dört tür kapsam vardır:
- normal prototip kapsamı devralma - ng-include, ng-switch, ng-denetleyici, yönerge
scope: true
- kopya / atama - ng-tekrarlı normal prototip kapsamı devralma. Her ng tekrarlama yeni bir alt kapsam oluşturur ve bu yeni alt kapsam her zaman yeni bir mülk edinir.
- kapsamı yalıtmak
scope: {...}
. Bu prototip değildir, ancak '=', '@' ve '&' öznitelikler aracılığıyla üst kapsam özelliklerine erişmek için bir mekanizma sağlar.
- transcluded kapsam - yönerge
transclude: true
. Bu aynı zamanda normal prototip kapsam mirasıdır, ancak aynı zamanda herhangi bir izolat kapsamının kardeşidir.
Tüm kapsamlar için (prototip veya değil), Angular her zaman $ parent ve $$ childHead ve $$ childTail özellikleri aracılığıyla bir üst-alt ilişkisini (yani, bir hiyerarşi) izler.
Şemalar graphvizHangi "* .dot" dosyaları, github . Tim Caswell'in " Nesne Grafikleriyle JavaScript Öğrenme ", diyagramlar için GraphViz kullanmanın ilham kaynağı oldu.