2013-08-12 11 views
45

Ho lavorato su un progetto che è più simile a un framework e ha diverse app/moduli che è possibile installare. Guardalo come un appstore di base o in un negozio google.play. È una specie di applicazione intranet e tutti i moduli possono essere aggiunti al tuo account utente.AngularJS: come nidificare le applicazioni all'interno di un'app angolare

il framework è già in fase di sviluppo, ma ora mi sto occupando dell'idea di applicazioni/moduli. (link to a proof of concept in development, can be found here)

un'applicazione deve essere in qualche modo autonomo, e non in grado di includere improvvisamente script dal quadro, questo è perfettamente possibile da loro strutturazione in moduli separati in questo modo:

angular.module('myApp', []); 

tuttavia, un'applicazione può avere modelli, script, css e può essere eseguito su un server separato, quindi sto cercando il modo migliore per recuperare lo script (s) e cssfile (s) e caricarli dinamicamente nell'app quando l'utente avvia il app dentro dal framework.

  • Attualmente sto strutturando le applicazioni come se avessero un modello principale, ad esempio www.framework.com/apps/myapp/views/app.html, per semplicità i raggrupparli in fasci script in 1 file di script per ogni applicazione, per cui v'è anche un www.framework.com/apps/myapp/script.js di essere inclusi.

Il framework contiene un modello che carica le app e un appController. Il modello contiene questo pezzo:

<div data-ng-controller="AppController" data-ng-include="app.appTemplate"> 
    <div>loading...</div> 
</div> 

questo si lega essenzialmente alla $scope.app.appTemplate che viene aggiornato quando tutti gli script vengono caricati, quindi prima si vede un modello di carico, più tardi, dopo gli script sono inclusi nella pagina aggiorna il app.appTemplate al modello principale sopra citato di un'applicazione.

durante il caricamento del primo modello di indice funziona, questo modello è attualmente caricato con il AppController dal framework, quindi sta utilizzando il $scope del framework e non è il proprio script.

devo ancora iniziare in qualche modo proprio modulo angolare della app, e lasciare che su di essa la propria senza correre nulla nel quadro di 'farla funzionare'

Sto ancora cercando di capire come meglio carico i file javascript dipendenti (probabilmente utilizzare requrejs o altro dipendenza loader) ma non ho attualmente idea di come 'stivale' l'applicazione senza lavorare dall'interno AppController

EDIT del quadro

Ho creato un piccolo progetto dimostrativo per mostrare i problemi a portata di mano, full code is visible at git-hub nel momento in cui questo progetto fa alcune cose programmate, l'idea sarebbe quella di rendere quelli meno codificati quando ho la prova del concetto giusto, ora è tutto sul caricamento delle applicazioni all'interno del framework. se è possibile, posso pensare a dove ottenere i nomi di URL e applicazioni da ...

risposta

37

Puoi avviare un modulo all'interno di un altro modulo bootstrap. Bootstrapping compila la vista e associa un rootScope ad esso, attraversando il suo percorso attraverso il DOM e impostando i bind di scope ed eseguendo le funzioni di collegamento della direttiva fino in fondo. Se lo fai due volte, avrai dei problemi.

Probabilmente dovresti ripensare la tua architettura. Penso che forse la parola "modulo" o "app" per quanto riguarda Angular sia un termine improprio e ti stia conducendo sulla strada sbagliata.

Ogni "app installata dall'utente" nella tua applicazione dovrebbe probabilmente essere controllata da un controller nel modulo dell'app o registrata in un modulo a cui fa riferimento il modulo dell'app. Quindi non dovresti "avviare più app", dovresti solo avviarne uno, fare riferimento agli altri moduli, quindi utilizzare Controller da quei moduli per controllare parti della tua visualizzazione sullo schermo.

Quello che faresti è quando è stato installato un nuovo "widget", è necessario registrare il suo file di modulo (.js) con il sistema, che conterrà un controller chiamato WidgetCtrl, quindi quando la pagina verrà caricata, tu " d fai riferimento al modulo del widget sul modulo dell'app. Da lì dovrebbe essere disponibile per l'assegnazione dinamica agli elementi usando ng-controller e/o ng-include.

Spero che abbia senso.

+0

ma non mi trovo con 100 appmoduli, quindi nel mio array di dipendenze? e quelle 100 dipendenze, non devono essere caricate a pageload allora? Stavo provando un'iniezione dinamica, solo perché potevo quindi inserire gli script quando sono necessari, non al caricamento della pagina. – Sander

+7

ecco come lo abbiamo risolto: abbiamo 1 modulo 'framework', e tra le sue dipendenze c'è un modulo' framework.applications'. ora, quando carichiamo dinamicamente gli script per un'applicazione, lega i controller a quel modulo framework.applications. (tramite la funzione '$ controllerProvider.register()', in pratica abbiamo ristrutturato la nostra architettura come hai detto tu, per fare in modo che i controllori siano la logica dell'app e non avere un modulo per app. – Sander

+1

E i routing? alla configurazione quando vengono aggiunti? – BenCr

4

beh, se ciascuna sotto-app si trova nel proprio modulo, è possibile utilizzare angular.bootstrap per caricare dinamicamente quel modulo.quando l'URL di uno specifico carichi app, è possibile recuperare lo script (s) necessario, poi, quando la promessa si risolve, si può fare qualcosa sulla falsariga di:

// grab a reference to the element where you'll be loading the sub-app 
var subapp = document.getElementById('subapp-id'); 

// assuming the script you get back contains an angular module declaration named 
// 'subapp', manually start the sub-app 
angular.bootstrap(angular.element(subapp), ['subapp']); 

Spero che questo aiuti

+0

ho provato questa strada, si può vedere nel repository github, nel 'framework.appController', quella che controlla il' $ routeParam' e carica gli script con promesse, quello dopo il caricamento degli script viene eseguito: 'angular.bootstrap ($ ('# application-container'), [$ scope.app.name]);' il '# application-container' è presente nell'html caricato nell'app e app.name è uguale al nome del modulo. ma questo chiaramente non fa il trucco. il modello dell'app non riesce a trovare il controller da caricare. – Sander

+0

Spero che non sia un problema il bootstrap di un modulo, all'interno di una ng-view di un altro modulo (l'app nou di framewwork viene eseguita sul tag body mentre l'applicazione è solo una div all'interno dell'area del contenuto) – Sander

+0

ah okay, Stavo cercando qualcosa del genere nel tuo codice per vedere se avevi già percorso quella strada. Credo di essermi perso, mi scuso. Interessante problema, continuerò a lavorare su questo. – UnicodeSnowman

24

Contrariamente alla risposta attualmente accettata, è effettivamente possibile.

Stavo lavorando su un problema simile e la risposta suggerita non era accettabile nel mio caso. Avevo già scritto pagine con più applicazioni, ma erano anni fa e le app erano indipendenti l'una dall'altra. Ci sono due cose da fare in pratica:

  • Dire all'applicazione principale di ignorare un elemento figlio.
  • Bootstrap l'elemento figlio.

C'è un attributo ng-non-bindable che indica semplicemente ad AngularJS di ignorare l'elemento. Questo gestisce il nostro primo problema.

Tuttavia, quando si tenta di eseguire il bootstrap dell'elemento figlio; AngularJS genera un errore che ti dice che è già avviato (almeno per me, versione 1.2.13). Il seguente trucco fa il lavoro:

<div ng-non-bindable data-$injector=""> 
    <div id="bootstrap-me"> 
    <script src="/path/to/app.js"></script> 
    <div ng-include="'/path/to/app.html'"/> 
    </div> 
</div> 

Questa soluzione non è perfetta. Idealmente, l'attributo ng-non-bindable può aggiungere dati richiesti - $ injector ad elemento. Sto per creare una funzione e, si spera, una richiesta di richiamo per AngularJS.


non ho avuto la possibilità di fare una richiesta di pull. Apparentemente e prevedibilmente dovrei dire, alcuni interni sono cambiati ma ng-non-bindable sta ancora lavorando alla versione 1.3.13 usando il codice demo di Ventzy Kunev (grazie ancora, vedi link sotto).

+1

Questo è stato davvero utile ed esattamente quello di cui avevo bisogno. Non ho nemmeno dovuto aggiungere 'data- $ injector' (Angular 1.3). Senza questa soluzione, non ho mai avuto direttive per le app nidificate al lavoro - grazie mille! –

+5

Questo funzionava perfettamente per me, nell'angolare 1.2.16. Ho appena provato 1.2.26 e 1.3.0-rc4 ed entrambe queste versioni ora lanciano l'eccezione: 'Errore: [ng: btstrpd] App già avviata con questo elemento '< div id =" viewAppDiv ">'' ... qualcun altro ha avuto questo problema? –

+2

Questa deve essere la risposta accettata. Ecco plunkr che illustra l'app incorporata in modale http://plnkr.co/edit/AVD1BVGb5T5GRH3QwBvl?p=preview Due app: "modal-app" e "list-app". modal-app mostra il modale ma il suo contenuto è gestito dal controllore in embeded list-app. Dato che l'html del modale è all'interno del modello che non viene renderizzato fino a quando non viene aperta la modale, viene eseguito il bootstrap dell'elenco-app, vedere la fine di modal.js. Nell'app reale, sto usando il servizio "notifier", che attiva l'evento dopo che il modal è stato reso, e quindi i miei bootstrap di classe "manager" avevano bisogno dell'app e i moduli non si conoscono l'un l'altro. –

2

Simile alla risposta di UnicodeSnowman sopra, un'altra potenziale soluzione che sembra funzionare per le mie esigenze (ho creato un editor Angular live su un sito di documentazione) è di gestire manualmente il processo di bootstrap avendo uno <div id="demos"> separato dal principale <div id="myApp">.

This article è stato molto utile per farlo funzionare correttamente.

processo generale

  • Crea la tua applicazione principale (ho scelto di bootstrap manualmente, ma si può essere in grado di utilizzare ng-app per questa parte)
  • creare una nuova struttura HTML/app (nel mio caso l'applicazione demo):
  • aggiungerlo ai demo div con un ID personalizzato: someCoolDemoContainer
  • bootstarp l'applicazione appena creata
  • spostarlo di nuovo in app originale (per il layout/scopi di posizionamento)

Esempio di codice(non testato; mostra solo processo di base)

<div id="myApp"> 
    <h1>Demo</h1> 

    <p>Click the button below to checkout the cool demo!</p> 

    <button ng-click="showDemo()">Show Demo</button> 

    <div class='insertion-point'></div> 
</div> 

<div id="demos"> 
</div> 

<script type="text/javascript"> 
    /* 
    * Init/bootstrap our main app 
    */ 
    var appContainer = document.getElementById('myApp'); 

    angular.module('myApp', ['myDependency']); 
    angular.bootstrap(appContainer, ['myApp']); 

    // Do lots of other things like adding controllers/models/etc. 

    /* 
    * Init/bootstrap our demo app when the user clicks a button 
    */ 
    function showDemo() { 
     // Append our demo code 
     $('#demos').append('<div id="someCoolDemoContainer">Angular app code goes here</div>'); 

     // Bootstrap the new app 
     var demoContainer = document.getElementById('someCoolDemoContainer'); 
     angular.module('someCoolDemo', ['myDependency']); 
     angular.module('someCoolDemo').controller('myController', function() { ... }); 

     angular.bootstrap(demoContainer, ['someCoolDemo']); 

     // Re-insert it back into the DOM where you want it 
     $('#myApp').find('.insertion-point').append($('#someCoolDemoContainer')); 
    } 
</script> 
+0

Breve nota su questo che ho appena realizzato: la tua applicazione principale avrà tutte le sue funzioni di configurazione ri-eseguite ogni volta che fai il bootstrap (in questo caso ogni volta che mostriamo una demo). Quindi, come ho mostrato sopra, è necessario assicurarsi che ci sia un modulo comune che entrambe le applicazioni possono utilizzare come dipendenza piuttosto che il modulo demo che dipende direttamente dall'applicazione. –

Problemi correlati