Portée d'accès AngularJS depuis l'extérieur de la fonction js

131

J'essaie de voir s'il existe un moyen simple d'accéder à la portée interne d'un contrôleur via une fonction javascript externe (totalement sans rapport avec le contrôleur cible)

J'ai vu sur quelques autres questions ici que

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

récupérerait la portée d'un élément DOM, mais mes tentatives ne donnent actuellement aucun résultat correct.

Voici le jsfiddle: http://jsfiddle.net/sXkjc/5/

Je suis actuellement en train de passer de JS simple à Angular. La principale raison pour laquelle j'essaie d'y parvenir est de garder le plus possible le code de ma bibliothèque d'origine; économisant la nécessité pour moi d'ajouter chaque fonction au contrôleur.

Des idées sur la façon dont je pourrais y parvenir? Les commentaires sur le violon ci-dessus sont également les bienvenus.

dk123
la source
Pour info, selon la documentation, l'utilisation .scope()des données de débogage doit être activée, mais l' utilisation des données de débogage en production n'est pas recommandée pour des raisons de vitesse. Les solutions ci-dessous semblent tourner autourscope()
rtpHarry
@rtpHarry a raison. Les réponses ci-dessous qui nécessitent l'utilisation de scope () sont obsolètes. Voir ma réponse ici stackoverflow.com/a/34078750/319302
Cagatay Kalan

Réponses:

223

Vous devez utiliser $ scope. $ Apply () si vous souhaitez apporter des modifications à une valeur de portée hors du contrôle d'angularjs, comme un gestionnaire d'événements jquery / javascript.

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

Démo: Fiddle

Arun P Johny
la source
2
@ dk123 angular.element("#scope")ne fonctionne pas, mais angular.element($("#scope"))fonctionne, vous devez également avoir jquery
Arun P Johny
1
Je sais que cela fait un moment, mais j'espère que certains pourront me répondre à ce sujet ... Pourquoi var scope = angular.element ($ ("# external")). Scope (); doit être déclaré dans la fonction de changement? Si je le déplace dans l'espace mondial, c'est interdit?
Marc M.
1
@MarcM. Je pense que cela a à voir avec la récréation de la portée d'Angular. Au moment où vous utilisez la fonction de changement, la portée précédente vers laquelle pointait la variable globale peut ne plus exister (en raison de la recréation).
dk123
1
angular.element ($ ("div [ng-controller = 'myCtrl']")). scope (); est meilleur que #outer supplémentaire dans l'élément div, je suppose
wyverny
1
scope () devient indéfini lorsque nous faisons: $ compileProvider.debugInfoEnabled (false); en angulaire. Alors, comment pouvons-nous le faire fonctionner avec debuginfoEnabled false?
Agnosco
26

Cela fait un moment que j'ai posté cette question, mais compte tenu des opinions que cela semble encore obtenir, voici une autre solution que j'ai trouvée au cours de ces derniers mois:

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

Le code ci-dessus crée essentiellement une fonction appelée safeApplyqui appelle la $applyfonction (comme indiqué dans la réponse d'Arun) si et seulement Angular ne passe actuellement pas par l' $digestétape. D'un autre côté, si Angular est en train de digérer les choses, il exécutera simplement la fonction telle quelle, car cela suffira à signaler à Angular d'effectuer les modifications.

De nombreuses erreurs se produisent lorsque vous essayez d'utiliser la $applyfonction alors qu'AngularJs en est actuellement à sa $digestphase. Le safeApplycode ci-dessus est un wrapper sûr pour éviter de telles erreurs.

(Remarque: personnellement, j'aime jouer en safeApplyfonction de $rootScopepour des raisons de commodité)

Exemple:

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

Démo: http://jsfiddle.net/sXkjc/227/

dk123
la source
1
Pourquoi votre fonction safeApply fonctionne-t-elle? On dirait que ce que vous dites est "exécutez la fonction par lui-même si Angular est dans les étapes $ apply ou $ digest, sinon utilisez $ apply () pour appliquer la fonction" .... Mais si vous exécutez la fonction par elle-même .. .. comment cela met-il à jour les modèles? Il semble que ce ne serait pas un comportement favorable, à moins qu'il se passe quelque chose que je ne sais pas. Un mécanisme dans Angular va-t-il interroger la portée $ pour les changements qui auraient pu lui arriver directement ???
trusktr
De plus, si vous avez besoin de vous protéger contre ces états, alors je considérerais qu'un bogue de la méthode $ apply () doit être corrigé.
trusktr
@trusktr D'après ce que je comprends, l'exécution de la fonction normalement est interceptée par angular si la fonction change des modèles, et donc angular les met à jour à la prochaine étape de digestion.
dk123
@trusktr Je conviens cependant que si le $ apply () normal peut être appliqué sans les sauvegardes, il n'y aurait rien de mieux. En substance, le seul but de safeApply est de se prémunir contre les erreurs $ apply (). Je ne sais pas si c'était un problème signalé et maintenant résolu, ou toujours en cours.
dk123
1
Juste parce que je suis tombé dessus: github.com/angular/angular.js/wiki/When-to-use-$scope.$apply () . _Si vous faites if (! $ Scope. $$ phase) $ scope. $ Apply () c'est parce que vous n'êtes pas assez haut dans la pile d'appels._
scheffield
17

Une autre façon de faire est:

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';
})
Charleston
la source
1
Je ne comprends toujours pas pourquoi ce commentaire n'est pas la meilleure réponse à cela. Après avoir creusé Internet pendant quelques jours, avec frustration et colère, c'est finalement ce qui a résolu mon problème. Merci @Charleston. Vous êtes génial, monsieur!
Rajkumar
13

nous pouvons l'appeler après avoir chargé

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
la source
8

Cela fait longtemps que je n'ai pas posé cette question, mais voici une réponse qui ne nécessite pas jquery:

function change() {
    var scope = angular.element(document.querySelector('#outside')).scope();
    scope.$apply(function(){
        scope.msg = 'Superhero';
    })
}
dk123
la source
3

Voici une solution réutilisable: 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
la source
2

Vous pouvez également essayer:

function change() {
    var scope = angular.element( document.getElementById('outer') ).scope();
    scope.$apply(function(){
        scope.msg = 'Superhero';
    })
}
harish sharma
la source
@ dk123 cela ne nécessite pas non plus JQuery.
harish sharma
1

La réponse acceptée est excellente. Je voulais regarder ce qui arrive à la lunette angulaire dans le contexte de ng-repeat. Le fait est qu'Angular créera une sous-portée pour chaque élément répété. Lors de l'appel à une méthode définie sur l'original $scope, qui conserve sa valeur d'origine (en raison de la fermeture de javascript). Cependant, le thisfait référence à la portée / à l'objet appelant. Cela fonctionne bien, tant que vous savez clairement quand $scopeet thissont les mêmes et quand ils sont différents. hth

Voici un violon qui illustre la différence: https://jsfiddle.net/creitzel/oxsxjcyc/

Charlie
la source
1

Je suis débutant, désolé si c'est une mauvaise pratique. Sur la base de la réponse choisie, j'ai fait cette fonction:

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

Je l'utilise de cette façon:

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

Au fait, désolé pour mon anglais

MrQwerty
la source
0
<input type="text" class="form-control timepicker2" ng-model='programRow.StationAuxiliaryTime.ST88' />

accès à la valeur de l'étendue

supposons que programRow.StationAuxiliaryTime est un tableau d'objets

 $('.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
la source
0

Nous devons utiliser la fonction intégrée Angular Js $ apply pour accéder aux variables de portée ou aux fonctions en dehors de la fonction de contrôleur.

Ceci peut être fait de deux façons :

| * | Méthode 1: Utilisation de l'ID:

<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>

| * | Méthode 2: Utilisation de l'init de ng-controller:

<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
la source