【问题标题】:Clarifying AngularJS theory snippet from Tutorial澄清教程中的 AngularJS 理论片段
【发布时间】:2016-11-05 11:20:57
【问题描述】:

在来自Step 3 of AngularJS Tutorial 的伟大理论sn-ps 中,那段让我想知道:

  1. 作用域,将我们的控制器和模板粘合在一起 一起变成一个动态的视图,不与其他部分隔离 页。这意味着一个随机的、不相关的变化 页面的不同部分(例如属性名称冲突)可能有 对我们的视图产生意想不到且难以调试的副作用。

(同一链接中未引用的第 1 部分非常清楚)

我无法想象一个真实的代码示例来说明引用文本中显示的问题。你能给我看一个这样的例子吗?


我自己的猜测是基于继承的作用域:

<!doctype html>
<html lang="en" ng-app="phonecatApp">
  <head>
    ...
  </head>
  <body>

    <div ng-controller="PhoneListController">

      {{secretObject.dontDareToTouchThat}} 

      <div ng-controller="PhoneListTwoController">

        <ul  ng-click="touchThat()">
          <li ng-repeat="phone in phones" >
            <span>{{phone.name}}</span>
            <p>{{phone.snippet}}</p>
          </li>
        </ul>

      </div>
    </div>

  </body>
</html>

控制器的逻辑:

'use strict';

angular.module('phonecatApp', [])
.controller('PhoneListController', function PhoneListController($scope) {

  $scope.secretObject = {
    dontDareToTouchThat: 'I"m pure and beautiful'
  }

}).controller('PhoneListTwoController', function PhoneListTwoController($scope) {

  $scope.touchThat = function(){
     $scope.secretObject.dontDareToTouchThat = 'Derp';
  }

  $scope.phones = [ ... ];
});

但我完全不确定,因为PhoneListTwoController 的可能操作看起来不像“页面不同部分的随机、不相关的变化”。一个作用域在另一个作用域内,操纵外部作用域,我认为作者的意思有所不同,例如两个同级作用域相互混淆。

所以,我再次请你用相关的代码示例来说明引用的段落。

【问题讨论】:

  • 我手头没有代码示例。但是您看过这些文档吗? docs.angularjs.org/guide/scope#scope-hierarchies
  • 谢谢康斯坦丁,我有!我不确定本教程的作者是指引用文本中的范围层次结构,因此是这个问题。
  • @fyodorananiev,顺便说一句,如果您必须提出任何改进建议以使初学者更容易理解此(或本教程的任何其他部分),非常欢迎您提交请求请求GitHub :)
  • @ExpertSystem 非常感谢您的建议!如果我将来想出一些改写本教程的想法,我将非常荣幸和高兴。

标签: javascript angularjs inheritance scope


【解决方案1】:

这确实是指范围继承及其(通常不是直截了当的)后果。如果您还没有看过它,这里有一篇很棒的文章:Understanding Scopes

它可能比你想象的要复杂得多:) 尤其是(如教程中提到的)大型真实应用程序,其中不同的团队在应用程序的不同部分工作,或者某些部分几个月都保持不变。


仅展示一个非常简单的“现实主义”示例(这又不像大型应用程序那么复杂):

想象一下您正在创业;一个电子商店。您想从小处着手,因此目前只有手机和平板电脑。

你只需要一个基本的布局——一个标题、一个导航栏和你的内容区域:

My Cool e-Shop
----------------------------
[Phones]   [Tablets]
----------------------------

<CONTENT HERE>

您设置了两条路由 - 一条用于手机,一条用于平板电脑 - 并决定将每个页面的内容封装为类似组件的指令。例如。 #/phones 路由将有一个类似&lt;phone-list&gt;&lt;/phone-list&gt; 的模板,而phoneList 指令将如下所示(不幸的是,您从未听说过隔离作用域):

.directive('phoneList', function phoneListDirective() {
  // DDO
  return {
    template:
        '<h2>Phones</h2>' +
        '<ol>' +
          '<li ng-repeat="phone in phones">' +
            '<b>{{ phone.name }}</b> (OS: {{ phone.os }})' +
          '</li>' +
        '</ol>',
    scope: true,
    link: phoneListPostLink
  };

  // Functions - Definitions
  function phoneListPostLink(scope) {
    scope.phones = [
      {id: 1, name: 'Samsung Galaxy', os: 'Android'},
      {id: 2, name: 'Google Nexus', os: 'Android'},
      {id: 3, name: 'Nokia Lumia', os: 'Windows'},
      {id: 4, name: 'Apple iPhone', os: 'iOS'}
    ];
  }
})

到目前为止,一切都很好。平板电脑的路线和指令几乎相同,一切正常。

很快,您的可用手机和平板电脑列表就会增加,您需要添加filter 功能。小菜一碟,您只需将以下 sn-p 添加到指令的模板中:

<div>
  Filter:
  <input type="search" ng-model="search.name" placeholder="Name..." />
  <input type="search" ng-model="search.os" placeholder="OS..." />
</div>
<li ng-repeat="phone in phones | filter:search">

就这么简单,您的用户可以按名称和操作系统搜索手机和平板电脑。生意兴隆,生活美满。

快进几个月,您的网站已经发展壮大,包含更多的部分和产品类别。您决定“全局搜索”小部件对您的导航栏来说是一个很好的补充。您需要做的就是将以下 sn-p 添加到您的主模板中:

<div class="search-widget">
  <input type="search" ng-model="query" placeholder="Search the entire site..." />
  <button ng-click="search(query)" ng-disabled="!query">Search</button>
</div>

(当然还要在你的主控制器上实现$scope.search() 方法...)

剩下的就是历史了:P
很快,销售额开始下降,您不知不觉就倒闭了。


这是一个简单的 POC,可以在实践中看到这一点:Demo


tl;dr
使用隔离范围和利润!

【讨论】:

  • 那幅插图太棒了!您为仔细说明这一点所付出的所有时间和精力——哇!我是否正确认为冲突的关键,因此错误,在于“过滤器:搜索”实际上运行在 AppCtrl 范围内声明的“搜索”函数,然后多次重新路由到搜索视图,对于数组中的每个项目?另外,我想知道为什么 Angular 在执行过滤器时不禁止路由。由于仅过滤模板中的数组而被重定向到其他页面是绝对出乎意料的:)
  • 冲突的关键是非隔离子作用域使用他们认为自己拥有/创建的对象(search),但是当父作用域暴露一个同名对象时,一切都崩溃了。 search 是一个函数,filter 可以接受对象或函数这一事实只是一个实现细节。该演示说明了许多可能的错误的一个示例。
  • 例如如果父作用域将search 定义为一个对象,您将有一个不同的错误,即过滤查询在视图中持续存在(因此为iPad 过滤平板电脑,然后切换到“电话”视图,将显示没有电话,因为它们将被{name: iPad} 过滤。(Demo 2) 另一个示例是尝试通过search.name 显式过滤并被函数具有name 属性的事实破坏(因此search.name 将是search默认)。(Demo 3
  • 主要的一点是,在一个带有几个指令和路由的小应用程序中,有很多事情可能会出错 - 想象一下这如何扩展到一个有几十个或数百个它们的实际应用程序.你不想在那里!
  • 阅读您的示例感觉更像是在喝优质葡萄酒。是的,我完全同意您对问题的总体概述,并且基本关键在于范围不被隔离。我也同意不断增长的复杂性加上非孤立的作用域会产生许多我无法想象的有趣但致命的错误。
【解决方案2】:

那段是由Georgios Kalpakas 添加为commit #c2033d7 on May 24

你可能想问他你的问题。

【讨论】:

  • 谢谢 George,您的建议最终得到了最佳答案——由 Georgios 本人提出。
【解决方案3】:

这里的教程可能有点夸张。至少它不是很精确。

我创建了一个simple example on plunker,它显示了哪些干扰是可能的,哪些是不可能的。

ng-controller 指令确实创建了一个新的子作用域。作用域上的变量在原型上由子作用域继承。

参考plunker example,这意味着控制器 1 定义的 $scope.someVariable 对控制器 2 定义的 $scope.someVariable 没有任何影响 - (控制器 1 既不是控制器 2 的祖先也不是后代)。这也意味着为 $scope.someVariable 设置的值不能被它们的父控制器覆盖,父控制器在其范围上设置相同的变量。控制器 3 也是父控制器的后代,它本身没有设置 $scope.someVariable。在这种情况下,plunker 显示父控制器设置的值在控制器 3 控制的视图 sn-p 中生效。在父控制器作用域的所有子作用域上,someVariable 将可用为
Object.getPrototypeOf($scope).someVariable .

尽管如此,我同意本教程,使用将其状态绑定到其控制器实例(在模板中称为 $ctrl)而不是直接使用作用域的组件有很多优点。组件有明确的进出口模式。这使它们可以互换,并增加了重复使用的机会。

【讨论】:

  • "教程在这里可能有点夸张。至少它不是很精确。"
  • 基本上,您的示例只考虑了一个非常具体的案例(这确实不是问题),但这并不能证明其他用例没有问题。
猜你喜欢
  • 1970-01-01
  • 2016-01-21
  • 2014-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多