AngularJS ui-router $state.go('^') only changing URL in address bar, but not loading controller

前端 未结 3 1524
时光取名叫无心
时光取名叫无心 2021-02-02 01:30

I am trying to create a \"Todo App\" with angularjs ui-router. It has 2 columns:

  • Column 1: list of Todos
  • Column 2: Todo details or Todo edit
相关标签:
3条回答
  • 2021-02-02 01:42

    I might have faced a similar problem the approach i took was to use $location.path(data.path).search(data.search); to redirect the page then in the controller I caught the $locationChangeSuccess event. I other words I use the $location.path(...).search(...) as apposed to $state.go(...) then caught the $locationChangeSuccess event which will be fired when the location changes occurs before the route is matched and the controller invoked.

    var TodoApp = angular.module('TodoApp', ['ngResource', 'ui.router'])
        .config(function ($stateProvider, $urlRouterProvider) {
            $urlRouterProvider.otherwise('/api/todo');
    
            $stateProvider
                .state('todo', {
                    url: '/api/todo',
                    controller: 'ListCtrl',
                    templateUrl: '/_todo_list.html'
                })
                .state('todo.details', {
                    url: '/{id:[0-9]*}',
                    views: {
                        'detailsColumn': {
                            controller: 'DetailsCtrl',
                            templateUrl: '/_todo_details.html'
                        }
                    }
                })
                .state('todo.edit', {
                    url: '/edit/:id',
                    views: {
                        'detailsColumn': {
                            controller: 'EditCtrl',
                            templateUrl: '/_todo_edit.html'
                        }
                    }
                })
                .state('todo.new', {
                    url: '/new',
                    views: {
                        'detailsColumn': {
                            controller: 'CreateCtrl',
                            templateUrl: '/_todo_edit.html'
                        }
                    }
                })
            ;
    
        })
    ;
    
    TodoApp.factory('Todos', function ($resource) {
        return $resource('/api/todo/:id', { id: '@id' }, { update: { method: 'PUT' } });
    });
    
    var ListCtrl = function ($scope, $state, Todos, todo.details) {
        /*here is where i would make the change*/        
        $scope.$on('$locationChangeSuccess', function () {
              $scope.search();
              $route.reload();
        });
    
            $scope.todos = [];
    
            $scope.search = function () {
                Todos.query(function (data) {
                    $scope.todos = $scope.todos.concat(data);
                });
            };
    
        $scope.search();
    };
    
    var DetailsCtrl = function ($scope, $stateParams, Todos) {
        $scope.todo = Todos.get({ id: $stateParams.id });
    };
    
    var EditCtrl = function ($scope, $stateParams, $state, Todos, $location) {
        $scope.action = 'Edit';
    
        var id = $stateParams.id;
        $scope.todo = Todos.get({ id: id });
    
        $scope.save = function () {
            Todos.update({ id: id }, $scope.todo, function () {
               //here is where I would make a change
                    $location.path('todo.details').search($stateParams);
            });
        };
    };
    
    var CreateCtrl = function ($scope, $stateParams, $state, Todos, $location) {
        $scope.action = 'Create';
    
        $scope.save = function () {
            Todos.save($scope.todo, function () {
               //here is where I would make a change
                    $location.path('todo.details');
            });
        };
    };
    

    the $locationChangeSuccess event occurs before the route is matched and the controller invoked

    0 讨论(0)
  • 2021-02-02 01:49

    I would give an example (a draft) of HOW TO nest edit into detail. Well, firstly let's amend the templates.

    The Detail template, contains full definition of the detail. Plus it now contains the attribute ui-view="editView". This will assure, that the edit, will "replace" the detail from the visibility perspective - while the edit scope will inherit all the detail settings. That's the power of ui-router

    <section ui-view="editView">
      <!-- ... here the full description of the detail ... -->
    </section>
    

    So, secondly let's move the edit state, into the detail

    // keep detail definition as it is
    .state('todo.details', {
        url: '/{id:[0-9]*}',
        views: {
            'detailsColumn': {
                controller: 'DetailsCtrl',
                templateUrl: '/_todo_details.html'
            }
        }
    })
    // brand new definition of the Edit
    .state('todo.details.edit', { // i.e.: url for detail like /todo/details/1/edit
        url: '/edit',
        views: {
            'editView': {    // inject into the parent/detail view
                controller: 'EditCtrl',
                templateUrl: '/_todo_edit.html'
            }
        }
    })
    

    Having this adjusted state and template mapping, we do have a lot. Now we can profit from the ui-router in a full power.

    We'll define some methods on a DetailCtrl (remember, to be available on the inherit Edit state)

    var DetailsCtrl = function ($scope, $stateParams, Todos) {
    
        $scope.id =  $stateParams.id // keep it here
    
        // model will keep the item (todos) and a copy for rollback
        $scope.model = {
            todos : {},
            original : {},
        }
    
        // declare the Load() method
    
        $scope.load = function() {
            Todos
              .get({ id: $stateParams.id })
              .then(function(response){
    
                  // item loaded, and its backup copy created
                  $scope.model.todos = response.data;
                  $scope.model.original = angular.copy($scope.model.todos);
    
              });
        };
    
        // also explicitly load, but just once,
        // not auto-triggered when returning back from Edit-child
        $scope.load()
    };
    

    OK, it should be clear now, that we do have a model with the item model.todos and its backup model.original.

    The Edit controller could have two actions: Save() and Cancel()

    var EditCtrl = function ($scope, $stateParams, $state, Todos) {
        $scope.action = 'Edit';
    
        // ATTENTION, no declaration of these, 
        // we inherited them from parent view !
        //$scope.id ..     // we DO have them
        //$scope.model ...
    
        // the save, then force reload, and return to detail
        $scope.save = function () {
            Todos
               .update({ id: id })
               .then(function(response){
    
                  // Success
                  $scope.load(); 
                  $state.go('^');
               },
               function(reason){
    
                 // Error
                 // TODO 
               });
        };
    
        // a nice and quick how to rollback
        $scope.cancel = function () {
             $scope.model.todos = Angular.copy($scope.model.original);
             $state.go('^');
        };
    };
    

    That should give some idea, how to navigate between parent/child states and forcing reload.

    NOTE in fact, instead of Angular.copy() I am using lo-dash _.cloneDeep() but both should work

    0 讨论(0)
  • 2021-02-02 01:58

    Huge thanks for Radim Köhler for pointing out that $scope is inherited. With 2 small changes I managed to solve this. See below code, I commented where I added the extra lines. Now it works like a charm.

    var TodoApp = angular.module('TodoApp', ['ngResource', 'ui.router'])
        .config(function ($stateProvider, $urlRouterProvider) {
            $urlRouterProvider.otherwise('/api/todo');
    
            $stateProvider
                .state('todo', {
                    url: '/api/todo',
                    controller: 'ListCtrl',
                    templateUrl: '/_todo_list.html'
                })
                .state('todo.details', {
                    url: '/{id:[0-9]*}',
                    views: {
                        'detailsColumn': {
                            controller: 'DetailsCtrl',
                            templateUrl: '/_todo_details.html'
                        }
                    }
                })
                .state('todo.edit', {
                    url: '/edit/:id',
                    views: {
                        'detailsColumn': {
                            controller: 'EditCtrl',
                            templateUrl: '/_todo_edit.html'
                        }
                    }
                })
                .state('todo.new', {
                    url: '/new',
                    views: {
                        'detailsColumn': {
                            controller: 'CreateCtrl',
                            templateUrl: '/_todo_edit.html'
                        }
                    }
                })
            ;
    
        })
    ;
    
    TodoApp.factory('Todos', function ($resource) {
        return $resource('/api/todo/:id', { id: '@id' }, { update: { method: 'PUT' } });
    });
    
    var ListCtrl = function ($scope, $state, Todos) {
        $scope.todos = [];
    
        $scope.search = function () {
            Todos.query(function (data) {
                $scope.todos = $scope.todos(data); // No concat, just overwrite
                if (0 < $scope.todos.length) { // Added this as well to avoid overindexing if no Todo is present
                    $state.go('todo.details', { id: $scope.todos[0].Id });
                }
            });
        };
    
        $scope.search();
    };
    
    var DetailsCtrl = function ($scope, $stateParams, Todos) {
        $scope.todo = Todos.get({ id: $stateParams.id });
    };
    
    var EditCtrl = function ($scope, $stateParams, $state, Todos) {
        $scope.action = 'Edit';
    
        var id = $stateParams.id;
        $scope.todo = Todos.get({ id: id });
    
        $scope.save = function () {
            Todos.update({ id: id }, $scope.todo, function () {
                $scope.search(); // Added this line
                //$state.go('^'); // As $scope.search() changes the state, this is not even needed.
            });
        };
    };
    
    var CreateCtrl = function ($scope, $stateParams, $state, Todos) {
        $scope.action = 'Create';
    
        $scope.save = function () {
            Todos.save($scope.todo, function () {
                $scope.search(); // Added this line
                //$state.go('^'); // As $scope.search() changes the state, this is not even needed.
            });
        };
    };
    
    0 讨论(0)
提交回复
热议问题