以下是我正在尝试创建的工作流程:
Angular应用程序加载。设置了一个“otherwise”路由。
当有人点击链接时,我不知道资源是产品还是类别,因此会使用唯一的“.html”标识符向查找端点发出查询。该端点返回两个内容:资源名称和ID(例如“product”和“10”)。因此,为了明确起见,他们打开了类似“http://www.example.com/some-identifier.html”的页面,我查询查找API以找出这是哪种类型的资源,并获得像“product”和“10”的结果--现在我知道它是产品控制器/模板,我需要从产品ID 10获取数据。
应用程序分配控制器和模板(“productController”和“product.html”),为正确的数据端点查询数据(“/api/product/10”),并呈现模板。
我面临的问题:
$http在配置期间不可用,因此我无法访问查找表。
在配置之后添加路由最好是松散的 -- 我曾经通过将$routeProvider分配给全局变量并在事后执行它来成功实现,但是,这很丑陋。
加载所有路由似乎不切实际 -- 对于许多连接/浏览器,文件大小会相当大。
我们现在不能改变惯例。我们有4年的SEO和大量的有机流量来放弃我们的URL。
我觉得我可能想错了,还有一些问题没有解决。查找表确实是问题所在 -- 不知道要加载什么样的资源(产品、类别等)。我阅读了this article关于动态加载路由,但是他也没有进行外部查询。对于我们来说,加载控制器不是问题,问题在于解析路由然后将它们分配给c
你会如何解决这个问题?
解决方案
非常感谢@user2943490为我指明了正确的方向。别忘了给他点赞! 我将其更加通用化,以便不必定义路由类型。 API结构此配置至少需要两个端点:/api/routes/lookup/:resource_to_lookup:/
和/api/some_resource_type/id/:some_resource_id:/
。我们查询查找以了解它指向哪种资源以及资源的ID。这使您可以拥有漂亮干净的URL,例如"http://www.example.com/thriller.html"(单个)和"http://www.example.com/michaeljackson.html"(集合)。
在我的情况下,如果我查询类似"awesome_sweatshirt.html"这样的东西,我的查询将返回一个JSON对象,其中"{type:'product',id:10}"。然后我查询"/api/product/id/10"以获取数据。
“这不会很慢吗?”你问道。有了Varnish,所有这些操作都在不到1秒的时间内完成。我们在本地看到的页面加载时间少于20毫秒。从一个缓慢的开发服务器上访问时,加载时间接近半秒钟。app.js
var app = angular.module('myApp', [
'ngRoute'
])
.config(function($routeProvider, $locationProvider) {
$routeProvider
.otherwise({
controller: function($scope, $routeParams, $controller, lookupService) {
/* this creates a child controller which, if served as it is, should accomplish your goal behaving as the actual controller (params.dashboardName + "Controller") */
if ( typeof lookupService.controller == "undefined" )
return;
$controller(lookupService.controller, {$scope:$scope});
delete lookupService.controller;
//We have to delete it so that it doesn't try to load again before the next lookup is complete.
},
template: '<div ng-include="templateUrl"></div>'
});
$locationProvider.html5Mode(true);
})
.controller('appController', ['$scope', '$window', '$rootScope', 'lookupService', '$location', '$route', function($scope, $window, $rootScope, lookupService, $location, $route){
$rootScope.$on('$locationChangeStart', handleUniqueIdentifiers);
function handleUniqueIdentifiers (event, currentUrl, previousUrl) {
window.scrollTo(0,0)
// Only intercept those URLs which are "unique identifiers".
if (!isUniqueIdentifierUrl($location.path())) {
return;
}
// Show the page load spinner
$scope.isLoaded = false
lookupService.query($location.path())
.then(function (lookupDefinition) {
$route.reload();
})
.catch(function () {
// Handle the look up error.
});
}
function isUniqueIdentifierUrl (url) {
// Is this a unique identifier URL?
// Right now any url with a '.html' is considered one, substitute this
// with your actual business logic.
return url.indexOf('.html') > -1;
}
}]);
lookupService.js
myApp.factory('lookupService', ['$http', '$q', '$location', function lookupService($http, $q, $location) {
return {
id: null,
originalPath: '',
contoller: '',
templateUrl: '',
query: function (url) {
var deferred = $q.defer();
var self = this;
$http.get("/api/routes/lookup"+url)
.success(function(data, status, headers, config){
self.id = data.id;
self.originalPath = url;
self.controller = data.controller+'Controller';
self.templateUrl = '/js/angular/components/'+data.controller+'/'+data.controller+'.html';
//Our naming convention works as "components/product/product.html" for templates
deferred.resolve(data);
})
return deferred.promise;
}
}
}]);
productController.js
myApp.controller('productController', ['$scope', 'productService', 'cartService', '$location', 'lookupService', function ($scope, productService, cartService, $location, lookupService) {
$scope.cart = cartService
// ** This is important! ** //
$scope.templateUrl = lookupService.templateUrl
productService.getProduct(lookupService.id).then(function(data){
$scope.data = data
$scope.data.selectedItem = {}
$scope.$emit('viewLoaded')
});
$scope.addToCart = function(item) {
$scope.cart.addProduct(angular.copy(item))
$scope.$emit('toggleCart')
}
}]);