我正在开发一个使用AngularJS的小型单页面应用程序和一个由Django Rest Framework提供支持的REST后端。
到目前为止它相当简单,一切正常,但在我继续前进之前,有些观点并不“正确”......
到目前为止,该应用程序管理与城市相关联的项目列表。
所以我使用$ resource实现了2个工厂:Project和City。
现在我使用ui-router在应用程序中导航并执行以下操作:
城市一致。
现在我确实为每个州关联了一个控制器,基本上我每次状态改变时都会查询后端以获取项目列表或单个项目。
1)我认为通过调用app.run()中的Project.query()和City.query()并将它们保存在$ rootScope中来维护项目列表是有意义的。
现在evertime我更新或删除了一个对象,我需要在整个$ rootScope.projects上迭代(forEach()),寻找匹配的id并相应地更新/删除。
当打开一个项目(/ project / {id})时,我需要搜索$ rootScope.projects来查找项目。
这样可以,或者最好在这种操作下始终与服务器同步?目前只有一个用户编辑项目,但将来可能会改变。
2)因为无论如何需要在我的项目中拥有城市名称(而不仅仅是id),当我获取/项目时,我会收到城市的嵌套对象,例如: {id:1,姓名:'纽约'}。当我更新/创建时,我只用id(例如城市:1)发送我的项目的平面表示。如果一切顺利,服务器回复201 OK并附加项目。问题是附加的项目也是平的,所以当我更新$ rootScope.projects时,我需要:
这没关系...但可能不是最佳的。有没有更好的方法呢?我可以进行平坦的GET并使用$ rootScope.cities来相应地填充模板。
3)如果我只在一个项目的状态下直接打开应用程序,比如/#/ project / 1,控制器会在GET完成之前尝试在$ rootScope.projects中找到项目ID 1。我添加了:
if(!$rootScope.$$phase) { //this is used to prevent an overlap of scope digestion
$rootScope.$apply(); //this will kickstart angular to recognize the change
}
在两个query()调用之后,它都有效。再次想知道这是不错的做法......
好吧,我有兴趣听到一些已经对此进行了阐述的人的回复。我可以提供更多的代码,但正如我所说的那样,我正在寻找有关角应用程序实际设计的反馈。
谢谢!
编辑1
感谢您的回复,我已经添加了一项服务,我甚至不需要观看,似乎完美无缺。我想分享一些代码,只是为了指出一些不好的做法或可以改进的东西。我发现很难找到完整的例子......
所以,你走了,这是非常直截了当的,我希望这些评论足够清楚。基本上这是我的projects.js脚本,它定义了用于通过REST与服务器进行通信的Project资源,用于处理控制器和Porject资源之间通信的ProjectService,以及用于应用程序的不同状态的不同控制器(列表)项目,查看项目和编辑项目。)
/**
* 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)
}
);
}
}]);
答案 0 :(得分:2)
你有一个良好的开端,你只需要深入挖掘Angular,一切都将落实到位。
首先,use Services/Factories/Providers。它们的功能很像资源,可以注入,它们可以作为单身人士使用。您应该始终使用服务而不是$rootScope
作为最佳实践,即使它们的工作方式类似,因为您无法通过服务制造尽可能多的愚蠢错误,并且您的代码将更加清晰。
例如,对于问题1,您可以为在幕后使用您的项目和城市资源的项目和城市提供服务;此服务将作为您的数据存储单例而不是$rootScope
,并且它可以提供便利方法,因此消费者不必手动执行query()
次呼叫。
对于问题2,由您决定是仅返回已更改的项目还是服务器上的所有项目。我建议退回所有项目以避免您找到的问题,或者让服务器接受一个参数让消费者选择他们想要返回的数据。
对于问题3,根据经验,如果您必须手动调用$apply()
,您可能会做错事。如果您在Angular框架之外执行代码(例如,如果您正在使用jQuery方法或处理自定义事件)并且需要更新模型以响应您的内容,那么您应该只调用$apply()
没有。在您的情况下,由于您使用的是Angular资源,因此您不需要调用$ apply()。
我认为您真正想做的是$watch()
您的数据服务以进行更改。这基本上就像调用$apply()
一样,但区别在于您让Angular确定何时需要更新,这可能更高效,更清洁,更安全。这是一个假设的例子,说明如何设置它:
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
);
}
现在,简而言之,这将会发生什么:
给他们一个机会,看看他们是如何工作的。一旦掌握了服务和观看,您就会发现编写一直有效的Angular代码要容易得多,而不仅仅是&#34;意外&#34;。< / p>