【问题标题】:Angularjs scope not retaining valueAngularjs范围不保留价值
【发布时间】:2013-07-13 12:57:59
【问题描述】:

朋友..

为了了解路由在 Angular 中的工作原理,我创建了一个简单的应用程序。此应用程序只有两个页面: 1.第一页将显示员工表的所有行。单击特定行后,第二页将显示包含该员工详细信息的表单。

第一页显示的列表使用如下代码:

<table>
   <tr ng-repeat="employee in employees">
       <td>{{employee.firstname}} - {{employee. address}}</td>           
       <td><span ng-click="getSingleEmployeeDetails(employee.id)">Edit</a></td>
   </tr>
</table>

我对这两个页面使用相同的控制器,这个控制器如下所示:

function EmployeeCtrl($scope,$http,Employee,$location,$routeParams) {
    // Get all employee details 
    var data;
    Employee.query().then(function(_data) {
      $scope.employees = _data.data;            
    }); 

    // Get Single Employee Details
    $scope.getSingleEmployeeDetails = function(id) {
      $scope.employee = scope.employees[id];
      $location.path('/editemployee/' + id);
    }   
}

但是我面临的问题是,当代码被路由到 /editemployee/1
由于某种原因,$scope.employees 失去了它的价值。

换句话说,表单永远不会填充员工详细信息。

我在这里做错了什么?

【问题讨论】:

  • 如果两个视图的控制器类型相同,并不意味着它们是同一个实例。所以问题是每次角度更改路由时,它都会创建一个新的控制器实例,该实例没有前一个实例所具有的任何信息。所以你需要使用一个服务来拥有一个可以存储数据的单例。
  • 实际上,如果您查看 John Lindquist 的这个示例,他似乎在更改路由时将一个控制器的 $scope 用于另一个控制器。他怎么能做到这一点? youtube.com/…
  • 如果两个作用域同时处于活动状态,则会发生作用域继承。尽管在 egghead.io 上完全推荐 John 的视频,但这是我第一次接触 Angular,我仍然会回头。
  • 理解范围继承的细节真的只需要很好地理解原型继承(特别是如果你来自 Class(y) 继承模型)。简而言之,如果你从你的祖先那里继承了一个属性,那么当有东西试图读取你身上的那个属性时,它将从你的祖先那里读取,但是如果直接在你的实例上分配,它将不再依赖祖先来获取值。这意味着事情可能会以奇怪的方式“打破”(第一次工作)。

标签: angularjs


【解决方案1】:

这与范围界定有关。雇员在实例化时被加载到 EmployeeCtrl 中。一旦你在 getSingleEmployeeDetails() 中执行一个路由事件,导致不同的控制器加载不同的 $scope。一个与 EmployeeCtrl 中的 $scope 分开的 $scope。解决此问题的一种简单方法是让 EmployeeCtrl 处理加载/显示所有员工和单个员工的功能,而无需路由到新控制器。这里的优点是它更容易共享信息,并且当用户单击单个员工时,您不必重新加载单个员工信息,因为您可以更轻松地共享该信息。缺点是您没有返回按钮导航来在单个员工的选择之间导航。

另一个选项是让 SingleEmployeeCtrl 在导航时重新加载信息。优点是您再次获得返回按钮访问权限,但缺点是您加载信息两次(一次用于加载完整列表,两次用于再次加载员工信息)。这也允许用户为单个员工记录添加书签,但是谁再添加书签呢?

【讨论】:

  • 第三个选项服务用作单例和 A) 返回数据的承诺或 B) 在控制器中是 $watch(ed)。现在是您自己选择的冒险。
  • 为了暂时保持简单,我试图实现第一个结果,即让单个控制器处理事情。我该怎么做。从加载 EditEmployee.html 页面的那一刻起,控制器就失去了它的 $scope。
  • @JamesHans 当某些变量设置为特定值时,您可以使用 ng-include 指令或 ng-if 指令来包含页面/视图。这样,您将从路线中删除页面条目,而不是更新 $location 您只需更新您的变量。正如chubbsondubs 在这里指出的那样,这“破坏”了路由/深度链接。要修复路由,您还可以侦听路由更改事件并手动更新控制器中的变量(虽然有点 hack)
  • 虽然你可以做一个 ng-include。我试图描述的是将 EditEmployee.html 中的标记组合到与控制器已经使用的 html 相同的文件中。因此,您可以轻松地在父 div 上使用 ng-show="selectedEmployee != null" 来显示/隐藏它。当然在用户点击员工表时设置 selectedEmployee 。深度链接可能无关紧要。更多时候不会。
【解决方案2】:

其他人已经解释了当您更改路由时会创建一个新控制器(和 $scope)的事实。另请注意,当 promise 解决时,$scope.employees 是异步填充的。可能发生的情况是在解决承诺之前调用 getSingleEmployeeDetails(),因此 employees 数组为空。

为了解决这个问题,我建议使用不同的架构。

您有两个视图/页面。 Angular 中的每个视图通常都有自己的控制器。模型/数据通常存储在服务中,并且用于检索和操作这些模型/数据的 API 由服务提供/公开。控制器只是将所有东西粘合在一起:它注入它需要的服务,然后引用关联视图需要的模型/数据。

因此,即使您的应用程序很简单,我还是建议采用上述方法:一个服务(存储您的员工对象)、两个控制器、两个视图。特别是,将query() 调用放入您的服务中(因此在创建服务时将调用一次)并将您的数据存储在服务中。服务 API 应定义返回承诺的函数/方法,该承诺最终将包含所需数据(员工列表或仅一个)。控制器应该使用这些方法来获取对所需数据的引用。

有关如何在服务中存储数据的示例,另请参阅 Should services expose their asynchronicity?

【讨论】:

  • 谢谢马克.. 这个架构为我清除了一些要点。我现在已经包含了一项服务,但不确定如何在该服务中包含 POST 调用。请检查我修改后的问题
  • @JamesHans,将修改后的员工对象作为参数传递给您在服务上定义的 API 方法。该服务方法将代表控制器调用 $http.post()。
猜你喜欢
  • 2014-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多