Angular Digest 循环正在运行,但 ng-bind 值未更新 [英] Angular Digest cycle being ran but ng-bind value not updating
问题描述
我有一个包含导航栏的父视图,在该视图内部我有一个 <div ui-view>
元素,它呈现我所在的任何子视图.
我想根据子视图的路线有条件地显示/隐藏父视图中的导航栏.现在,我有这个:
我的应用第一次加载时,vm.hideNavbar
设置为 true,这按预期工作.
vm.hideNavbar
修改为false后,绑定值不更新.它仍然是true
.
我应用中的每个控制器都扩展了这个 BaseController
:
导出类 BaseController {公共隐藏导航栏:布尔值;构造函数(公共 $scope:IBaseScope,私有 $state:ng.ui.IStateService){if ($state.current.url === '/login') {this.hideNavbar = true;} 别的 {this.hideNavbar = false;}$scope.vm = 这个;}}
因此,每次加载新控制器时,它都会调用 BaseController
的构造函数并有条件地设置 $scope.vm.hideNavbar
.如果我在此构造函数的末尾立即运行 $scope.$apply()
,angular 会抛出错误,指出摘要循环已经在运行.
因此,正在运行摘要循环,但我认为该值并未更新.我唯一的想法是我已经实例化了多个 BaseController
的副本,因为我的初始控制器和我导航到的控制器都扩展了这个控制器.所以,现在,我的 vm.hideNavbar
绑定值仍在查看旧控制器.
我是否在正确的轨道上?我该如何解决这个问题?
在这种情况下,我会建议使用 视图继承
(而不是 controller
,而不是 state
).在此处查看更多详细信息:
有一个工作示例
我们需要的是 'root'
状态.它将是任何其他state
(states family)的超级父.这可能是状态定义:
$stateProvider.state('root', {摘要:真实,templateUrl: 'layout.tpl.html',控制器:MyNamespace.RootCtrl,}).state('登录', {父母:根",网址:/登录",templateUrl: 'tpl.html',控制器:MyNamespace.LoginCtrl,}).state('家', {父母:根",网址:/家",templateUrl: 'tpl.html',控制器:MyNamespace.HomeCtrl,})
甚至一些其他的状态层次结构也会以那个 'root'
状态开始:
$stateProvider.state('父母', {父母:根",网址:/父母",templateUrl: 'tpl.html',控制器:MyNamespace.ParentCtrl}).state('parent.child1', {网址:/child1",templateUrl: 'tpl.html',控制器:MyNamespace.Child1Ctrl}).state('parent.child2', {网址:/child2",templateUrl: 'tpl.html',控制器:MyNamespace.Child2Ctrl})
我们可以看到许多 controllers:...
被定义,它们是:
module MyNamespace {//所有状态的真正超级父级//但它是关于 VIEW 继承的(它的 $scope)//与控制器层次结构无关导出类 RootCtrl 扩展 BaseController {}导出类 HomeCtrl 扩展 BaseController {}导出类 LoginCtrl 扩展 BaseController {}导出类 ParentCtrl 扩展 BaseController {}导出类 Child1Ctrl 扩展 BaseController {}导出类 Child2Ctrl 扩展 BaseController {}}
正如片段注释中所提到的 - 有继承,但只是在代码级别.传递的 $scope
由视图层次结构继承.
视图层次结构中的第一个控制器是 RootCtrl
,它实际上是唯一一个分配(创建)共享参考模型的控制器 rootSetting : {}
而且它们都源自这个控制器基础:
module MyNamespace {导出接口 IRootSetting {隐藏导航栏:布尔值;}导出接口 IMyRootScope 扩展 ng.IScope {根设置:IRootSetting}导出接口 IBaseScope 扩展 IMyRootScope {}导出类 BaseController {公共隐藏导航栏:布尔值;静态 $inject = ['$scope', '$state'];构造函数(公共 $scope:IBaseScope,受保护的 $state: ng.ui.IStateService) {//实际上只会在 RootCtrl 中分配//所有其他人(孩子)将获得该引用//通过作用域继承$scope.rootSetting = $scope.rootSetting ||{隐藏导航栏:假};if ($state.current.url === '/login') {this.$scope.rootSetting.hideNavbar = true;} 别的 {this.$scope.rootSetting.hideNavbar = false;}}}}
有了这个,使用这个根模板:
<div ng-if="!rootSetting.hideNavbar">...//导航栏<div ui-view="">//子视图的标准内容
我们可以看到,在这里我们评估了共享参考模型 rootSetting
及其属性 hideNavbar
这是UI-Router
带来的视图继承
的真正优势.
在操作中检查它这里
I have a parent view that contains a navbar, and inside of that view I have a <div ui-view>
element which renders whatever child view I'm on.
I want to conditionally show/hide the navbar within the parent view, based on the route of the child view. Right now, I have this:
<nav ng-show="!vm.hideNavbar">
The first time my app is loaded, vm.hideNavbar
is set to true and this works as expected.
After vm.hideNavbar
is changed to false, the bound value is not updated. It is still true
.
Every controller in my app extends this BaseController
:
export class BaseController {
public hideNavbar: boolean;
constructor(public $scope: IBaseScope, private $state: ng.ui.IStateService) {
if ($state.current.url === '/login') {
this.hideNavbar = true;
} else {
this.hideNavbar = false;
}
$scope.vm = this;
}
}
So, everytime a new controller is loaded, it calls the constructor for BaseController
and conditionally sets $scope.vm.hideNavbar
. If I immediately run $scope.$apply()
at the end of this constructor, angular throws errors saying the digest cycle is already being ran.
So, the digest cycle is being ran, but the value in my view is not being updated. My only thought is that I have instantiated more than one copy of the BaseController
since my initial controller and the controller I navigated to both extend this controller. So, now, my bound value of vm.hideNavbar
is still looking at the old controller.
Am I on the right track with this? How can I solve this issue?
In this case, I would suggest to go with view inheritance
(not controller
, not state
). Check more details here:
- How do I share $scope data between states in angularjs ui-router?
- How to inherit resolve data in ui-router
There is a working example
What we would need is a 'root'
state. It will be the super parent of any other state
(states family). This could be the state definition:
$stateProvider
.state('root', {
abstract: true,
templateUrl: 'layout.tpl.html',
controller: MyNamespace.RootCtrl,
})
.state('login', {
parent: "root",
url: "/login",
templateUrl: 'tpl.html',
controller: MyNamespace.LoginCtrl,
})
.state('home', {
parent: "root",
url: "/home",
templateUrl: 'tpl.html',
controller: MyNamespace.HomeCtrl,
})
even some other state hierarchy will start with that 'root'
state:
$stateProvider
.state('parent', {
parent: "root",
url: "/parent",
templateUrl: 'tpl.html',
controller: MyNamespace.ParentCtrl
})
.state('parent.child1', {
url: "/child1",
templateUrl: 'tpl.html',
controller: MyNamespace.Child1Ctrl
})
.state('parent.child2', {
url: "/child2",
templateUrl: 'tpl.html',
controller: MyNamespace.Child2Ctrl
})
We can see many controllers:...
being defined, and here they are:
module MyNamespace {
// the real SUPER parent of all states
// but it is about VIEW inheritance (its $scope)
// not about controller hierarchy
export class RootCtrl extends BaseController {
}
export class HomeCtrl extends BaseController {
}
export class LoginCtrl extends BaseController {
}
export class ParentCtrl extends BaseController {
}
export class Child1Ctrl extends BaseController {
}
export class Child2Ctrl extends BaseController {
}
}
As mentioned in the snippet comment - there is inheritance, but just on a code level. The passed $scope
is inherited by view hierarchy.
The first controller in the view hierarchy is RootCtrl
which will in fact be the only, who will assign (create) the shared reference model rootSetting : {}
And they all derive from this one controller base:
module MyNamespace {
export interface IRootSetting {
hideNavbar: boolean;
}
export interface IMyRootScope extends ng.IScope {
rootSetting: IRootSetting
}
export interface IBaseScope extends IMyRootScope {
}
export class BaseController {
public hideNavbar: boolean;
static $inject = ['$scope', '$state'];
constructor(public $scope: IBaseScope,
protected $state: ng.ui.IStateService) {
// will be in fact assigned in the RootCtrl only
// all others (children) will get that reference
// via scope inheritance
$scope.rootSetting = $scope.rootSetting || {hideNavbar: false};
if ($state.current.url === '/login') {
this.$scope.rootSetting.hideNavbar = true;
} else {
this.$scope.rootSetting.hideNavbar = false;
}
}
}
}
Having that in place, with this root template:
<div>
<div ng-if="!rootSetting.hideNavbar">
... // navbar
</div>
<div ui-view="">
// standard content of child views
</div>
</div>
We can see, that here we evaluate the shared reference model rootSetting
and its property hideNavbar
This is the real advantages of view inheritance
coming with UI-Router
.
Check it in action here
这篇关于Angular Digest 循环正在运行,但 ng-bind 值未更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!