【问题标题】:What's the correct way to update model and URL?更新模型和 URL 的正确方法是什么?
【发布时间】:2014-12-07 17:42:57
【问题描述】:

假设我在同一页面上有以下元素:

  1. 过滤器面板(类似于 http://www.imdb.com/search/name)
  2. 基于过滤器选项的项目

我想实现以下逻辑:

  1. URL 应在路径 (/appliedOptionA/appliedOptionB) 中包含应用的过滤器数据
  2. 当用户打开站点应用程序从 URL 获取过滤器数据时,更新过滤器 面板和项目面板
  3. 当用户更改过滤器应用更新 URL 并刷新项目时

第一个想法:配置ng-router,获取过滤数据作为参数,将数据转换为模型,构建过滤器面板,加载项目,构建项目面板。 主要问题:当用户更改过滤器时它应该如何工作?如果我要更新 URL,它将触发具有不同参数的相同控制器并重复该过程 -> 获取过滤器数据作为参数,将数据转换为模型,构建过滤器面板,项目等。我不需要它 - 我的模型和 UI 已经是最新的了。

我的问题:

保持模型和 URL 同步的正确方法是什么? URL 管理模型 (ng-route) 还是模型更改管理 URL(如何?)?

【问题讨论】:

    标签: angularjs url model routing


    【解决方案1】:

    我的建议是使用angular-ui-router 并使用它而不是ng-router,因为angular-ui-router 为您提供了更大的灵活性,并且在我看来更易于使用。

    鉴于您的“主要问题”,听起来您希望有一个应用程序控制器(我们将其称为"appCtrl")处理所有更改并对其模型进行必要的更改。这种方法的主要缺点是它迫使您在appCtrl 中维护“filteredItems”模型,这并不有趣,并且可能导致不必要的麻烦。一种更简单的方法是让另一个控制器处理对 url 的这些更改,而不是 appCtrl 本身。话虽如此,让我向您展示如何做到这一点。

    *警告:帖子很长! (但希望有帮助)*

    因此,我还创建了一个我将要讨论的所有内容的演示,以防你“只想要 codez”:

    创建应用程序

    由于我们正在创建一个全新的应用程序,让我们从创建和配置应用程序开始。

    注意:请记住将angular-ui-router 脚本加载到您的应用程序中并添加依赖项。

    app.js

    var myApp = angular.module('myApp', ["ui.router"]);
    

    现在我们有了app,让我们使用ngApp directive 将它添加到index.html。

    index.html

    <body ng-app="myApp"></body>
    

    应用到位后,让我们看看您在问题中解决的问题:

    • URL 应在路径 (/appliedOptionA/appliedOptionB) 中包含应用的过滤器数据
    • 当用户打开站点应用时,从 URL 获取过滤器数据,更新过滤器面板和项目面板
    • 当用户更改过滤器应用更新 URL 并刷新项目时

    您要做的是创建一个state。本质上,状态是根据 url 模式激活/停用的。给定一个特定的模式,您可以告诉应用程序如何操作(使用控制器)和外观(使用模板)。在继续之前,请确保引用 url routing,这将有助于 url 模式。

    我们需要:

    • 为状态命名 ("filteredItems")
    • 提供网址格式
    • 提供控制器
    • 提供templateUrl

    让我们配置应用程序,以及:

    • 在url不存在的情况下提供默认url路由
    • 添加默认home状态
    • 添加filteredItems 状态

    app.js

    myApp.config(function($stateProvider, $urlRouterProvider){
    
    // default url - used if url pattern is not recognized
     $urlRouterProvider.otherwise('/'); 
    
     $stateProvider
        .state('home', { // default state
          url: '/'
        })
        .state('filteredItems', { // give the state a name
            url: "/:appliedOptionA/:appliedOptionB", // provide the url pattern
            controller: 'FilteredItemsCtrl', // controller to use for this state
            templateUrl: "filteredItems.html" // url for the template
        });
    });
    

    我们现在已经将状态的 url 配置为接受参数(appliedOptionAappliedOptionB)。使用$stateParams service,我们可以创建一个filteredItemsCtrl,并让它负责过滤掉数据。

    但在我们这样做之前,我们应该创建一个服务。使用 Angular 时,避免使用控制器来维护数据,因为它们可以被创建/销毁。最佳实践是改用服务。话虽如此,让我们创建一个惊人的itemService

    itemService.js

    angular.module('myApp').factory('itemService', itemService);
    
    function itemService (){
      var items = {
          appliedOptionA: [ 'Item 1', 'Item 2', 'Item 3' ],
          appliedOptionB: [ 'Item 4', 'Item 5', 'Item 6' ]
        };
    
        function getItems (){
          return items;
        }
    
        return {
          getItems: getItems
        }
    }
    

    很漂亮吧?我很高兴你也这么认为!

    现在我们有了服务,我们可以创建FilteredItemsCtrl。让我们也将我们新创建的itemService 注入到FilteredItemsCtrl 中,这样它就可以访问数据了。

    注意:记得将$stateParams 服务作为依赖注入!

    filteredItemsCtrl.js

    angular.module('myApp').controller('FilteredItemsCtrl', filteredItemsCtrl);
    
    filteredItemsCtrl.$inject = ['$stateParams', '$scope', 'itemService'];
    
    function filteredItemsCtrl ($stateParams, $scope, itemService){
      var items = itemService.getItems(); // get items from service
    
        // filter items
        $scope.appliedOptionA = items.appliedOptionA.filter(function (item){
          if ($stateParams.appliedOptionA){
            return item.toLowerCase().indexOf($stateParams.appliedOptionA) > -1 ||
                   item.indexOf($stateParams.appliedOptionA) > -1
          }
        });
        $scope.appliedOptionB = items.appliedOptionB.filter(function (item){
          if ($stateParams.appliedOptionB){
            return item.toLowerCase().indexOf($stateParams.appliedOptionB) > -1 ||
                   item.indexOf($stateParams.appliedOptionB) > -1
          }
        });
    }
    

    当我们定义我们的状态的 url 时,我们将其设置为:url: "/:appliedOptionA/:appliedOptionB"。这意味着$stateParams 将填充应用选项A、应用选项B 属性。这也意味着每次 URL 更改时,都会创建一个 new filteredItemsCtrl,因此,您不必维护应用程序的模型!

    在我们忘记之前,我们还需要为这个状态创建一个模板。

    filteredItems.html

    <div>
      <div>
         <span>Filtered Option A Items:</span>
        <ul>
          <li ng-repeat="optionItemA in appliedOptionA">
            {{ optionItemA }}
          </li>
        </ul>
      </div>
    
      <div>
        <span>Filtered Option B Items:</span>
        <ul>
          <li ng-repeat="optionItemB in appliedOptionB">
            {{ optionItemB }}
          </li>
        </ul>
      </div>
    </div>
    

    创建状态、filteredItemCtrl 和视图后,剩下的就是包含 ui-view 指令,以使状态的模板正确显示:

    index.html

     <body ng-app="myApp"
          ng-controller="MyAppCtrl">
          <div>
            <div>
                Applied Option A: <input ng-model="appliedOptionA" />
              </div>
              <div>
                Applied Option B: <input ng-model="appliedOptionB" />
              </div>
    
              <button type="button"
                      ng-click="filter()">Search</button>
          </div>
    
          <!-- where the state template goes -->
          <div ui-view></div>
        </body>
    

    这就是它的全部内容!很抱歉这篇文章很长,但希望你能发现这个信息!如果您有任何问题,请告诉我!

    【讨论】:

      【解决方案2】:

      如果您希望搜索关键字反映在 URL 中(以便可以将它们添加为书签并直接寻址),我建议不要强制更改模型(每次击键后),而是等待提交表格。然后你重写 URL(通过 $location.path 和/或 $location.search),ngRoute 就会启动。

      NgRoute 将(重新)加载/刷新页面,就像它是第一次一样,这与包含搜索关键字的可直接寻址页面的想法相匹配。我不建议您在提交后立即获取数据,因为正如您所说,ngRoute 会刷新页面。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-02-07
        • 1970-01-01
        • 2012-09-04
        • 2011-07-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多