你的第一个AngularJS应用

简介: AngularJS是Google开源出来的一款 Javascript MVC 框架。利用AngularJS,你可以构建结构清晰、便于测试和维护的前端应用。

AngularJS是Google开源出来的一款 Javascript MVC 框架。利用AngularJS,你可以构建结构清晰、便于测试和维护的前端应用。

使用AngularJS,你可以通过directive去定义很多自己的HTML元素属性。AngularJS无缝衔接了HTML(view)和Javascript(model),这样你就不需要去过多地关注Dom如何变化,你只需专注的处理你的数据。

AngularJS和服务器通信非常方便。和大多数Javascript的MVC框架一样, 只要你的应用提供一个RESTful API,AngularJS就可以和你的服务器相配合。同时,AngularJS提供了基于XHR的服务,这将大大简化你的代码,也方便将可复用的服务抽象成API调用。

Raoni Boaventura提供了一个教程,一步一步地教你写一个简单的AngularJS应用,让我们一起来看一下吧。

为了简化问题,我们要做的是一个直接从网络上拉取信息的应用,比如,一个查看方程式赛车比赛信息的应用。直接用Ergast的api获取信息。

可以看一下这个demo,对我们要做一个什么样的应用有个大致的概念。

开动吧

推荐使用augular-seed,提供了一个很好的项目骨架。

我们的应用的骨架大概是这样的:

image.png

现在要开始写代码了。从最重要的页面开始吧:锦标赛的表格。

image.png

HTML大概是这个样子的(为了提高可读性,先忽略CSS):

<bodyng-app="F1FeederApp"ng-controller="driversController">

 <table>

   <thead>

     <tr><thcolspan="4">Drivers Championship Standings</th></tr>

   </thead>

   <tbody>

     <trng-repeat="driver in driversList">

       <td>{{$index + 1}}</td>

       <td>

         <imgsrc="img/flags/{{driver.Driver.nationality}}.png" />

         {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}

       </td>

       <td>{{driver.Constructors[0].name}}</td>

       <td>{{driver.points}}</td>

     </tr>

   </tbody>

 </table>

</body>

我想你会注意到模板里面有一些{{}} 之类的表达式。我们可以在里面做一些计算。举一些例子吧:

{{ 1 + 1 }}

{{ 946757880 | date }}

{{ user.name }}

是不是和javascript很像?不过,虽然它们很强大,我们不应该用它们去实现一些高层级的逻辑——这该交给directive。


理解基本的directive

上面的模板里还有一些类似ng-attributes的语句,这些正是directive。

directive让AngularJS把特定的行为附加到DOM元素中。让我们看一下上面的模板中的例子:

  • ng-app 初始化你的应用,定义其作用域。在 AngularJS 中,同一页面可以有多个应用,ng-app 指令表明应用的首尾位置。
  • ng-controller 定义视图由哪个控制器负责。在我们的例子中,driversController提供了车手的列表(driversList)。
  • ng-repeat 这个最常用。当使用循环的时候,ng-repeat定义模板的范围。在我们的例子中,就driversList中的每个车手,ng-repeat会生成重复的行。


添加控制器

当然,没有控制器,我们的视图什么也干不了。让我们在controllers.js中添加一个driversController

angular.module('F1FeederApp.controllers', []).

controller('driversController', function($scope) {

   $scope.driversList = [

     {

         Driver: {

             givenName: 'Sebastian',

             familyName: 'Vettel'

         },

         points: 322,

         nationality: "German",

         Constructors: [

             {name: "Red Bull"}

         ]

     },

     {

         Driver: {

         givenName: 'Fernando',

             familyName: 'Alonso'

         },

         points: 207,

         nationality: "Spanish",

         Constructors: [

             {name: "Ferrari"}

         ]

     }

   ];

});

你可能注意到了我们将$scope传递给了控制器。$scope变量将控制器和视图相连接。事实上,它储存了模板中会用到的所有数据。任何你加入的内容(比如我们的例子中的driversList)可以直接在视图中访问。现在我们先用一个静态的数据数组,稍后我们会把它换成API服务。

app.js中加入:

angular.module('F1FeederApp', [

 'F1FeederApp.controllers'

]);

这行代码让我们初始化了我们的应用,同时也登记了需要的依赖。稍后我们会回到这个文件。

好了,现在让我们把这一切在index.html中整合起来:

<!DOCTYPE html>

<html>

<head>

 <title>F-1 Feeder</title>

 <scriptsrc="lib/angular/angular.js"></script>

 <scriptsrc="lib/angular/angular-route.js"></script>

 <scripttype="text/javascript"src="js/app.js"></script>

 <scripttype="text/javascript"src="js/controllers.js"></script>

 <scripttype="text/javascript"src="js/services.js"></script>

</head>

<bodyng-app="F1FeederApp"ng-controller="driversController">

 <table>

   <thead>

     <tr><thcolspan="4">Drivers Championship Standings</th></tr>

   </thead>

   <tbody>

     <trng-repeat="driver in driversList">

       <td>{{$index + 1}}</td>

       <td>

         <imgsrc="img/flags/{{driver.Driver.nationality}}.png" />

         {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}

       </td>

       <td>{{driver.Constructors[0].name}}</td>

       <td>{{driver.points}}</td>

     </tr>

   </tbody>

 </table>

</body>

</html>

现在你可以尝试运行下这个应用了。

如果你希望调试应用,建议看下Chrome的Batarang 插件。


从服务器获取信息

既然我们已经知道如何展示这些数据了,现在该是我们从RESTful服务器获取信息的时候了。

AngularJS提供的$http$resource帮助我们和服务器通讯。

$http是以XMLHttpRequest和[JSONP]为基础的抽象层,$resource则提供更高层的抽象。在这里我们使用$http

为了将API调用从控制器中抽象出来,我们创建一个自己定制的服务,该服务将抓取我们需要的信息,将$http封装起来。在services.js中加入:

angular.module('F1FeederApp.services', []).

 factory('ergastAPIservice', function($http) {

   var ergastAPI = {};

   ergastAPI.getDrivers = function() {

     return$http({

       method: 'JSONP',

       url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK'

     });

   }

   return ergastAPI;

 });

开头两行,我们创建了一个名为F1FeederApp.services的新模块,并在ergastAPIservice模块内注册了服务。注意,我们将$http传递给了该服务。这就告诉了Angular的依赖注入引擎我们的新服务依赖于$http服务。

类似地,我们需要让Angular将我们的新模块包含到应用中。在app.js注册下即可:

angular.module('F1FeederApp', [

 'F1FeederApp.controllers',

 'F1FeederApp.services'

]);

现在我们只需调整一下controller.js,将ergastAPIservice作为依赖:

angular.module('F1FeederApp.controllers', []).

 controller('driversController', function($scope, ergastAPIservice) {

   $scope.nameFilter = null;

   $scope.driversList = [];

   ergastAPIservice.getDrivers().success(function (response) {

       //Dig into the responde to get the relevant data

       $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings;

   });

 });

好了,重新启动一下应用,看看结果如何。注意我们完全没有改动模板,只是增加了一个nameFilter。让我们把这个变量用起来。


过滤器

好极了!我们的控制器已经可以工作了。但是它只能显示一个车手的列表。让我们加一些功能吧。我们来实现一个简单的文本搜索框,可以过滤列表。将以下这行加入到index.html,加在<body>标签下面:

<input type="text"ng-model="nameFilter"placeholder="Search..."/>

现在我们要用上 ng-model 指令了。这个指令将文本框绑定到$scope.nameFilter变量,并且确保该变量的值会及时更新。现在让我们稍微调整下index.html,加上一行ng-repeat指令。

<tr ng-repeat="driver in driversList | filter: nameFilter">

这一行告诉ng-repeat,在输出数据之前,车手数组先要经过nameFilter的过滤。

此刻就是双向数据绑定发挥威力的时候了:每次你在搜索框里键入一些值的时候,Angular会及时更新$scope.nameFilter的内容。由于绑定是双向的,所以nameFilter更新的时候,相应的指令(ng-repeat)也会获得新的数值,然后视图会立刻更新。

重启你的应用,看看搜索框。

image.png

注意,过滤器会搜索所有属性中的关键词,包括你不想包括的内容。假设你只想通过Driver.givenNameDriver.familyName过滤:首先,在driversController文件的$scope.driversList = [];下加入这样一行:

$scope.searchFilter = function (driver) {

   varkeyword = new RegExp($scope.filterName, 'i');

   return !$scope.filterName || keyword.test(driver.Driver.givenName) || keyword.test(driver.Driver.familyName);

};

现在回到index.html,更新包括ng-repeat的那行:

<tr ng-repeat="driver in driversList | filter: searchFilter">

重新启动应用,现在你可以通过姓名搜索了。


路由

接下来我们要创建一个车手详情页面,当我们点击车手的时候,我们就可以看到关于他的一些详细信息。

首先,我们在app.js里加入$routeProvider服务,这个服务将帮助我们处理应用路由。然后我们加入两个路由:一个转向锦标赛表格,另一个转向车手详情。

angular.module('F1FeederApp', [

 'F1FeederApp.services',

 'F1FeederApp.controllers',

 'ngRoute'

]).

config(['$routeProvider', function($routeProvider) {

 $routeProvider.

   when("/drivers", {templateUrl: "partials/drivers.html", controller: "driversController"}).

   when("/drivers/:id", {templateUrl: "partials/driver.html", controller: "driverController"}).

   otherwise({redirectTo: '/drivers'});

}]);

修改之后,访问http://domain/#/drivers,这将加载driversController,然后在partials/drivers.html寻找需要渲染的部分视图。等等!我们好像还没有部分视图?我们需要创建他们。


部分视图

AngularJS允许你将路由绑定到特定的控制器和视图。不过我们首先需要告诉Angular在哪里渲染这些部分视图。这需要使用ng-view指令。修改一下你的index.html

<!DOCTYPE html>

<html>

<head>

 <title>F-1 Feeder</title>

 <scriptsrc="lib/angular/angular.js"></script>

 <scriptsrc="lib/angular/angular-route.js"></script>

 <scripttype="text/javascript"src="js/app.js"></script>

 <scripttype="text/javascript"src="js/controllers.js"></script>

 <scripttype="text/javascript"src="js/services.js"></script>

</head>

<bodyng-app="F1FeederApp">

 <ng-view></ng-view>

</body>

</html>

现在,只要是通过应用路由浏览,Angular 就会加载相应的视图,并且

<ng-view>标签处渲染。你所需要做的只是创建一个名为partials/drivers.html的文件,然后将锦标赛表格放在那里。同时我们也将将车手的姓名和详情页面连接起来。

<inputtype="text"ng-model="nameFilter"placeholder="Search..."/>

<table>

<thead>

 <tr><thcolspan="4">Drivers Championship Standings</th></tr>

</thead>

<tbody>

 <trng-repeat="driver in driversList | filter: searchFilter">

   <td>{{$index + 1}}</td>

   <td>

     <imgsrc="img/flags/{{driver.Driver.nationality}}.png" />

     <ahref="#/drivers/{{driver.Driver.driverId}}">

       {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}

     </a>

     </td>

   <td>{{driver.Constructors[0].name}}</td>

   <td>{{driver.points}}</td>

 </tr>

</tbody>

</table>

最后,让我们确定下详情页面要展示什么。一个总结了车手相关信息(比如生日、国籍)的页面,同时包括一个最近成绩的表格。在services.js里加入这些:

angular.module('F1FeederApp.services', [])

 .factory('ergastAPIservice', function($http) {

   var ergastAPI = {};

   ergastAPI.getDrivers = function() {

     return$http({

       method: 'JSONP',

       url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK'

     });

   }

   ergastAPI.getDriverDetails = function(id) {

     return$http({

       method: 'JSONP',

       url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/driverStandings.json?callback=JSON_CALLBACK'

     });

   }

   ergastAPI.getDriverRaces = function(id) {

     return$http({

       method: 'JSONP',

       url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/results.json?callback=JSON_CALLBACK'

     });

   }

   return ergastAPI;

 });

这次我们把车手的ID提供给服务,这样我们就可以获取特定车手的信息了。修改一下controllers.js

angular.module('F1FeederApp.controllers', []).

 /* Drivers controller */

 controller('driversController', function($scope, ergastAPIservice) {

   $scope.nameFilter = null;

   $scope.driversList = [];

   $scope.searchFilter = function (driver) {

       var re = new RegExp($scope.filterName, 'i');

       return !$scope.filterName || re.test(driver.Driver.givenName) || re.test(driver.Driver.familyName);

   };

   ergastAPIservice.getDrivers().success(function (response) {

       //Digging into the response to get the relevant data

       $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings;

   });

 }).

 /* Driver controller */

 controller('driverController', function($scope, $routeParams, ergastAPIservice) {

   $scope.id = $routeParams.id;

   $scope.races = [];

   $scope.driver = null;

   ergastAPIservice.getDriverDetails($scope.id).success(function (response) {

       $scope.driver = response.MRData.StandingsTable.StandingsLists[0].DriverStandings[0];

   });

   ergastAPIservice.getDriverRaces($scope.id).success(function (response) {

       $scope.races = response.MRData.RaceTable.Races;

   });

 });

值得注意的是我们将$routeParams服务插入了车手控制器。这个服务允许我们使用$routeParams.id访问URL参数(比如:id)。

现在我们已经有数据了,我们只需要处理一下局部视图了。创建一个partials/driver.html:

<sectionid="main">

 <navid="secondary"class="main-nav">

   <divclass="driver-picture">

     <divclass="avatar">

       <imgng-show="driver"src="img/drivers/{{driver.Driver.driverId}}.png" />

       <imgng-show="driver"src="img/flags/{{driver.Driver.nationality}}.png" /><br/>

       {{driver.Driver.givenName}}<br/>{{driver.Driver.familyName}}

     </div>

   </div>

   <divclass="driver-status">

     Country: {{driver.Driver.nationality}}   <br/>

     Team: {{driver.Constructors[0].name}}<br/>

     Birth: {{driver.Driver.dateOfBirth}}<br/>

     <ahref="{{driver.Driver.url}}"target="_blank">Biography</a>

   </div>

 </nav>

<divclass="main-content">

   <tableclass="result-table">

     <thead>

       <tr><thcolspan="5">Formula 1 2013 Results</th></tr>

     </thead>

     <tbody>

       <tr>

         <td>Round</td><td>Grand Prix</td><td>Team</td><td>Grid</td><td>Race</td>

       </tr>

       <trng-repeat="race in races">

         <td>{{race.round}}</td>

         <td><img  src="img/flags/{{race.Circuit.Location.country}}.png" />{{race.raceName}}</td>

         <td>{{race.Results[0].Constructor.name}}</td>

         <td>{{race.Results[0].grid}}</td>

         <td>{{race.Results[0].position}}</td>

       </tr>

     </tbody>

   </table>

 </div>

</section>

注意这次我们用上了ng-show。只有当你提供的表达式是true的时候,它才会显示HTML元素。在我们的例子中,只有当控制器加载了车手对象后才会显示头像。


完成

加上一些CSS之后,大致是这样的效果:

image.png

现在你的应用已经可以上线了。确认下路由能工作。你可以在index.html加入一个静态的菜单以加强导航。一切皆有可能。


结论

我们已经介绍了开发一个简单应用所需的一切。最后别忘了,Angular是一个非常强大的框架。我们只是试了试水而已。以后有机会将向大家展示Angular区别于其他前端MVC框架的特性:可测试性。我们将评测使用Karma编写和运行测试的过程,介绍持续集成的工具YeomenGruntBower,以及Angular的其他优势。敬请期待。


相关文章
|
20天前
|
人工智能 缓存 前端开发
DeepSeek Harness 首发实测 + 入门教程,夯爆了!梁神我错了
DeepSeek Harness + DeepSeek V4 Pro 项目实战保姆级教程!手把手带你从零安装开源 AI 编程工具,开发架构图、知识讲解网站、3D 网页游戏、全栈 AI 应用 4 个项目,覆盖运行模式选择、插件安装与开发,看看能不能对标 Claude。
13265 90
DeepSeek Harness 首发实测 + 入门教程,夯爆了!梁神我错了
|
8天前
|
人工智能 自然语言处理 安全
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
本文聚焦阿里云2026年推出的三款自研AI办公产品,清晰拆解千问办公、Qoder Teams、Qoder CN的差异化定位与能力边界:千问办公主打职场全场景提效,支持自然语言指令一键完成PPT生成、数据分析等高频办公任务;Qoder Teams面向程序员团队,深度整合AI代码生成、团队协同与企业知识库能力;Qoder CN则专为金融、政务等强合规场景打造,实现数据不出境与VPC私有化部署。文章同步给出分场景选型指南与最新活动定价,帮助不同类型的企业按需组合产品,实现业务岗、研发岗与强合规场景的AI能力全覆盖。
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
|
13天前
|
Web App开发 人工智能 API
16 个超火的 DeepSeek Harness 插件,大肥鱼已经落后 N 个版本了。。。
DeepSeek Harness 精选插件推荐合集,从图片识别、浏览器操控、多 Agent 协作到手机远程控制,一口气带你看完 DSH 社区热门的十几个插件,覆盖技能扩展、UI 界面增强、整活玩法三大类,让你的鲸鱼变得更强。
1804 4
|
15天前
|
人工智能 Java BI
【AI】DeepSeek Harness 安装、运行、管理插件
本文介绍了如何运行DeepSeek开源的Agent框架DeepSeek Harness(dsh)。主要内容包括:使用nvm安装适配的Node版本;通过代理加速克隆GitHub源码;使用pnpm安装依赖并启动项目;配置DeepSeek API Token;安装扩展功能的插件。该框架自带Web界面,支持模型适配、文件编辑等插件化功能
1992 1
|
人工智能 JavaScript 开发工具
DeepSeek Harness 本地安装与使用指南
DeepSeek Harness(DSH)是DeepSeek AI开源的Agent运行框架,支持本地文件操作、命令执行与工具调用。基于Cordis插件架构,具备高扩展性与强可控性,适合开发者搭建可控Agent环境或开展模型基准测试。当前为开发者预览版,需Node.js环境,推荐先用`npx @deepseek-ai/dsh web`快速体验。
5264 0
|
9天前
|
人工智能 Linux iOS开发
Ollama使用教程:Ollama官网下载、Ollama本地部署大模型(2026最新)
Ollama 是一款免费开源的本地大模型运行工具,支持在 Windows/macOS/Linux 上离线运行 Qwen、DeepSeek、Llama 等主流开源模型,数据不出本机、隐私安全。提供 OpenAI 兼容 API,命令行一键拉取/运行/管理模型,无需联网,无调用限制,是开发者与 AI 爱好者部署本地 AI 助手的理想选择。(239 字)
|
17天前
|
人工智能 JavaScript 测试技术
保姆级教程:DeepSeek Harness从安装到跑通测试,30分钟上手
DeepSeek Harness是DeepSeek开源的AI Agent运行时,主打“一行命令安装、5分钟跑通”。它让模型真正动手干活——读代码、跑测试、分析失败、生成修复方案。本文手把手教你30分钟从零上手,覆盖安装、配置、实测及避坑指南,助你快速掌握下一代AI编程范式。
|
6天前
|
人工智能 监控 测试技术
Qwen3.8-Flash 来了,100万上下文、Agent、Coding 都加强了
8月26日,通义千问发布Qwen3.8-Flash-Next:125B参数、每Token仅激活6B,原生支持26万Token、可扩展至100万上下文;Coding、Agent与工具调用能力显著增强,面向真实软件工程任务,推动大模型从“回答问题”迈向“完成工作”。