首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >AngularJS REST应用程序设计优化

AngularJS REST应用程序设计优化
EN

Stack Overflow用户
提问于 2014-02-24 14:20:08
回答 1查看 356关注 0票数 2

我正在开发一个使用AngularJS的小页面应用程序和一个由Django REST框架驱动的Rest后端。

到目前为止,一切都很简单,但在我更进一步之前,有些地方感觉“不对”.

到目前为止,应用程序管理与城市相关的项目列表。

因此,我使用$resource实现了两个工厂: Project。

  • GET /projects返回json中的所有项目
  • GET /cities返回json中的所有城市

现在,我使用ui-路由器在应用程序中导航,状态如下:

  • 项目:显示项目列表
  • 项目/{id}:显示一个项目
  • 项目/编辑/{ id }:创建一个新项目(如果id为null)或更新项目

为城市而设。

现在,我确实为每个状态关联了一个控制器,基本上每次状态更改以获得项目列表或单个项目时,我都在查询后端。

1)我认为,通过在我的Project.query()中调用City.query()和app.run()并将它们保存在$rootScope中,维护项目1和所有项目的列表是有意义的。

现在,每当我更新或删除一个对象时,我都需要在整个$rootScope.projects上迭代(forEach() ),寻找匹配的id并相应地更新/删除。

在打开单个项目(/ project /{id})时,我也需要在$rootScope.projects中搜索以找到项目。

这可以吗,还是在这样的操作中始终与服务器同步是最好的做法?目前只有一个用户编辑项目,但这种情况在将来可能会改变。

2)由于我的项目中需要有城市名称(而不仅仅是id),所以当我获得/projects时,我会收到一个嵌套的城市对象,例如{id: 1,name:'New York'}。当我更新/创建时,我只使用id发送我的项目的平面表示(例如,城市: 1)。如果一切顺利,服务器将回复201 OK和附加的项目。问题是附加的项目也是平面的,所以当我更新$rootScope.projects时,我需要:

  • 首先找到要更新的项目的id。
  • 循环遍历各个城市,以查找哪个城市与此项目相关。
  • 用上面找到的城市对象替换project.city。

这样就好了..。但可能不是最理想的。有更好的方法吗?我可以有一个平面获取并使用$rootScope.cities相应地填充模板。

3)如果我在只有一个项目的状态下直接打开应用程序,比如/#/ project /1,则控制器试图在GET完成之前在$rootScope.projects中找到项目id 1。我补充说:

代码语言:javascript
复制
if(!$rootScope.$$phase) { //this is used to prevent an overlap of scope digestion
  $rootScope.$apply(); //this will kickstart angular to recognize the change
}

在这两个查询()调用之后,它就能工作了。只是想知道这是否是个好练习..。

好吧,我很想听听你们中一些已经经历过这件事的人。我可以提供更多的代码,但正如我说的,它是工作的,所以我更多地寻找一个反馈的角度应用程序的实际设计。

谢谢!

编辑1

谢谢你的回复,我增加了一项服务,我甚至不需要$watch,似乎工作得很好。我想分享一些代码,以指出一些不好的做法或东西,可以改进。我发现很难找到完整的例子。

所以这里,它是非常直接的,我希望评论足够清楚。基本上,这就是我的projects.js脚本,它定义了用于通过REST与服务器通信的项目资源,处理控制器和Porject资源之间的通信的ProjectService,以及应用程序不同状态下使用的不同控制器(列表项目、视图项目和编辑项目)。

代码语言:javascript
复制
/**
 * Controllers and Resource manager for projects.
 */


/**
 * This is the resource link to the REST framework.
 * Adapted the "update" method (available view $update) to
 * use the PUT method as per Django Rest requirements
 */
angularApp.factory('Project', ['$resource', function($resource){
  return $resource('/api/projects/:id', {id: '@id'}, {
    update: {method:'PUT', params: {id: '@id'}},
  });
}]);

/**
 * This Service handles the project management
 */
angularApp.factory('ProjectService', ['Project', function(Project) {
  var projectsLoaded = false,
      projects = [];

  return {

    /**
     * Returns the complete list of the projects
     * from the server.
     * If the projects have already been loaded, then
     * use the cache instead.
     */
    getProjects: function() {
      if (projectsLoaded) {
        return projects;
      } else {
        projects = Project.query(function(){
          projectsLoaded= true;
        });
        return projects;
      }
    },

    /**
     * Load a single project from the server.
     * If the full list has already been loaded, then
     * find it in the list instead
     *
     * @param Integer projectId
     */
    getSingleProject: function(projectId) {
      var toReturn = false;
      if(!projectsLoaded) {
        toReturn = Project.get({id: projectId});
      } else {
        projects.forEach(function(project, index) {
          if(project.id == projectId) {
            toReturn = project;
          }
        });
      }
      return toReturn;
    },

    getNewProject: function() {
      return new Project();
    },

    /**
     * Deletes a project.
     * If the project list is already loaded, then update the list
     * accordingly
     *
     * @param Project project : project to delete
     * @param callbackSuccess function(result)
     * @param callbackRejection function(rejection)
     */
    delete: function(project, callbackSuccess, callbackRejection) {
      project.$delete().then(function(result){
        if(projectsLoaded) {
          projects.forEach(function(project, index) {
            if(project.id == result.id) {
              projects.splice(index, 1);
            }
          })
        };
        callbackSuccess(result);
      }, function(rejection) {
        callbackRejection(rejection);
      });
    },

    /**
     * Creates a new project.
     * If the project list is loaded, then add the newly created
     * project to the list.
     *
     * @param Project projectToSave : the project to save in the database
     * @param callbackSuccess function(result) : result is the value returned by the server
     * @param callbackRejection function(rejection)
     */
    save: function(projectToSave, callbackSuccess, callbackRejection) {
      projectToSave.$save().then(function(result) {
        if(projectsLoaded) {
          projects.unshift(result);
        }
        callbackSuccess(result);
      }, function(rejection) {
        callbackRejection(rejection);
      });
    },

    /**
     * Updates a project, also updates the list if needed
     *
     * @param Project projectToUpdate to update
     * @param callbackSuccess function(result)
     * @param callbackRejection function(rejection)
     */
    update: function(projectToUpdate, callbackSuccess, callbackRejection) {
      projectToUpdate.$update().then(function(result){
        if(projectsLoaded) {
          projects.forEach(function(project, index) {
            if(result.id == project.id) {
              project = result;
            }
          })
        }
        callbackSuccess(result);
      }, function(rejection) {
        callbackRejection(rejection);
      });
    },


  };

}]);

/**
 * Controller to display the list of projects
 */
angularApp.controller('ProjectListCtrl', ['$scope', 'ProjectService', function ($scope, ProjectService) {
  $scope.projects = ProjectService.getProjects();
}]);


/**
 * Controller to edit/create a project
 */
angularApp.controller('ProjectEditCtrl', ['$scope', '$stateParams', 'ProjectService', '$state', function ($scope, $stateParams, ProjectService, $state) {
  $scope.errors = null;
  if($stateParams.id) {
    $scope.project = ProjectService.getSingleProject($stateParams.id)
  } else {
    $scope.project = ProjectService.getNewProject();
  }

  $scope.save = function() {
    ProjectService.save($scope.project,
      function(result) {
         $state.go('project_view', {id:result.id});
      }, function(rejection) {
        $scope.errors = rejection.data;
      }
    );
  };

  $scope.update = function() {
    ProjectService.update($scope.project,
      function(result) {
         $state.go('project_view', {id: result.id});
      }, function(rejection) {
        $scope.errors = rejection.data;
      }
    );
  };

}]);

/**
 * Controller to show one project and delete it
 */
angularApp.controller('ProjectCtrl', ['$scope', '$stateParams', 'ProjectService', '$state', function($scope, $stateParams, ProjectService, $state) {
  $scope.project = ProjectService.getSingleProject($stateParams.id)

  $scope.delete = function() {
    ProjectService.delete($scope.project,
      function(result){
        $state.go('projects')},
      function(rejection){
        console.log(rejection)
      }
    );
  }
}]);
EN

回答 1

Stack Overflow用户

发布于 2014-02-24 16:18:04

你有一个很好的开端,你只需要在角度上再深入一点,一切都会井然有序。

首先,使用服务/工厂/提供者。它们的功能非常类似于资源,可以被注入,并且作为单身汉工作。您应该始终使用服务而不是$rootScope作为最佳实践,尽管它们的工作方式类似,因为您不能在服务上犯那么多愚蠢的错误,代码也会更干净。

例如,对于问题1,您可以为您的项目和城市在幕后使用您的项目和城市资源提供服务;该服务将充当您的数据存储单例而不是$rootScope,并且它可以提供方便的方法,这样使用者就不必手动执行query()调用。

对于问题2,将取决于您是只返回更改的项目还是返回服务器上的所有项目。我建议返回所有项目以避免您发现的问题,或者让服务器接受一个参数,让使用者选择他们想要返回的数据。

对于问题3,根据经验,如果您必须手动调用$apply(),您可能做错了什么。只有在执行角度框架之外的代码(比如使用$apply()方法或处理自定义事件)并需要更新模型以响应您所做的事情时,您才应该调用jQuery。在您的例子中,由于您使用的是角的资源,所以不需要调用$apply()。

我认为您真正想要做的是$watch()您的数据服务以进行更改。这基本上就像调用$apply()一样,但区别在于,您可以从角度上决定何时需要更新,这样可以更高效、更干净和更安全。下面是一个假设的例子,说明您如何设置它:

代码语言:javascript
复制
function MySuperController($scope, DataService){
    //get data from the service singleton
    $scope.projects = DataService.getCoolProjects();

    //watch for changes in that data
    $scope.$watch(
        //thing to watch
        //Will get called a LOT, so make sure it's not time-intensive!
        function(){
            return DataService.getCoolProjects();
        },
        //what to do on change
        function(changedData){
            $scope.projects = changedData;
            //no $apply() needed; angular will do automatically!
        },
        //do a 'deep' watch that checks the value of each project
        true
    );
}

简单地说,下面是将要发生的事情:

  • 您将发出GET请求,它将检索数据并将其存储在DataService中。
  • 角将检查它的$watches
  • 您的$watch会注意到DataService.getCoolProjects()与过去不同
  • 您的代码将更改$scope的一个属性。
  • 角将自动应用您的变化,像往常一样。

试试看它们是如何工作的。一旦您掌握了服务和$watch的诀窍,您就会发现编写好的角度代码总是工作很容易,而不仅仅是“意外”。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21990409

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档