Cómo requerir un controlador en una directiva angularjs

86

¿Alguien puede decirme cómo incluir un controlador de una directiva en otra directiva angularJS? por ejemplo tengo el siguiente código

var app = angular.module('shop', []).
config(['$routeProvider', function ($routeProvider) {
    $routeProvider.when('/', {
        templateUrl: '/js/partials/home.html'
    })
        .when('/products', {
        controller: 'ProductsController',
        templateUrl: '/js/partials/products.html'
    })
        .when('/products/:productId', {
        controller: 'ProductController',
        templateUrl: '/js/partials/product.html'
    });
}]);

app.directive('mainCtrl', function () {
    return {
        controller: function ($scope) {}
    };
});

app.directive('addProduct', function () {
    return {
        restrict: 'C',
        require: '^mainCtrl',
        link: function (scope, lElement, attrs, mainCtrl) {
            //console.log(cartController);
        }
    };
});

Según todas las cuentas, debería poder acceder al controlador en la directiva addProduct, pero no lo soy. ¿Existe una mejor manera de hacer esto?

Le Garden Fox
fuente
5
requireasegura la presencia de otra directiva y luego incluye su controlador. ^requirecomprueba los elementos por encima del actual además del elemento actual. Por lo tanto, debe usar las dos directivas juntas para que esto funcione. De lo contrario, simplemente defina un controlador con app.controllery luego úselo en ambas directivas. De cualquier manera, ¿puedes poner esto en un Plunker simple junto con tu código HTML?
Josh David Miller

Respuestas:

187

Tuve suerte y respondí esto en un comentario a la pregunta, pero estoy publicando una respuesta completa en aras de la integridad y para que podamos marcar esta pregunta como "Respondida".


Depende de lo que desee lograr al compartir un controlador; puede compartir el mismo controlador (aunque tenga diferentes instancias), o puede compartir la misma instancia de controlador.

Compartir un controlador

Dos directivas pueden usar el mismo controlador pasando el mismo método a dos directivas, así:

app.controller( 'MyCtrl', function ( $scope ) {
  // do stuff...
});

app.directive( 'directiveOne', function () {
  return {
    controller: 'MyCtrl'
  };
});

app.directive( 'directiveTwo', function () {
  return {
    controller: 'MyCtrl'
  };
});

Cada directiva obtendrá su propia instancia del controlador, pero esto le permite compartir la lógica entre tantos componentes como desee.

Requerir un controlador

Si desea compartir la misma instancia de un controlador, utilice require.

requireasegura la presencia de otra directiva y luego incluye su controlador como parámetro para la función de enlace. Entonces, si tiene dos directivas en un elemento, su directiva puede requerir la presencia de la otra directiva y obtener acceso a sus métodos de controlador. Un caso de uso común para esto es require ngModel.

^require, con la adición del signo de intercalación, comprueba los elementos anteriores a la directiva además del elemento actual para tratar de encontrar la otra directiva. Esto le permite crear componentes complejos donde los "subcomponentes" pueden comunicarse con el componente principal a través de su controlador con gran efecto. Los ejemplos podrían incluir pestañas, donde cada panel puede comunicarse con las pestañas generales para manejar el cambio; un juego de acordeón podría asegurar que solo uno esté abierto a la vez; etc.

En cualquier caso, debe utilizar las dos directivas juntas para que esto funcione. requirees una forma de comunicarse entre componentes.

Consulte la página de la Guía de directivas para obtener más información: http://docs.angularjs.org/guide/directive

Josh David Miller
fuente
4
¿Es posible requerir un controlador de directiva de hermanos? Básicamente, necesito compartir la misma instancia de un controlador o servicio entre directivas de hermanos (como en los hermanos DOM, no en el mismo elemento DOM) que se repite usando ng-repeat. Imagine que cada elemento repetido tiene una directiva que necesita un estado o lógica compartida entre ellos.
CMCDragonkai
2
@CMCDragonkai No hay forma de hacer eso, pero hay dos formas comunes de lograr lo mismo. La primera es que si los hermanos son todos del mismo "tipo", entonces el elemento sobre ngRepeat puede ser como una directiva de contenedor y todos los subelementos pueden requerir esa directiva en su lugar, todos compartiendo el mismo controlador. La solución más común, y a menudo más canónica, es utilizar un servicio compartido. ¿Puede explicar qué hacen estos hermanos y qué necesitan compartir?
Josh David Miller
Sí terminó haciendo la primera opción. Usando un controlador de directiva de contenedor. Funciona genial. Es para mampostería.
CMCDragonkai
Esta es una gran respuesta y ha solidificado mi comprensión de cómo funciona todo esto. ¡Gracias! (Como nota, esta puede ser una característica más nueva, pero puede usarla requirepara especificar una sola directiva, o una matriz de directivas; cada directiva puede tener un prefijo de intercalación ( ^) para requisitos más granulares)
jedd.ahyoung
Usar el mismo controlador en dos directivas no le da a cada directiva su propia instancia.
jsbisht
27

Hay una buena respuesta de stackoverflow aquí por Mark Rajcok:

Controladores de directiva AngularJS que requieren controladores de directiva principal

con un enlace a este jsFiddle muy claro: http://jsfiddle.net/mrajcok/StXFK/

<div ng-controller="MyCtrl">
    <div screen>
        <div component>
            <div widget>
                <button ng-click="widgetIt()">Woo Hoo</button>
            </div>
        </div>
    </div>
</div>

JavaScript

var myApp = angular.module('myApp',[])

.directive('screen', function() {
    return {
        scope: true,
        controller: function() {
            this.doSomethingScreeny = function() {
                alert("screeny!");
            }
        }
    }
})

.directive('component', function() {
    return {
        scope: true,
        require: '^screen',
        controller: function($scope) {
            this.componentFunction = function() {
                $scope.screenCtrl.doSomethingScreeny();
            }
        },
        link: function(scope, element, attrs, screenCtrl) {
            scope.screenCtrl = screenCtrl
        }
    }
})

.directive('widget', function() {
    return {
        scope: true,
        require: "^component",
        link: function(scope, element, attrs, componentCtrl) {
            scope.widgetIt = function() {
                componentCtrl.componentFunction();
            };
        }
    }
})


//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});

function MyCtrl($scope) {
    $scope.name = 'Superhero';
}
Joseph Oster
fuente
4
Para mí, lo que hizo que el ejemplo de Mark Rajcok hiciera más clic fue prestar atención a cómo se crean los métodos del controlador. Por lo general, verá métodos de controlador creados a través de $ scope.methodName = function () {...}, pero para que esto funcione, debe usar this.methodName para los métodos a los que desea acceder. No me di cuenta de eso al principio.
coblr