How to reuse controllers in AngularJs when success locations are different and $location.path('..') is not supported?

StackOverflow https://stackoverflow.com//questions/23016735

  •  21-12-2019
  •  | 
  •  

Question

Right now the $location service is getting in the way. Suppose one wants to use the same controller for multiple routes, however the expectation is that upon a successful 'save' the destination routes would be different.

.when('/sponsors/:sponsorId/games/add', {templateUrl: 'partials/games/create',controller: 'GameCreateCtrl', access: 'sponsor'})

// an admin can see all the games at
.when('/admin/games/add', {templateUrl: 'partials/games/create',controller: 'GameCreateCtrl', access: 'admin'})

A game is is displayed on success of either action. The route is just the parent path. e.g. /admin/games or /sponsors/:sponsorId/games.

The $location service does not seem to support the relative path $location.path('..'). Should it? What is the best way to reuse the GameCreateCtrl in this situation?

$scope.save = function () {
    GameService.save($scope.game).$promise.then(function(res){
        console.log(res);
        growl.addSuccessMessage("Successfully saved game: " + $scope.game.name);
        console.log("saving game by id:" +  $scope.game._id);
        var path = $location.path();
        $location.path(path.replace('/add', ''));  // this seems like a hack 
    });
}
Was it helpful?

Solution

You can do it with resolve:

.when('/sponsors/:sponsorId/games/add', {
    templateUrl: 'partials/games/create',
    controller: 'GameCreateCtrl',
    resolve: {
        returnUrl: function($routeParams){
            return '/sponsors/' + $routeParams.sponsorId + '/games';
        }
    }
})
.when('/admin/games/add', {
    templateUrl: 'partials/games/create',
    controller: 'GameCreateCtrl',
    resolve: {
        returnUrl: function(){
            return '/admin/games';
        }
    }
})

In controller:

app.controller('myCtrl', function($scope, returnUrl){
    $scope.save = function () {
        GameService.save($scope.game).$promise.then(function(res){
            // ...
            $location.path(returnUrl);  // this seems like a hack 
        });
    };
});

You are passing different returnUrl parameter to controller depending on route.

OTHER TIPS

I would like to thank the poster karaxuna with their solution. Its the answer I am accepting. However, it is often helpful to have other options at ones disposal.

Another way to solve this would be to create a global function.

function getParentPath($location) {
    if ($location.path() != '/') /* can't move up from root */ {
        var pathArray = $location.path().split('/');
        var parentPath = "";
        for (var i = 1; i < pathArray.length - 1; i++) {
            parentPath += "/";
            parentPath += pathArray[i];
        }
        return parentPath;
    }
}

This works very where when edits/adds follow a rest style with regards to route locations. In these cases the parentPath would always go back to the plural listing of all records.
and perhaps add a method to root scope

$rootScope.goParentPath = function ($location) {
        $location.path(getParentPath($location));
    }

function inside controllers could call the getParentPath function. e.g.

$scope.cancel = function() {
        $scope.goParentPath($location)
    }

I am actually leaning considering an approach that combines the first answer with a getParentPath is some situations.

For a little bit of brevity, the routes would make use of the resolve callout, but use the parentPath function in many cases. ex:

.when('/admin/games/:id', {templateUrl: 'partials/games/edit', controller: 'EditGameCtrl', access: 'admin',resolve:{returnUrl: getParentPath}})
.when('/admin/games/add', {templateUrl: 'partials/games/create', controller: 'EditGameCtrl', access: 'admin', resolve:{returnUrl: getParentPath}})
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top