如果父项具有ng-if,则Angular ng-show不起作用

时间:2014-01-02 14:07:38

标签: angularjs

我有一个视图,其中父div有ng-if,有些子元素有ng-show。当嵌套在具有ng-if的元素下时,ng-show似乎无法正常工作。这是一个Angular bug还是我做错了什么? See this plunker

HTML:

<!-- with ng-if on the parent div, the toggle doesn't work -->
    <div ng-if="true">
      <div>
          visibility variable: {{showIt}}
      </div>
      <div ng-show="!showIt">
          <a href="" ng-click="showIt = true">Show It</a>
      </div>
      <div ng-show="showIt">
        This is a dynamically-shown div.
        <a href="" ng-click="hideIt()">Hide it</a>
      </div>
    </div>

    <br/><br/>

    <!-- with ng-show on the parent div, it works -->
    <div ng-show="true">
      <div>
          visibility variable: {{showIt}}
      </div>
      <div ng-show="!showIt">
          <a href="" ng-click="showIt = true">Show It</a>
      </div>
      <div ng-show="showIt">
        This is a dynamically-shown div.
        <a href="" ng-click="hideIt()">Hide it</a>
      </div>
    </div>

JavaScript:

scope.hideIt = function () {
  scope.showIt = false;
};

谢谢,

安迪

4 个答案:

答案 0 :(得分:18)

上面提到的Nemesv你应该使用$parent,但是虽然有效,但不是正确的解决方案。这个解决方案的问题是:

  1. 它在ng-if的范围与控制器范围之间创建了高耦合。
  2. 由于1,将ng-if更改为ng-show会破坏您的代码。
  3. 一旦你要筑巢更多的范围,它就变得一团糟($parent.$parent.$parent....
  4. 解决方案:

    快速正确的解决方案是不直接在您的范围内定义showIt,而是将其放在对象中(例如component.isVisible)。

    说明:

    要理解为什么这个看似反直觉的解决方案有效且确实是正确的解决方案,首先需要了解更多关于继承如何与角度协同工作的方法:

    范围使用原型继承相互继承,原型继承是Javascript中继承的形式。这看起来如下:

    var myScope = {
        showIt : false
    }
    var ngIfScope = {};
    nfIfScope.__proto__ = myScope;
    

    当你现在获取一个不存在的ngIfScope对象上的属性时,它会查看它的原型以在那里找到它。因此,如果您请求ngIfScope.showIt,浏览器会执行以下操作:

    if (ngIfScope.hasOwnProperty("showIt")) {
        return ngIfScope.getOwnProperty("showIt"); // getOwnProperty does not actually exist in javascript
    } else {
        return ngIfScope.__proto__.showIt;
    }
    

    (实际上这是递归发生的,但这对于这个例子来说并不重要。)

    设置属性要简单得多:

    ngIfScope.setOwnProperty("showIt", newValue);
    

    现在我们已经掌握了这些信息,我们可以看到原始实施方式究竟出了什么问题。

    我们从以下范围开始:

    var myScope = {
        showIt : false
    }
    var ngIfScope = {};
    ngIfScope.__proto__ = myScope;
    

    当用户点击show按钮时,执行以下代码:

    ngIfScope.showIt = true;
    

    ,结果范围是:

    var myScope = {
        showIt : false
    }
    var ngIfScope = {
        showIt : true
    }
    ngIfScope.__proto__ = myScope;
    

    正如您所看到的,新值是用ngIfScope写的,而不是myScope中所写的那样。结果是ngIfScope.showIt掩盖了来自myScope的变量,而myScope.showIt实际上并未实际更改。

    现在让我们看看如果我们将可见性触发器放在一个对象中会发生什么。

    我们从新范围开始:

    var myScope = {
        component : {
            isVisible : false
        }
    };
    var nfIfScope = {};
    ngIfScope.__proto__ = myScope;
    

    到目前为止没有太大变化。但现在让我们看看当用户点击按钮时会发生什么:

    ngIfScope.component.isVisible = true;
    

    使用一些辅助变量,我们可以看到它是如何在浏览器中执行的:

    var tempObject = ngIfScope.component; 
    tempObject.isVisible = true;
    

    这里的第一行是 get 操作。由于component未在ngIfScope上定义,因此Javascript引擎会查看ngIfScopemyScope)的原型以在那里找到它,如上所述。因此:

    tempObject === ngIfScope.__proto__.component === myScope.component 
    

    我们现在正在myScope.component上直接更改值,因此这次变量不会黯然失色。产生的范围是:

    var myScope = {
        component : {
            isVisible : true
        }
    };
    var ngIfScope = {};
    var ngIfScope.__proto__ = myScope;
    

    我们现在有一个工作实现,没有显式绑定到$parent范围,因此范围之间没有(或很少)耦合。 Prototypal继承为我们工作,嵌套范围也可以直接使用。

答案 1 :(得分:13)

ng-show不同,ng-if指令会创建新范围

因此,当您在showIt = true内写ng-if时,您在子范围内设置了showIt属性,而不是在主范围内。

要解决此问题,请使用$parent访问您父母范围内的财产:

<div ng-if="true">
   <div>
       visibility variable: {{showIt}}
   </div>
   <div ng-show="!showIt">
       <a href="" ng-click="$parent.showIt = true">Show It</a>
   </div>
   <div ng-show="showIt">
     This is a dynamically-shown div.
     <a href="" ng-click="hideIt()">Hide it</a>
   </div>
 </div>

演示Plunker

答案 2 :(得分:1)

在两种情况下都使用函数或表达式,因此这种变体正在起作用。

<div ng-if="true">
  <div>
      visibility variable: {{showIt}}
  </div>
  <div ng-show="!showIt">
      <a href="" ng-click="showIt = true">Show It</a>
  </div>
  <div ng-show="showIt">
    This is a dynamically-shown div.
    <a href="" ng-click="showIt = false">Hide it</a>
  </div>
</div>

这个变种也是

<div ng-if="true">
  <div>
      visibility variable: {{showIt}}
  </div>
  <div ng-show="!showIt">
      <a href="" ng-click="changeState(true)">Show It</a>
  </div>
  <div ng-show="showIt">
    This is a dynamically-shown div.
    <a href="" ng-click="changeState(false)">Hide it</a>
  </div>
</div>

代码

$scope.changeState= function (state) {
  $scope.showIt = state;
};

答案 3 :(得分:-1)

所以Tiddo对Namesv所说的是真的,但也过于复杂。

这样做:

row

在父作用域中的对象中包含showIt将确保在子作用域中使用它时它是同一个对象