AngularJS'de yalıtılmış kapsamı olmayan bir yönergeden bir denetleyici işlevi çağırın


95

İzole kapsam kullanmadan bir yönergenin içinden üst kapsamdaki bir işlevi çağırmanın bir yolunu bulamıyorum. Yalıtılmış kapsam kullanırsam, üst kapsamdaki işleve erişmek için yalıtılmış olarak "&" kullanabileceğimi, ancak gerekli olmadığında yalıtılmış kapsam kullanmanın sonuçları olacağını biliyorum. Aşağıdaki HTML'yi düşünün:

<button ng-hide="hideButton()" confirm="Are you sure?" confirm-action="doIt()">Do It</button>

Bu basit örnekte, bir JavaScript onay iletişim kutusu göstermek ve doIt () 'i yalnızca onay iletişim kutusunda "Tamam" ı tıklarlarsa çağırmak istiyorum. Bu, yalıtılmış bir kapsam kullanarak basittir. Yönerge şöyle görünecektir:

.directive('confirm', function () {
    return {
        restrict: 'A',
        scope: {
            confirm: '@',
            confirmAction: '&'
        },
        link: function (scope, element, attrs) {
            element.bind('click', function (e) {
                if (confirm(scope.confirm)) {
                    scope.confirmAction();
                }
            });
        }
    };
})

Ancak sorun şu ki, ben izole kapsam kullandığım için, yukarıdaki örnekte ng-hide artık üst kapsama karşı değil, izole kapsamda (çünkü herhangi bir direktifte izole edilmiş bir kapsam kullanmak o elemandaki tüm direktiflerin izole kapsamı kullanın). İşte ng- hide'nin çalışmadığı yukarıdaki örnekten bir jsFiddle . (Bu kemanta, giriş kutusuna "evet" yazdığınızda düğmenin gizlenmesi gerektiğini unutmayın.)

Alternatif, yalıtılmış bir kapsam KULLANMAMAK olacaktır , ki bu yönergenin kapsamının izole edilmesine gerek olmadığı için burada gerçekten istediğim şey budur. Sahip olduğum tek sorun , izole kapsama aktarmazsam üst kapsamda bir yöntemi nasıl çağırırım ?

Burada izole kapsamı KULLANMADIĞIM ve ng- hide'nin iyi çalıştığı bir jsfiddle var , ancak, elbette, confirmAction () çağrısı çalışmıyor ve nasıl çalışmasını sağlayacağımı bilmiyorum.

Lütfen dikkat, gerçekten aradığım cevabın, izole bir kapsam KULLANMADAN dış kapsamdaki fonksiyonları nasıl çağıracağım. Ve bu onaylama diyaloğunun başka bir şekilde çalışmasını sağlamakla ilgilenmiyorum, çünkü bu sorunun amacı, dış kapsama nasıl çağrı yapılacağını bulmak ve yine de diğer direktiflerin ana kapsama aykırı çalışmasını sağlamaktır.

Alternatif olarak, diğer direktifler yine de ana kapsama aykırı çalışacaksa, yalıtılmış bir kapsam kullanan çözümleri duymak isterim, ancak bunun mümkün olduğunu düşünmüyorum.


Oradan geçenler için Dan Wahlin, izolat kapsamı ve işlev parametrelerini açıklayan çok iyi bir makale yazdı: weblogs.asp.net/dwahlin/…
tanguy_k

Yanıtlar:


118

Yönerge yalnızca bir işlevi çağırdığından (ve bir özellik için bir değer belirlemeye çalışmadığından), $ parse yerine (yalıtılmış olmayan bir kapsamla) $ eval kullanabilirsiniz :

scope.$apply(function() {
    scope.$eval(attrs.confirmAction);
});

Ya da daha iyisi, sadece $ apply kullanın , bu $ eval () argümanını kapsama karşı verecektir:

scope.$apply(attrs.confirmAction);

Vaktini boşa harcamak


Bunu bilmiyordum, bunun için teşekkürler. Her zaman $ parse yönteminin biraz fazla uzun olduğunu düşünmüşümdür.
Clark Pan

2
bu işlevde parametreleri nasıl ayarlarsınız?
CMCDragonkai

2
@CMCDragonkai, işlevin bağımsız değişkenleri varsa, bunları HTML'de belirtebilir confirm-action="doIt(arg1)"ve ardından scope.arg1$ eval çağrılmadan önce ayarlayabilirsiniz . Ancak, muhtemelen $ parse: stackoverflow.com/a/16200618/215945
Mark Rajcok

Kapsam yapmak mümkün değil mi. $ Eval (attrs.doIt ({arg1: 'blah'}) ;?
CMCDragonkai

3
@CMCDragonkai, hayır, scope.$eval(attrs.confirmAction({arg1: 'blah'})çalışmayacak. Bu sözdizimiyle $ parse kullanmanız gerekir.
Mark Rajcok

17

AngularJS'de $ parse hizmetini kullanmak istiyorsunuz.

Yani örneğiniz için:

.directive('confirm', function ($parse) {
    return {
        restrict: 'A',

        // Child scope, not isolated
        scope : true,
        link: function (scope, element, attrs) {
            element.bind('click', function (e) {
                if (confirm(attrs.confirm)) {
                    scope.$apply(function(){

                        // $parse returns a getter function to be 
                        // executed against an object
                        var fn = $parse(attrs.confirmAction);

                        // In our case, we want to execute the statement in 
                        // confirmAction i.e. 'doIt()' against the scope 
                        // which this directive is bound to, because the 
                        // scope is a child scope, not an isolated scope. 
                        // The prototypical inheritance of scopes will mean 
                        // that it will eventually find a 'doIt' function 
                        // bound against a parent's scope.
                        fn(scope, {$event : e});
                    });
                }
            });
        }
    };
});

$ Parse kullanarak bir mülk ayarlamakla ilgili bir şey var, ancak geldiğinizde o köprüyü geçmenize izin vereceğim.


Teşekkürler Clark, tam da aradığım buydu. $ Parse kullanmayı denedim, ancak kapsamı geçemediğim için benim için çalışmadı. Bu harika.
Jim Cooper

Bu çözümün de kapsam olmadan çalıştığını görünce şaşırdım: doğru. Anladığım kadarıyla kapsam şuydu: doğru, direktifin kapsamını prototip olarak üst kapsamdan devralan şeydir. Bunun neden hala onsuz çalıştığına dair bir fikriniz var mı?
Jim Cooper

2
hiçbir alt kapsam (silme scope:true) çalışmaz, çünkü yönerge yeni bir kapsam yaratmayacaktır ve bu nedenle işlevde scopebaşvurduğunuz linközellik, daha önce 'üst' kapsam ile tam olarak aynı kapsamdadır.
Clark Pan

Bu durum için $ application yeterlidir (cevabıma bakın).
Mark Rajcok

1
$ Etkinliği ne için?
CMCDragonkai

12

hideButtonÜst kapsamı açıkça çağırın .

İşte keman: http://jsfiddle.net/pXej2/5/

Ve işte güncellenmiş HTML:

<div ng-app="myModule" ng-controller="myController">
    <input ng-model="showIt"></input>
    <button ng-hide="$parent.hideButton()" confirm="Are you sure?" confirm-action="doIt()">Do It</button>
</div>

Çalışan bir çözüm için +1. Aslında $ parent kullanmayı denedim ve işe yaramayınca pes ettim. Çözümünüzü gördükten sonra daha yakından baktım ve geçtiğim parametrelere $ parent eklemekte başarısız olduğum ortaya çıktı (orijinal kemanımda gösterilmemiştir). Örneğin, showIt'i hideButton () işlevine geçirmek isteseydim, $ parent.hideButton ($ parent.showIt) kullanmam gerekirdi ve ikinci $ ebeveyn eksikti. Ancak Clark'ın cevabını kabul edeceğim, çünkü bu çözüm benim yönergemin kullanıcısına $ parent eklemesi gerektiğini bilmesini gerektirmiyor. Görüş için teşekkürler!
Jim Cooper

1

Sahip olduğum tek sorun, izole kapsama aktarmazsam üst kapsamda bir yöntemi nasıl çağırırım?

Burada izole kapsamı KULLANMADIĞIM ve ng-hide'nin iyi çalıştığı bir jsfiddle var, ancak, elbette, confirmAction () çağrısı çalışmıyor ve nasıl çalıştıracağımı bilmiyorum.

İlk olarak küçük bir nokta: Bu durumda dış denetleyicinin kapsamı, yönergenin kapsamının üst kapsamı değildir; dış kontrolörün kapsamı olan yönerge kapsamı. Diğer bir deyişle, yönergede kullanılan değişken isimleri doğrudan denetleyicinin kapsamı içinde aranacaktır.

Sonra, bu düğme için yazma attrs.confirmAction()çalışmıyorattrs.confirmAction

<button ... confirm-action="doIt()">Do It</button>

dizedir "doIt()"ve bir dizeyi çağıramazsınız, örneğin "doIt()"().

Sorunuz gerçekten:

İsmini bir dizge olarak aldığımda bir işlevi nasıl çağırırım?

JavaScript'te, çoğunlukla aşağıdaki gibi noktalı gösterimi kullanarak işlevleri çağırırsınız:

some_obj.greet()

Ancak dizi gösterimini de kullanabilirsiniz:

some_obj['greet']

İndeks değerinin nasıl bir dizge olduğuna dikkat edin . Yani, direktifinizde bunu kolayca yapabilirsiniz:

  link: function (scope, element, attrs) {

    element.bind('click', function (e) {

      if (confirm(attrs.confirm)) {
        var func_str = attrs.confirmAction;
        var func_name = func_str.substring(0, func_str.indexOf('(')); // Or func_str.match(/[^(]+/)[0];
        func_name = func_name.trim();

        console.log(func_name); // => doIt
        scope[func_name]();
      }
    });
  }

+1, çünkü işlevi ayrıştırma fikri aldatıcıydı ve bana benzer ama başka bir sorunla yardımcı oldu. Teşekkürler!
Anmol Saraf
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.