在 AngularJS UI-Router 中动态改变视图

Dynamically changing View in AngularJS UI-Router

我的应用程序中有一个包含 4 页的演练。我决定让 Walkthrough 成为一个具有多个视图的单一状态来代表 4 个页面中的每一个。

在我的 html 中,我将 div 定义为指向当前视图的 ui 视图,然后我的控制器会根据需要进行更改。

问题是,当我将 $scope.currentView 更新为 'general' 时,它并没有改变屏幕上实际看到的内容!如果我在我的 _init 函数中手动将其更改为 'general',它会显示常规页面,但我无法根据按钮单击进行更改。

HTML:

<div ui-view="{{currentView}}@walkthrough"></div>

控制器:

var _init = function () {
    $scope.currentView = 'welcome';
};
_init();

$scope.setView = function (view) {
    $scope.currentView = view;
};

我的状态定义:

.state('walkthrough', {
    url: '/walkthrough',
    views: {
        '': {
            templateUrl: 'app/walkthrough/walkthrough.html',
            controller: 'walkthroughController'
        },
        'welcome@walkthrough': {
            templateUrl: 'app/walkthrough/welcome.html'
        },
        'general@walkthrough': {
            template: 'general'
        }
    }
})

以及更新视图的按钮:

<img class="start-button center-block" ng-click="setView('general')" />

更新 1

我尝试了以下方法来解决,none 其中有效:

  1. 将 currentView 更改为 getter 函数 getCurrentView(),其中 returns currentView。相同的行为。
  2. 在 $scope.$apply 中包装 currentView setter。获取 $apply already in progress 错误
  3. 在 $timeout 中包装 currentView setter。相同的行为

更新 2

我添加了一个 <pre> 部分,它调用与 ui-视图 {{currentView}}@walkthrough 中相同的代码。它显示了正确的视图,即使页面本身没有更新显示新视图。

更新 3

我已经尝试了如何以编程方式设置视图的所有组合,但我尝试过的都没有奏效。我是否使用服务器获取视图、函数、直接向上 $scope 变量,什么都没有。变量本身是正确的,但是当我更改变量时视图不会改变。

奇怪的是,当我在 init() 函数中设置 currentView 的值时,它会起作用一次。如果我将值更改为代码本身中的下一个视图之一,它会起作用($scope.currentView = 'general' <- 这显示了一般页面),但如果我单击按钮更改 currentView至 'general'.

我已经尝试了各种 $scope.$applys、$digests 和 $timeouts。我所做的任何事情都不会使视图本身更新。唯一剩下的就是用 ng-show/hide 把它变成一堆 div ,这真的很难看,而且很难管理,这也是我首先要使用视图的原因。

更新 4

仍然没有进展,无论我尝试什么...我认为将变量更改包装在 $timeout 中的一些奇怪组合可能有用,但唉,没什么。我最后的想法是将所有这些都更改为它们自己的独立状态,但是我最终会得到一堆重复的代码,这显然不是很好。我在我的应用程序的其他部分使用了几乎相同类型的更改(不过是更改状态,而不是视图),并且效果很好。我不明白为什么我不能动态更改视图。任何帮助将不胜感激。

更新 5

在下面的用户评论后有了一些归宿,但毫无进展。我试图调用所有方式的 $state 更改来刷新视图,但没有任何效果。我尝试了以下所有方法,none 其中对页面有任何影响:

$state.reload();
$state.go($state.current, {}, { reload: true });
$state.transitionTo($state.current, $stateParams, {
    reload: true,
    inherit: false,
    notify: true
});

虽然 ui-view 指令将采用一个内插值 ({{ something }}) 作为视图名称,但它实际上并没有观察这个变化。相反,视图更新仅由 $stateChangeSuccess$viewContentLoading 事件触发。这就是为什么您可以在第一次而且只有第一次观察到它工作的原因。

您可以通过查看 ui-router 源代码中 $ViewDirective 的链接函数来验证这一点:https://github.com/angular-ui/ui-router/blob/master/src/viewDirective.js.

这意味着您的 setView 函数需要调用 $state.go 来触发状态更改,而不是仅仅在作用域上设置 属性。作为通常的状态更改过程,这最终将导致广播 $stateChangeSuccess 事件。

所以我解决了这个问题,方法是让每个演练页面都有自己的父演练的继承状态,每个演练都有一个视图。可能不是最好的方法,但我不想在这上面浪费更多时间。

我仍然喜欢只使用嵌套视图并以这种方式导航的方法,因为将来使用该方法添加更多内容会更容易,而不会膨胀我的 stateProvider。

        .state('walkthrough', {
            url: '/walkthrough',
            views: {
                '': {
                    templateUrl: 'app/walkthrough/walkthrough.html',
                    controller: 'walkthroughController'
                }
            }
        })
        .state('walkthrough.welcome', {
            url: '/welcome',
            views: {
                'walkthrough.welcome@walkthrough': {
                    templateUrl: 'app/walkthrough/welcome.html'
                }
            }
        })
        .state('walkthrough.general', {
            url: '/general',
            views: {
                'walkthrough.general@walkthrough': {
                    templateUrl: 'app/walkthrough/general.html'
                }
            }
        })
        .state('walkthrough.business', {
            url: '/business',
            views: {
                'walkthrough.business@walkthrough': {
                    templateUrl: 'app/walkthrough/business.html'
                }
            }
        })
        .state('walkthrough.friends', {
            url: '/friends',
            views: {
                'walkthrough.friends@walkthrough': {
                    templateUrl: 'app/walkthrough/friends.html'
                }
            }
        })

现在我可以使用

轻松地在两者之间导航
<img ui-sref="walkthrough.general" />