AngularJS: 带有加载条的路由






4.96/5 (6投票s)
一个使用GitHub的Angular应用程序,主要用于演示angular-loading-bar和ui-router的使用,以便在较长的请求期间提供更好的反馈。
引言
在开发Angular应用程序时,请求有时需要一些时间来处理。您会给用户什么样的指示来表明请求仍在加载?如果您开发了自定义解决方案,如何使其在所有HTTP请求中都可重用?
我们将使用angular-loading-bar
库来解决这个问题。页面顶部有一个小的进度条,类似于您最近在YouTube上看到的。
此外,我们将使用ui-router库
。这将使我们能够创建一个灵活的路由系统。我将在本文后面详细介绍。
最后,我们将构建一个体面的AngularJS应用程序,其中包含一些新概念,如一次性绑定和控制器别名。我们还将使我们的应用程序模块化。
必备组件
- AngularJs - 我不会介绍模块、控制器、服务和指令等简单概念。这些概念不是此项目的目标,但具备一些知识将有帮助。
- JavaScript/HTML - 当然
我们要构建什么:一个简单的GitHub应用程序(噔!噔!噔!)
为了涵盖这些示例,我们将构建一个非常简单的GitHub应用程序,该应用程序将搜索用户的仓库,并允许用户查看一些仓库的详细信息。应用程序外观如下。
这是一个非常简单的应用程序,主要用于涵盖一些简单概念。
背景
在回顾代码之前,我想先介绍一些概念。
IIFE 模式
我使用了这种模式以及“use strict
”来保持代码的整洁、模块化,并将其与全局作用域隔离开。IIFE 是一个在页面加载时执行的匿名函数。代码被包装在匿名函数中,以避免与全局作用域发生任何冲突。示例如下:
(function () {
'use strict';
// Any code you execute here gets executed upon script load.
}());
AngularJs 一次性绑定 {{::$scope.title}}
如果您还不知道,这是v1.3中的一项新功能。“::
”允许您进行一次性绑定。您应该谨慎使用此功能。您不希望错误地对您可能期望稍后更新的属性进行一次性绑定。另一方面,在可能的情况下使用它,例如当绑定的数据实际上是静态的时,比如一个百万行的表。(提示:避免这样!)
AngularJs 控制器“as”
这是v1.2中引入的一项功能。它允许您在模板HTML中使用控制器中的“this
”作用域对象,而不是注入$scope
。您可以在此处阅读更多内容。该项目中的控制器使用此语法,而不是$scope
。
angular-loading-bar
这是一个很酷的项目,我真的很喜欢。将“angular-loading-bar
”注入您的应用程序后,它会自动跟踪$http
和$resource
请求,并显示加载条。加载条会自动显示,您无需做任何事情!
var app = angular.module("gitHubApp", ["angular-loading-bar"]);
ui-router
这是一个基于状态的Angular路由系统。与ng-router
不同,它不基于URL,而是基于应用程序的状态。您可以定义状态,例如:productEdit
、productView
、productsAll
。对于每个状态,您可以指定一个控制器、模板、解析(resolve)、抽象(abstract)和其他一些属性。UI-router还支持使用部分状态(partial states)进行视图分割。这个概念超出了本文的范围。您可以在此处阅读有关部分状态的更多信息。
定义路由
此代码已注释,以便于理解。
var app = angular.module("gitHubApp", ["ui.router"]);
app.config(
[
"$stateProvider",
function ($stateProvider) {
$stateProvider
// The search state : loads the search page
.state("search", {
// The Url to set for this state
url: "/",
// The template to load
templateUrl: "app/github/views/searchView.html",
// Set the controller using the "as" feature released in v1.2.
controller: "SearchController as vm"
})
// Repository Detail state
.state("repoDetail", {
// Set the url and accept two query string parameters.
url: "/repo/:username/:repoName",
templateUrl: "app/github/views/repoDetailView.html",
controller: "RepoDetailController as vm",
// Resolve : This is called before the template or controller is loaded.
// This is perfect since it makes the $http request
// and the loading bar shows
// but the template loads after our data loads.
resolve: {
// DI, inject this service into repoDetail() below
gitHubService: "gitHubService",
// repoDetail is another DI method, which will be resolved
// and injected into our controller.
// $stateParams is automatically injected into the function
repoDetail: function (gitHubService, $stateParams) {
return gitHubService.getRepoInfo
($stateParams.username, $stateParams.repoName);
}
}
});
}
]);
在本文的下面,您可以找到仓库视图控制器(Repository View Controller)的代码,并了解resolve如何注入repoDetail
。
从控制器加载状态
在控制器中,您可以通过注入$state
然后调用$state(stateName, {optionStates});
来加载新状态。
示例
// Create out controller and inject the $state
angular.module("gitHubApp").controller("SearchController", ["$state", searchController]);
function searchController($state) {
var vm = this;
vm.viewDetail = function() {
// Specify the stateName and pass in the parameters required by that state.
$state.go("repoDetail", {username: vm.username, repoName: vm.repoName});
// If no paramaters, then simple $state.go("repoDetail");
}
}
从视图加载状态
您可以使用ui-sref
或data-ui-sref
来指定状态和附加参数。
<a ui-sref="repoDetail({username: result.owner.login, repoName: result.name})">
{{::result.full_name}}</a>
<a ui-sref="search">go back</a>
Using the Code
您已经在上面ui-router
和angular-loading-bar
部分的代码中看到了应用程序的代码。在这里,我将重点介绍其他部分。
项目组织
项目组织方式如下:
我认为将模块放在app目录中并分组相关的js和视图文件更容易。这使得查找相关的视图和控制器文件更加容易。通用服务、指令、过滤器等都放在common目录中。
至于命名约定,它是*Controller.js和*View.html。例如:searchController.js和searchView.html。
GitHub服务
我最终为GitHub API调用创建了一个单独的服务。我不想在这个简单的例子中使事情过于复杂。代码非常直观。
(function () {
'use strict';
angular.module("gitHubApp").service("gitHubService", ["$http", gitHubService]);
function gitHubService($http) {
var getReposByUser = function (query) {
return $http.get("https://api.github.com/users/" +
encodeURIComponent(query) + "/repos");
}
var getRepoInfo = function (username, repoName) {
return $http.get("https://api.github.com/repos/" +
encodeURIComponent(username) + "/" + encodeURIComponent(repoName));
}
return {
getReposByUser: getReposByUser,
getRepoInfo: getRepoInfo
};
}
}());
搜索控制器
此搜索控制器主要负责处理搜索Web服务调用以获取GitHub结果。
(function () {
'use strict';
angular.module("gitHubApp").controller
("SearchController", ["gitHubService", searchController]);
function searchController(gitHubService) {
var vm = this;
// Search function called when search button is clicked
vm.search = function () {
// Check if our vm.query is specified.
if (!vm.query || vm.query.length == 0) {
alert('Please specify a search query');
return;
}
// Make call to webservice to get repos for the user.
gitHubService.getReposByUser(vm.query).then(function (response) {
// We can handle error using response.status != 200,
// or using the sucess() error() callbacks, instead of then()
vm.results = response.data;
});
}
}
}());
搜索视图
我们添加了一个搜索UI和结果表。搜索输入绑定到ng-model="vm.query"
。结果表仅在有有效结果时显示。我们还有几个验证div
来显示可能的错误。
<!-- Since we use Controller as vm, the vm property automatically gets injected here. -->
<div>
<label for="inputUsername">Username:</label>
<!-- The Search Input : binds to vm.query -->
<input type="text" class="form-control" placeholder="Username"
id="inputUsername" ng-model="vm.query" required>
<!-- Search button fires the search using ng-click -->
<button class="btn btn-primary" type="button" ng-click="vm.search();">Search</button>
</div>
<!-- Possibly invalid search query. -->
<div class="alert alert-warning" ng-if="vm.query && vm.results.message">
No results found. Please try again.
</div>
<div class="alert alert-warning" ng-if="vm.results && vm.results.length == 0">
No repos found. Please try again.
</div>
<!-- Go through our results, if any, and display them -->
<table class="table table-striped" ng-if="vm.results">
<thead>
<tr>
<th>Id</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="result in vm.results">
<!-- "::" is one time binding released in AngularJs 1.3-->
<td>{{::result.id}}</td>
</tr>
</tbody>
</table>
仓库详情控制器
这更有趣。:) 如果您还记得ui-router
的状态“repoDetail
”,您会记得我们使用了resolve
属性来返回一个repoDetail
对象。如果您不记得了,请参考上面的ui-router
。代码如下:
(function () {
'use strict';
angular.module("gitHubApp").controller("RepoDetailController",
["repoDetail", repoDetailController]);
// repoDetail automatically resolved and got injected here from the state.
function repoDetailController(repoDetail) {
var vm = this;
vm.repo = repoDetail.data; // Use statusText != 200 to catch errors
}
}());
FYI:控制器和模板不会加载,直到resolve函数完成。然而,angular-loading-bar
会显示进度条,指示内容正在加载,这非常棒。
仓库详情视图
这里没有什么太有趣的事情可以分享,除了返回按钮,它使用了ui-sref
。
<!-- ui-sref="stateName" -->
<a class="btn btn-primary" ui-sref="search" style="margin-top: 10px;">go back</a>
最终应用程序
最终应用程序建立在BootStrap之上,并包含更多细节。同样,这是一个概念应用程序。目标是保持简单并涵盖不同的概念。
关注点
我认为angular-loading-bar
与ui-router
的resolve
属性的组合效果非常好!它的工作方式就像浏览器一样,用户可以预期。当请求正在处理时,您会看到一个进度条,当内容加载时,您会看到实际的视图。
ui-router
库也非常强大。状态的概念使应用程序的开发和管理非常简单明了。
最后但同样重要的是,使用{::property}
进行一次性绑定非常棒。这是我期待了很长时间的功能。(即使我上个月才开始接触Angular,我也一直在关注绑定性能的提升)。
历史
- 发布日期:2014年7月2日:添加了简单的GitHub应用程序,该应用程序可以拉取用户的仓库并显示仓库详情。该应用程序还演示了
ui-router
和angular-loading-bar
组件。