angular里面,有两个内置指令非常危险一个是ng-if一个是ng-repeat,这两个指令会让你在写模板的时候,输出结果不一定按照预期显示。angular里面的作用域遵循js的原型继承模型,子作用域继承父作用域,那么相当于子作用域的原型链是它父作用域的一个节点。而我们在创建一个directive的时候,scope参数有三种方式:
- true:共享父作用域的scope,在directive内对scope的任何修改都会同步给父作用域,也就是内外scope完全是同一个
- false:创建一个隔离作用域,和父作用域完全没有任何关系,directive内部的任何修改都不会影响父作用域的scope上的数据
- { ...props }:创建一个有条件的隔离作用域,虽然这个作用域基本面是和父作用域隔离的,但是对象内的参数可以实现绑定,仅这些props可以产生影响,其中又有3种形式,@和&仅接收外部作用域的变化,而=可以实现内部的改动同步到外部去。也就是说,只有=的那部分是共享的。
这些知识可以随便查资料了解。
但是,ng-if和ng-repeat会打破这种共享关系。如果你在一个directive中使用了另外一个directive,而想让它们拥有同样的数据绑定(大部分情况下我们都这样希望),那么一旦你在子diretive上使用了ng-if或ng-repeat,那么情况就发生了变化。这两个内置指令会创建一个隔离作用域,而非共享父作用域。
上面这张图示意了这种令人抓狂场景产生的原因。原本,父子指令共享了一个作用域,但是当你在子指令上使用了ng-if的时候,angular内部会创建一个隔离作用域,ng-if作为指令,仅仅接收父作用域传过来的一个值,但它不会回写数据,只是控制了渲染效果。而这个时候,my-child-directive被放在ng-if作用域的内部,这时,它共享的,是ng-if的作用域,而这个时候,你把child scope打印出来看,会发现,啥也没有,父作用域的东西完全没有传过来。但是,不要心急,由于angualr作用域的继承性,你仍然可以读到一些scope上的值,但是,你不能反写,一旦你开始反写,你发现,父作用域接收不到你的反写内容。反写的内容被直接写在了ng-if scope上,而如果你精通js的原型链模型,就知道,如果一个对象的原型链上有某属性,这个时候,你再对这个对象的同名属性赋值,那么值被覆盖了,它永远无法操作原型链的上游。不过,angular可以,因为它的scope提供了父级作用域的引用$parent,所以,当你在child scope上反写不成功时,往往在前面加$parent.就行了。而如果,你的child scope本身也就是一个隔离作用域,那反写是不可能了,但你还可以通过$parent.$parent.来操作父级作用域。
遇到ng-repeat无法获取到数据时,往往是因为ng-repeat比ng-if更狠,它创建了一个更封闭的隔离作用域,但你按照上述进行思考,也可以实现自己的目的。