Zakres dostępu AngularJS spoza funkcji js

134

Próbuję sprawdzić, czy istnieje prosty sposób na uzyskanie dostępu do wewnętrznego zakresu kontrolera za pomocą zewnętrznej funkcji JavaScript (całkowicie nieistotnej dla kontrolera docelowego)

Widziałem to na kilku innych pytaniach

angular.element("#scope").scope();

pobrałby zakres z elementu DOM, ale moje próby obecnie nie przynoszą odpowiednich wyników.

Oto jsfiddle: http://jsfiddle.net/sXkjc/5/

Obecnie przechodzę przez przejście od zwykłego JS do Angular. Głównym powodem, dla którego próbuję to osiągnąć, jest zachowanie mojego oryginalnego kodu biblioteki w jak największym stopniu nienaruszonym; oszczędzając mi potrzeby dodawania każdej funkcji do kontrolera.

Jakieś pomysły, jak mógłbym się do tego zabrać? Mile widziane są również komentarze do powyższych skrzypiec.

dk123
źródło
FYI zgodnie z dokumentami używającymi .scope()wymaga, aby dane debugowania były włączone, ale używanie danych debugowania w środowisku produkcyjnym nie jest zalecane ze względu na szybkość. Poniższe rozwiązania wydają się obracać wokółscope()
rtpHarry
@rtpHarry ma rację. Odpowiedzi poniżej, które wymagają użycia funkcji scope (), są nieaktualne. Zobacz moją odpowiedź tutaj stackoverflow.com/a/34078750/319302
Cagatay Kalan

Odpowiedzi:

227

Musisz użyć $ scope. $ Apply (), jeśli chcesz wprowadzić jakiekolwiek zmiany w wartości zasięgu spoza kontroli angularjs, takich jak obsługa zdarzeń jquery / javascript.

function change() {
    alert("a");
    var scope = angular.element($("#outer")).scope();
    scope.$apply(function(){
        scope.msg = 'Superhero';
    })
}

Demo: Fiddle

Arun P Johny
źródło
2
@ dk123 angular.element("#scope")nie działa, chociaż angular.element($("#scope"))działa, musisz mieć również jquery
Arun P Johny
1
Wiem, że minęło trochę czasu, ale mam nadzieję, że niektórzy mogą mi odpowiedzieć na ten temat ... Dlaczego var scope = angular.element ($ ("# external")). Scope (); muszą być zadeklarowane wewnątrz funkcji change? Jeśli przeniosę go do globalnej przestrzeni, to nie ma wyjścia?
Marc M.
1
@MarcM. Myślę, że ma to związek z odtwarzaniem zakresu Angulara. Do czasu korzystania z funkcji zmiany poprzedni zakres wskazywany przez zmienną globalną może już nie istnieć (z powodu odtwarzania).
dk123
1
angular.element ($ ("div [ng-controller = 'myCtrl']")). scope (); jest lepsze niż dodatkowy #outer w elemencie div, myślę, że
wyverny
1
scope () staje się niezdefiniowane, gdy to zrobimy: $ compileProvider.debugInfoEnabled (false); w kątowym. Jak więc sprawić, by działał z debuginfoEnabled false?
Agnosco
26

Minęło trochę czasu, odkąd opublikowałem to pytanie, ale biorąc pod uwagę opinie, które nadal wydaje się mieć, oto kolejne rozwiązanie, na które natknąłem się w ciągu ostatnich kilku miesięcy:

$scope.safeApply = function( fn ) {
    var phase = this.$root.$$phase;
    if(phase == '$apply' || phase == '$digest') {
        if(fn) {
            fn();
        }
    } else {
        this.$apply(fn);
    }
};

Powyższy kod tworzy w zasadzie funkcję o nazwie safeApplyże Calles $applyfunkcyjne (jak wskazano w odpowiedzi na Arun) wtedy i tylko kątowe obecnie nie przeżywa na $digestscenie. Z drugiej strony, jeśli Angular aktualnie przetrawia rzeczy, po prostu wykona funkcję tak, jak jest, ponieważ wystarczy to, aby zasygnalizować Angularowi, aby wprowadził zmiany.

Podczas próby użycia $applyfunkcji, gdy AngularJs jest obecnie na swoim $digestetapie, występuje wiele błędów . PliksafeApply kod jest bezpiecznym opakowaniem, które zapobiega takim błędom.

(uwaga: osobiście lubię się wtrącać safeApplyjako funkcję$rootScope dla wygody)

Przykład:

function change() {
    alert("a");
    var scope = angular.element($("#outer")).scope();
    scope.safeApply(function(){
        scope.msg = 'Superhero';
    })
}

Demo: http://jsfiddle.net/sXkjc/227/

dk123
źródło
1
Dlaczego funkcja safeApply działa? Wygląda na to, że mówisz „wykonaj funkcję samodzielnie, jeśli Angular znajduje się na etapach $ apply lub $ digest, w przeciwnym razie użyj $ apply (), aby zastosować funkcję” .... Ale jeśli wykonujesz funkcję samodzielnie ... .. jak to aktualizuje jakiekolwiek modele? Wydaje się, że nie byłoby to korzystne zachowanie, chyba że dzieje się coś, o czym nie wiem. Czy jakiś mechanizm w Angular przeszukuje $ scope pod kątem zmian, które mogły mieć miejsce bezpośrednio w nim ???
trusktr
Dodatkowo, jeśli chcesz zabezpieczyć się przed tymi stanami, rozważę błąd w metodzie $ apply (), który należy naprawić.
trusktr
@trusktr Z tego, co rozumiem, normalnie wykonywanie funkcji jest wychwytywane przez kąt, jeśli funkcja zmienia jakiekolwiek modele, a zatem kątowy aktualizuje je w następnym etapie podsumowania.
dk123
@trusktr Zgodziłbym się jednak, że gdyby można było zastosować zwykłą $ apply () bez zabezpieczeń, nie byłoby nic lepszego. W istocie jedynym celem safeApply jest zabezpieczenie przed błędami $ apply (). Nie jestem jednak pewien, czy był to zgłoszony problem, a teraz naprawiony, czy nadal trwa.
dk123
1
Tylko dlatego, że się o to natknąłem : github.com/angular/angular.js/wiki/When-to-use-$scope.$apply () . _Jeśli robisz if (! $ Scope. $$ phase) $ scope. $ Apply () to dlatego, że nie jesteś wystarczająco wysoko na stosie wywołań._
scheffield
17

Innym sposobem na to jest:

var extScope;
var app = angular.module('myApp', []);
app.controller('myController',function($scope, $http){
    extScope = $scope;
})
//below you do what you want to do with $scope as extScope
extScope.$apply(function(){
    extScope.test = 'Hello world';
})
Czarleston
źródło
1
Nadal nie rozumiem, dlaczego ten komentarz nie jest najlepszą odpowiedzią na to. Po kilku dniach kopania w Internecie, z frustracją i złością, w końcu to rozwiązało mój problem. Dziękuję @Charleston. Jesteś wspaniały, panie!
Rajkumar
13

możemy to nazwać po załadowaniu

http://jsfiddle.net/gentletech/s3qtv/3/

<div id="wrap" ng-controller="Ctrl">
    {{message}}<br>
    {{info}}
    </div>
    <a  onClick="hi()">click me </a>

    function Ctrl($scope) {
        $scope.message = "hi robi";
        $scope.updateMessage = function(_s){
            $scope.message = _s;    
        };
    }

function hi(){
    var scope = angular.element(document.getElementById("wrap")).scope();
        scope.$apply(function() {
        scope.info = "nami";
        scope.updateMessage("i am new fans like nami");
    });
}
fallwind
źródło
8

Minęło dużo czasu, odkąd zadałem to pytanie, ale oto odpowiedź, która nie wymaga jquery:

function change() {
    var scope = angular.element(document.querySelector('#outside')).scope();
    scope.$apply(function(){
        scope.msg = 'Superhero';
    })
}
dk123
źródło
3

Oto rozwiązanie wielokrotnego użytku: http://jsfiddle.net/flobar/r28b0gmq/

function accessScope(node, func) {
    var scope = angular.element(document.querySelector(node)).scope();
    scope.$apply(func);
}

window.onload = function () {

    accessScope('#outer', function (scope) {
        // change any property inside the scope
        scope.name = 'John';
        scope.sname = 'Doe';
        scope.msg = 'Superhero';
    });

};
flobar
źródło
2

Możesz też spróbować:

function change() {
    var scope = angular.element( document.getElementById('outer') ).scope();
    scope.$apply(function(){
        scope.msg = 'Superhero';
    })
}
harish sharma
źródło
@ dk123 to również dawka nie wymaga JQuery.
harish sharma
1

Przyjęta odpowiedź jest świetna. Chciałem przyjrzeć się, co dzieje się z teleskopem Angular w kontekście ng-repeat. Chodzi o to, że Angular utworzy podzakres dla każdego powtarzającego się elementu. Podczas wywoływania metody zdefiniowanej w oryginale $scope, która zachowuje swoją pierwotną wartość (z powodu zamknięcia javascript). Jednak thisodwołuje się do zakresu / obiektu wywołującego. To działa dobrze, o ile masz pewność, kiedy $scopeithis są takie same i gdy są one różne. hth

Oto skrzypce, które ilustrują różnicę: https://jsfiddle.net/creitzel/oxsxjcyc/

Charlie
źródło
1

Jestem nowicjuszem, więc przepraszam, jeśli to zła praktyka. Na podstawie wybranej odpowiedzi wykonałem tę funkcję:

function x_apply(selector, variable, value) {
    var scope = angular.element( $(selector) ).scope();
    scope.$apply(function(){
        scope[variable] = value;
    });
}

Używam tego w ten sposób:

x_apply('#fileuploader', 'thereisfiles', true);

Przy okazji, przepraszam za mój angielski

MrQwerty
źródło
0
<input type="text" class="form-control timepicker2" ng-model='programRow.StationAuxiliaryTime.ST88' />

dostęp do wartości zakresu

załóżmy, że programRow.StationAuxiliaryTime jest tablicą obiektów

 $('.timepicker2').on('click', function () 
    {
            var currentElement = $(this);

            var scopeValues = angular.element(currentElement).scope();
            var model = currentElement.attr('ng-model');
            var stationNumber = model.split('.')[2];
            var val = '';
            if (model.indexOf("StationWaterTime") > 0) {
                val = scopeValues.programRow.StationWaterTime[stationNumber];
            }
            else {
                val = scopeValues.programRow.StationAuxiliaryTime[stationNumber];
            }
            currentElement.timepicker('setTime', val);
        });
Sreeraj
źródło
0

Musimy użyć wbudowanej funkcji Angular Js $ apply do zmiennych zakresu dostępu lub funkcji poza funkcją kontrolera.

Można to zrobić na dwa sposoby:

| * | Metoda 1: użycie identyfikatora:

<div id="nameNgsDivUid" ng-app="">
    <a onclick="actNgsFnc()"> Activate Angular Scope</a><br><br>
    {{ nameNgsVar }}
</div>

<script type="text/javascript">

    var nameNgsDivVar = document.getElementById('nameNgsDivUid')

    function actNgsFnc()
    {
        var scopeNgsVar = angular.element(nameNgsDivVar).scope();
        scopeNgsVar.$apply(function()
        {
            scopeNgsVar.nameNgsVar = "Tst Txt";
        })
    }

</script>

| * | Metoda 2: Używanie init kontrolera ng:

<div ng-app="nameNgsApp" ng-controller="nameNgsCtl">
    <a onclick="actNgsFnc()"> Activate Angular Scope</a><br><br>
    {{ nameNgsVar }}
</div>

<script type="text/javascript">

    var scopeNgsVar;
    var nameNgsAppVar=angular.module("nameNgsApp",[])
    nameNgsAppVar.controller("nameNgsCtl",function($scope)
    {
        scopeNgsVar=$scope;
    })

    function actNgsFnc()
    {
        scopeNgsVar.$apply(function()
        {
            scopeNgsVar.nameNgsVar = "Tst Txt";
        })
    }

</script>
Sujay UN
źródło
0

Tak zrobiłem dla mojego CRUDManager classzainicjalizowanego w kontrolerze Angulara, który później przeszedł do zdarzenia kliknięcia przycisku jQuery zdefiniowanego poza kontrolerem :

W kontrolerze kątowym:

        // Note that I can even pass over the $scope to my CRUDManager's constructor.
        var crudManager = new CRUDManager($scope, contextData, opMode);

        crudManager.initialize()
            .then(() => {
                crudManager.dataBind();
                $scope.crudManager = crudManager;
                $scope.$apply();
            })
            .catch(error => {
                alert(error);
            });

W jQuery Save button click event poza kontrolerem:

    $(document).on("click", "#ElementWithNgControllerDefined #btnSave", function () {
        var ngScope = angular.element($("#ElementWithNgControllerDefined")).scope();
        var crudManager = ngScope.crudManager;
        crudManager.saveData()
            .then(finalData => {
               alert("Successfully saved!");
            })
            .catch(error => {
               alert("Failed to save.");
            });
    });

Jest to szczególnie ważne i przydatne, gdy zdarzenia jQuery muszą być umieszczone POZA KONTROLEREM , aby zapobiec dwukrotnemu uruchomieniu .

Antonio Ooi
źródło