12

Con ui-router, aggiungo tutta la logica di risoluzione nella funzione di stato come questa;Ui-router angolare risolve il valore come stringa

//my-ctrl.js 
    var MyCtrl = function($scope, customers) { 
     $scope.customers = customers; 
    } 

    //routing.js 
    $stateProvider.state('customers.show', { 
     url: '/customers/:id', 
     template: template, 
     controller: 'MyCtrl', 
     resolve: { // <-- I feel this must define as like controller 
     customers: function(Customer, $stateParams) { 
      return Customer.get($stateParams.id); 
     } 
     } 
    }); 

Tuttavia IMO, resolve oggetto deve appartenere a un controller, ed è facile da leggere e conservare se è definito all'interno di un file di controllo.

//my-ctrl.js 
    var MyCtrl = function($scope, customers) { 
     $scope.customers = customers; 
    } 
    MyCtrl.resolve = { 
     customers: function(Customer, $stateParams) { 
     return Customer.get($stateParams.id); 
     }; 
    }; 

    //routing.js 
    $stateProvider.state('customers.show', { 
     url: '/customers/:id', 
     template: template, 
     controller: 'MyCtrl', 
     resolve: 'MyCtrl.resolve' //<--- Error: 'invocables' must be an object. 
    }); 

Tuttavia, quando definisco come MyCtrl.resolve, a causa di IIFE, ottengo il seguente errore.

Failed to instantiate module due to: ReferenceError: MyCtrl is not defined 

Quando definisco che uno come stringa 'MyCtrl.resolve', ottengo questo

Error: 'invocables' must be an object. 

vedo quello controller è definita come stringa, quindi penso è anche possibile fornire il valore come stringa utilizzando un decoratore o qualcosa del genere.

Qualcuno ha fatto questo approccio? In modo che possa mantenere pulito il mio routings.js e inserire informazioni pertinenti. in un file pertinente?

+0

Come accennato in precedenza, il controller non è ancora disponibile a quel punto. Lo schema di progettazione che utilizzo ha un servizio 'Resolver'. Usando 'this.self.name' nell'ambito delle funzioni di risoluzione (dove si effettua la richiesta' Customer.get() '), è possibile ottenere il nome dello stato. Quindi, facendo qualcosa come 'return Resolver.prepare (this.self.name)', puoi estrarre tutta la logica in 'Resolver' e ridurre il tuo codice a una singola riga (e uguale) nelle dichiarazioni di stato. –

risposta

8

Sembra un modo semplice per costruire la risoluzione, ma non penso che tu possa farlo.

A parte il fatto che "risolvere" richiede un oggetto, è definito in una fase in cui tutti i fornitori disponibili sono disponibili. In questo momento, il controller non esiste ancora.

Ancora peggio, tuttavia, la "risoluzione" è destinata a definire gli input per il controller stesso. Per definire la risoluzione nel controller, attendere che venga valutata prima del il controller sia creato sia una dipendenza circolare.

In passato, ho definito le funzioni di risoluzione all'esterno della definizione $stateProvider, consentendo almeno di riutilizzarle. Non ho mai provato a diventare più fanatico di così.

var customerResolve = ['Customer', '$stateParams', 
    function(Customer, $stateParams) { 
     return Customer.get($stateParams.id); 
    } 
]; 

// .... 

$stateProvider.state('customers.show', { 
    url: '/customers/:id', 
    template: template, 
    controller: 'MyCtrl', 
    resolve: { 
    customers: customerResolve 
    } 
}); 
+0

stato di configurazione, non è necessario avere un oggetto controller completo, come si vede il controller è solo una stringa. – allenhwkim

+2

È vero, ma in fase di esecuzione, affinché Angular valuti la risoluzione, sarà necessario creare il controller. Non può creare il controller finché non ha valutato la risoluzione. –

2

È necessario assicurarsi che il controller si trovi all'interno della stessa chiusura della configurazione di stato. Questo non significa che debbano essere definiti nello stesso file.

Così, invece di una stringa, utilizzare una proprietà statica del controller:

resolve: MyCtrl.resolve, 

Aggiornamento

Poi per il file di controllo:

var MyCtrl; 
(function(MyCtrl, yourModule) { 

    MyCtrl = function() { // your contructor function} 
    MyCtrl.resolve = { // your resolve object } 

    yourModule.controller('MyCtrl', MyCtrl); 

})(MyCtrl, yourModule) 

E poi quando definisci i tuoi stati in un altro file, che è incluso o concatenato o richiesto dopo il file del controller:

(function(MyCtrl, yourModule) { 

    configStates.$inject = ['$stateProvider']; 
    function configStates($stateProvider) { 

     // state config has access to MyCtrl.resolve 
     $stateProvider.state('customers.show', { 
      url: '/customers/:id', 
      template: template, 
      controller: 'MyCtrl', 
      resolve: MyCtrl.resolve 
     }); 
    } 

    yourModule.config(configStates); 

})(MyCtrl, yourModule); 

Per il codice di produzione sarà ancora voglia di avvolgere tutti questi IIFEs all'interno di un altro IIFEs. Gulp o Grunt possono farlo per te.

+0

come faccio a fare in modo che il controller si trovi nella stessa chiusura quando ogni file utilizza IIFE? – allenhwkim

+0

Aggiungi l'IIFE attorno al tuo jat concatenato al momento della compilazione. Uglify ha un'opzione per avvolgere il tuo codice in una chiusura, un IIFE. In questo modo il tuo codice sarà all'interno dello stesso IIFE. – Martin

+0

Funzionerà in produzione, ma non in modalità sviluppo, che non esegue alcun ugacle. – allenhwkim

4

Questa domanda riguarda le funzionalità del pacchetto ui-router. Di default, ui-router non supporta le stringhe per la risoluzione dei parametri.Ma se osservate il codice sorgente di ui-router, vedrete che è possibile implementare questa funzionalità senza apportare modifiche dirette al suo codice.

Ora, vi mostrerò la logica dietro metodo suggerito ed è implementazione

Analizzando il codice

Per prima cosa diamo un'occhiata a $ state.transitionTo funzione angolare-ui-router/src/urlRouter.js . Dentro quella funzione vedremo questo codice

for (var l = keep; l < toPath.length; l++, state = toPath[l]) { 
    locals = toLocals[l] = inherit(locals); 
    resolved = resolveState(state, toParams, state === to, resolved, locals, options); 
    } 

Ovviamente questo è dove i parametri di "resolve" sono risolti per ogni stato genitore. Successivamente, diamo un'occhiata alla funzione resolveState sullo stesso file. Troveremo questa linea c'è:

dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state); 
var promises = [dst.resolve.then(function (globals) { 
    dst.globals = globals; 
})]; 

Questo è in particolare dove promesse per risolvere i parametri vengono recuperati. Ciò che è buono per l'uso, la funzione che lo fa è portato a un servizio separato. Ciò significa che possiamo agganciare e modificare il suo comportamento con il decoratore.

Per riferimento l'attuazione di $ Resolve è in angolare-ui-router/src/file di resolve.js

Attuazione del gancio

La firma per la funzione determinazione di $ risoluzione è

this.resolve = function (invocables, locals, parent, self) { 

Dove "invocabili" è l'oggetto della nostra dichiarazione di stato. Quindi dobbiamo controllare se "invocabili" è una stringa. E se lo è, otterremo una funzione di controllo per stringa e invocheremo la funzione dopo "." carattere

//1.1 Main hook for $resolve 
$provide.decorator('$resolve', ['$delegate', '$window', function ($delegate, $window){ 
    var service = $delegate; 



    var oldResolve = service.resolve; 
    service.resolve = function(invocables, locals, parent, self){ 
    if (typeof(invocables) == 'string') { 
     var resolveStrs = invocables.split('.'); 

     var controllerName = resolveStrs[0]; 
     var methodName = resolveStrs[1]; 

     //By default the $controller service saves controller functions on window objec 
     var controllerFunc = $window[controllerName]; 
     var controllerResolveObj = controllerFunc[methodName](); 

     return oldResolve.apply(this, [controllerResolveObj, locals, parent, self]); 

    } else { 
     return oldResolve.apply(this, [invocables, locals, parent, self]); 
    } 
    }; 

    return $delegate; 
}]); 

EDIT:

Potete anche ignorare $ controllerProvider con provider come questo:

app.provider("$controller", function() { 

} 

In questo modo diventa possibile aggiungere una nuova funzione getConstructor, che restituirà costruttore controller nome. E così si eviterà di utilizzare oggetto $ finestra nel gancio:

$provide.decorator('$resolve', ['$delegate', function ($delegate){ 
    var service = $delegate; 

    var oldResolve = service.resolve; 
    service.resolve = function(invocables, locals, parent, self){ 
     if (typeof(invocables) == 'string') { 
     var resolveStrs = invocables.split('.'); 

     var controllerName = resolveStrs[0]; 
     var methodName = resolveStrs[1]; 

     var controllerFunc = $controllerProvider.getConstructor(controllerName); 
     var controllerResolveObj = controllerFunc[methodName](); 

     return oldResolve.apply(this, [controllerResolveObj, locals, parent, self]); 

     } else { 
     return oldResolve.apply(this, [invocables, locals, parent, self]); 
     } 
    }; 

codice completa dimostrando questo metodo http://plnkr.co/edit/f3dCSLn14pkul7BzrMvH?p=preview

+0

Mi piace tranne che ottieni il controller da '$ window' invece di' $ controller'. È possibile farlo usare '$ controller'? Non importa cosa ti darò la taglia poiché hai fatto il maggior sforzo per risolvere il problema. – allenhwkim

+0

Purtroppo quasi impossibile. $ controller crea un'istanza di controller e tenta di risolvere le dipendenze. Ma poiché abbiamo dipendenze personalizzate (risolviamo i parametri) $ controller dirà che non conosce il parametro "clienti". Potrebbe essere possibile decorare il servizio $ controllerProvider e sovrascrivere la sua funzione di registro. Questo darebbe un modo per ottenere un elenco delle funzioni del controller. Ma l'attuale angolare non lo consente al giorno d'oggi. Il risultato di esposizione angolare risulta di $ get invece del servizio effettivo. Quindi la funzione "register" non è visibile –

+0

L'opzione successiva era di modificare $ injector. Ma ancora una volta, angolare non permette di decorare questo servizio e la semplice sovrascrittura dei suoi metodi non dà nulla. –

0

Se l'intenzione è quella di avere il risolutore nello stesso file come il controller, il modo più semplice per farlo è quello di dichiarare il risolutore il file di controllo in funzione:

//my-ctrl.js 
var MyCtrl = function($scope, customers) { 
    $scope.customers = customers; 
} 
var resolverMyCtrl_customers = (['Customer','$stateParams', function(Customer, $stateParams) { 
    return Customer.get($stateParams.id); 
}]); 

//routing.js 
$stateProvider.state('customers.show', { 
    url: '/customers/:id', 
    template: template, 
    controller: 'MyCtrl', 
    resolve: resolverMyCtrl_customers 
}); 
0

Questo dovrebbe funzionare.

//my-ctrl.js 
var MyCtrl = function($scope, customer) { 
    $scope.customer = customer; 
}; 

//routing.js 
$stateProvider 
    .state('customers.show', { 
     url: '/customers/:id', 
     template: template, 
     resolve: { 
      customer: function(CustomerService, $stateParams){ 
       return CustomerService.get($stateParams.id) 
      } 
     }, 
     controller: 'MyCtrl' 
}); 


//service.js 
function CustomerService() { 
    var _customers = {}; 

    this.get = function (id) { 
     return _customers[id]; 
    }; 
} 
Problemi correlati