Question

I'm making a document editor. Documents can be Type A or Type B. They are accessed by url by document id, but the id does not make it clear if the document is of type A or B.

So, I need to load the document by id, determine its type from its data, and then pass it to either the TypeAController or TypeBController.

Right now, with ui-router, I have something like this:

$stateProvider
.state('loading', {
    url: '/{documentId}',
    template: 'Loading...',
    controller: function ($stateParams, $state) {
        loadDocument($stateParams.documentId)
            .then(function (loadedDocument) {
                if (loadedDocument.type === 'A') {
                    $state.go('EditA');
                } else if (loadedDocument.type === 'B') {
                    $state.go('EditB');
                }
            })
    }
})
.state('A', {...})
.state('B', {...})

The loading state loads the document, determines its type, and then goes to the next state.

Frustratingly, though, I can't find a way to actually pass the loaded document to the next states! I can make a globalish service into which I can insert the document, or I can just pass the id of the document along and load it again in each state (hopefully from a cache this time), but these methods are so clunky and everything else about angular and angular-ui has been so smooth.

Any suggestions?

Was it helpful?

Solution

One solution could be to move it to the parent state, which is available to all children. Something like this:

$stateProvider
.state('loading', {
    url: '/{documentId}',
    template: 'Loading...',
    controller: function ($scope, $stateParams, $state) {
        loadDocument($stateParams.documentId)
            .then(function (loadedDocument) {

                // assign the document to the parent model $scope
                // in this case $scope.model.doc  
                $scope.model = { "doc" : loadedDocument };
                if (loadedDocument.type === 'A') {
                    $state.go('.EditA');
                } else if (loadedDocument.type === 'B') {
                    $state.go('.EditB');
                }
            })
    }
})
.state('loading.EditA', {...}) // here we can use the $scope.model.doc
.state('loading.EditB', {...}) // in every child state

The $scope.model.doc represents the reference to the shared document.

Here (UI-Router example - contact.js) we can see how parent is setting the contacts collection, all child states are accessing it. The example in action

OTHER TIPS

You could have a Documents service which owns and provides an API to all the document data. The controllers then inject the Documents service, and reference the document they're interested in on their scope.

Something like:

app.service('Documents', function(){
  Documents = {};

  return {
    open: function(doc_id) { ... }  // loads and caches doc (if not already cached) and returns a promise with a reference to Documents[doc_id]
    sync: function(doc_id) { ... }  // sync doc with server
    close: function(doc_id) { ... } // remove doc_id from Documents 
  };
});

app.controller('editX', function($scope, $stateParams, Documents){
  Documents.open($stateParams.documentId).then(function(ref){
    $scope.document = ref;
  });

  ...

})
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top