我有一个视图,其中父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;
};
谢谢,
安迪
答案 0 :(得分:18)
$parent
,但是虽然有效,但不是正确的解决方案。这个解决方案的问题是:
ng-if
的范围与控制器范围之间创建了高耦合。ng-if
更改为ng-show
会破坏您的代码。$parent.$parent.$parent....
)快速正确的解决方案是不直接在您的范围内定义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引擎会查看ngIfScope
(myScope
)的原型以在那里找到它,如上所述。因此:
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将确保在子作用域中使用它时它是同一个对象