Angular Digest 循环正在运行,但 ng-bind 值未更新 [英] Angular Digest cycle being ran but ng-bind value not updating

查看:26
本文介绍了Angular Digest 循环正在运行,但 ng-bind 值未更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包含导航栏的父视图,在该视图内部我有一个 <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:

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屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆