Ángulo de acceso AngularJS desde fuera de la función js

131

Estoy tratando de ver si hay una manera simple de acceder al alcance interno de un controlador a través de una función externa de JavaScript (completamente irrelevante para el controlador de destino)

He visto en un par de otras preguntas aquí que

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

recuperaría el alcance de un elemento DOM, pero mis intentos actualmente no están dando resultados adecuados.

Aquí está el jsfiddle: http://jsfiddle.net/sXkjc/5/

Actualmente estoy pasando por una transición de JS simple a Angular. La razón principal por la que estoy tratando de lograr esto es mantener mi código de biblioteca original intacto tanto como sea posible; ahorrando la necesidad de agregar cada función al controlador.

¿Alguna idea sobre cómo podría lograr esto? Los comentarios sobre el violín anterior también son bienvenidos.

dk123
fuente
Para su información, según los documentos, es .scope()necesario que los Datos de depuración estén habilitados, pero no se recomienda el uso de Datos de depuración en producción por razones de velocidad. Las siguientes soluciones parecen girar en torno ascope()
rtpHarry
@rtpHarry tiene razón. Las respuestas a continuación que requieren el uso de scope () están en desuso. Vea mi respuesta aquí stackoverflow.com/a/34078750/319302
Cagatay Kalan

Respuestas:

223

Debe usar $ scope. $ Apply () si desea realizar cambios en un valor de alcance desde fuera del control de angularjs como un controlador de eventos jquery / javascript.

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

Demostración: Fiddle

Arun P Johny
fuente
2
@ dk123 angular.element("#scope")no funciona, aunque sí angular.element($("#scope"))funciona, también debe tener jquery
Arun P Johny
1
Sé que ha pasado un tiempo, pero espero que algunos puedan responderme sobre esto ... ¿Por qué var scope = angular.element ($ ("# external")). Scope (); tiene que ser declarado dentro de la función de cambio? Si lo muevo al espacio global, ¿no es posible?
Marc M.
1
@MarcM. Creo que tiene que ver con la recreación de alcance de Angular. En el momento en que usa la función de cambio, el alcance anterior al que apuntaba la var global puede que ya no exista (debido a la recreación).
dk123
1
angular.element ($ ("div [ng-controller = 'myCtrl']")). scope (); es mejor que #outer adicional en el elemento div, supongo
wyverny
1
el alcance () se vuelve indefinido cuando lo hacemos: $ compileProvider.debugInfoEnabled (falso); en angular Entonces, ¿cómo podemos hacer que funcione con debuginfoEnabled false?
Agnosco
26

Ha pasado un tiempo desde que publiqué esta pregunta, pero teniendo en cuenta las opiniones que todavía parece tener, aquí hay otra solución que he encontrado durante estos últimos meses:

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

El código anterior básicamente crea una función llamada safeApplyque llama a la $applyfunción (como se indica en la respuesta de Arun) si y solo Angular actualmente no está pasando por la $digestetapa. Por otro lado, si Angular actualmente está digiriendo cosas, simplemente ejecutará la función tal como está, ya que eso será suficiente para indicarle a Angular que realice los cambios.

Se producen numerosos errores al intentar utilizar la $applyfunción mientras AngularJs se encuentra actualmente en su $digestetapa. El safeApplycódigo anterior es un contenedor seguro para evitar tales errores.

(nota: personalmente me gusta tirar en safeApplyfunción de $rootScopepara fines de conveniencia)

Ejemplo:

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

Demostración: http://jsfiddle.net/sXkjc/227/

dk123
fuente
1
¿Por qué funciona su función safeApply? Parece que lo que está diciendo es "ejecutar la función por sí mismo si Angular está en las etapas $ apply o $ digest, de lo contrario, use $ apply () para aplicar la función" ... Pero si ejecuta la función por sí misma ... .. ¿Cómo actualiza eso algún modelo? Parece que eso no sería un comportamiento favorable, a menos que ocurra algo que no conozca. ¿Algún mecanismo en Angular va a sondear el $ alcance de los cambios que pueden haberle sucedido directamente?
trusktr
Además, si necesita protegerse contra esos estados, entonces consideraría que hay un error del método $ apply () que debe corregirse.
trusktr
@trusktr Por lo que entiendo, la ejecución de la función normalmente es capturada por angular si la función cambia algún modelo y, por lo tanto, angular los actualiza en la siguiente etapa de resumen.
dk123
@trusktr Sin embargo, estoy de acuerdo en que si el $ apply () normal se puede aplicar sin las garantías, no habría nada mejor. En esencia, el único propósito de safeApply es proteger contra los errores $ apply (). Sin embargo, no estoy seguro de si se trata de un problema informado y ahora solucionado, o si aún es un problema continuo.
dk123
1
Solo porque me tropecé con él: github.com/angular/angular.js/wiki/When-to-use-$scope.$apply () . _Si está haciendo if (! $ Scope. $$ phase) $ scope. $ Apply () es porque no está lo suficientemente alto en la pila de llamadas._
scheffield
17

Otra forma de hacerlo es:

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';
})
charlestón
fuente
1
Todavía no entiendo por qué este comentario no es la mejor respuesta para esto. Después de excavar en Internet durante un par de días, con frustración y enojo, finalmente esto fue lo que resolvió mi problema. Gracias @Charleston. ¡Eres genial, señor!
Rajkumar
13

podemos llamarlo después de cargar

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");
    });
}
viento de cola
fuente
8

Ha pasado mucho tiempo desde que hice esta pregunta, pero aquí hay una respuesta que no requiere jquery:

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

Aquí hay una solución reutilizable: 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
fuente
2

También puedes probar:

function change() {
    var scope = angular.element( document.getElementById('outer') ).scope();
    scope.$apply(function(){
        scope.msg = 'Superhero';
    })
}
harish sharma
fuente
@ dk123 esto también no requiere JQuery.
harish sharma
1

La respuesta aceptada es genial. Quería ver qué sucede con el alcance angular en el contexto de ng-repeat. La cuestión es que Angular creará un sub-alcance para cada elemento repetido. Al llamar a un método definido en el original $scope, que conserva su valor original (debido al cierre de JavaScript). Sin embargo, el thishace referencia al alcance / objeto de llamada. Esto funciona bien, siempre y cuando tenga claro cuándo $scopey si thisson iguales y cuándo son diferentes. hth

Aquí hay un violín que ilustra la diferencia: https://jsfiddle.net/creitzel/oxsxjcyc/

Charlie
fuente
1

Soy novato, lo siento si es una mala práctica. Basado en la respuesta elegida, hice esta función:

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

Lo estoy usando de esta manera:

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

Por cierto, perdón por mi inglés

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

acceso al valor del alcance

suponga que programRow.StationAuxiliaryTime es una matriz de objetos

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

Necesitamos usar la función incorporada Angular Js $ apply para acceder a variables de alcance o funciones fuera de la función del controlador.

Esto se puede hacer de dos maneras :

| * | Método 1: Usar 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étodo 2: Usando init del controlador 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
fuente