2013-05-02 2 views
38

Sto cercando di implementare un sistema di plugin in angularjs che consentirebbe agli utenti di configurare quali "widget" vedranno su una determinata pagina. Ogni widget è definito da un controller e un modello (url). È possibile creare una direttiva che crea un'istanza di un controller, la invoca con un modello e include il contenuto risultante?AngularJS Render Controller e Template

L'obiettivo è qualcosa di simile:

<div class="widget" ng-repeat="widget in widgets"> 
    <widget controller="widget.controller" templateUrl="widget.templateUrl"></widget> 
</div> 
+1

Questo è essenzialmente ciò che 'ngView' fa; anche se il tuo caso d'uso è un po 'più semplice, potresti trovare utile il suo [codice sorgente] (https://github.com/angular/angular.js/blob/master/src/ng/directive/ngView.js). Fondamentalmente, si recupera, si aggiunge al DOM, quindi si compila il modello in $ e poi si assegna il controller: 'element.children(). Data ('$ ngControllerController', controller);'. Se avrò tempo più tardi oggi posterò una risposta più completa. –

risposta

72

Ci sono due modi per farlo; uno usa le direttive helper già disponibili (come ngInclude e ngController) e il secondo è manuale; la versione manuale potrebbe essere essere più veloce, ma non posso essere sicuro.

Il modo semplice:

Il metodo più semplice è quello semplice creare un nuovo elemento con ngController e ngInclude attributi, aggiungerlo alla elemento della direttiva, e quindi $compile esso:

var html = '<div ng-controller="'+ctrl+'" ng-include="'+tpl+'"></div>'; 
element.append(html); 
$compile(element.contents())(scope); 

The Manual Way:

Il modo manuale è quello di fare ciò che queste direttive si farebbero da soli fare a turno; questa logica è molto simile a ciò che fa ngView (sebbene senza la complessità). Preleviamo il modello, lo memorizziamo in $templateCache e quindi lo aggiungiamo al DOM. Creiamo un nuovo ambito figlio e istanziamo il controller fornito con esso e assegniamo quel controller all'elemento. Infine, $compile esso:

$http.get(tpl, { cache: $templateCache }) 
.then(function(response) { 
    templateScope = scope.$new(); 
    templateCtrl = $controller(ctrl, { $scope: templateScope }); 
    element.html(response.data); 
    element.children().data('$ngControllerController', templateCtrl); 
    $compile(element.contents())(templateScope); 
}); 

(Si noti che non v'è alcuna raccolta dei rifiuti qui, che si avrebbe bisogno di attuare, se i widget cambiano)

Ecco un Plunker dimostrando entrambi i metodi: http://plnkr.co/edit/C7x9C5JgUuT1yk0mBUmE?p=preview

+0

Questa sembra una grande soluzione, è chiaro e logico, il problema con il 'modo semplice' è che il ng-include non viene valutato, carica il controller :) – perrohunter

+1

Questa riga è ottima: '$ http.get (tpl, {cache: $ templateCache}) .then (funzione (risposta) {...) '. Grazie! – SimplGy

+0

Questo non funziona per me a meno che non chiamo 'scope. $ Apply()' – Simone