3.2.1 配置构建Angular应用——简单的笔记存储应用——展示功能

时间:2021-10-05 20:33:17

  本节我们会通过构建一个简单的笔记存储应用(可以载入并修改一组简单的笔记)来学习如何应用Angular的特性。这个应用用到的特性有:

  1. 在JSON文件中存储笔记
  2. 展示、创建、修改和删除笔记
  3. 在笔记中使用Markdown格式
  4. 同步编辑和预览Markdown

  本应用已经包含了基础的HTML和CSS代码,还有一个用Node写的简单的RESTful服务器,用于管理笔记,这样我们就可以专注于Angular而不是API。我们学习的重点是如何把Angular加入其中并学习它的重要特性。

  3.2.1 获取项目文件

  首先,我们需要来获取一下该项目的文件,可以通过git来获取,执行如下命令:

    $ git clone https://github.com/ionic-in-action/chapter3.git(克隆chapter3仓库)

    $ cd chapter3(切换到chapter3目录)

    $ git checkout step1(检出step1标签)

  (如果你不想使用git,可以直接下载文件:https://github.com/ionic-in-action/chapter3/archive/step1.zip)

  3.2.2 启动开发服务器

  由于这个项目搭载的是RESTful服务器,需要你掌握一些NodeJS的知识。在项目server.js文件中可以看到一个简单的RESTful服务器,它是基于Express.js框架开发的,这样做的原因是你需要长期管理笔记,通过RESTful API可以让应用阅读、创建、编辑和删除列表中的笔记。服务器还可以通过HTTP请求把文件载入浏览器,ionic serve就是通过这种方式来运行你的Ionic应用的。

  需要注意的是:

  • 服务器运行在3000端口上;
  • 服务器会接受请求,根据URL和HTTP方法来修改列表中的笔记;
  • 服务器使用JSON文件来作为数据库(data/notes.json),你可以根据自己的情况使用其他的数据库;

  如果服务器不能运行,则说明你缺少一些必备的node包,解决方法,在终端中进入项目目录,运行$ npm install,npm会检查依赖列表并下载依赖。

  然后输入命令$ node server来启动服务器,如果需要终止服务器可以按Ctrl+S或者直接关闭命令窗口。

   运行后的基础模板如下图所示:

  3.2.1 配置构建Angular应用——简单的笔记存储应用——展示功能

  3.2.3 创建Angular应用

  Angular开发简单来说就是用JavaScript创建一个Angular应用并在HTML中使用它。Angular和DOM紧密结合,所以你可以把一个Angular应用严格限制在一个DOM元素及其子元素中。在本例中使用的是<html>元素,所有Angular可以访问整个页面。Ionic通常使用的是<body>元素。

  首先,你必须要先载入Angular库,然后要创建一个Angular应用,你需要在一个元素上使用ngApp指令并声明应用名称。打开index.html文件,并添加ngApp指令:

<html lang="en" ng-app="App">

   现在,你已经把一个名为App的Angular应用附加到了HTML根元素上。这样Angular就可以访问整个DOM,不过你也可以把它附加到<body>标签中。我们建议把它放在<html>或者<body>元素中。

  上面我们只是添加了ngApp的指令,还没有在JavaScript中声明这个应用,下面我们来完成这一步。Angular有一套模板系统,用来封装程序代码。声明新模块时,你需要提供名字和一个数组,其中包含所有依赖。Ionic本身也是一个Angular模块。Angular模块的声明方式如下,创建一个新文件js/app.js并写入下面的代码:

angular.module('App',[]);

   最后,在index.html文件</body>标签前添加一个<script>标签来载入Angular模块:

<script src="js/app.js"></script>

   你现在已经在页面中声明并载入了一个最基本的Angular应用。angular.module()方法会创建模块并把它附加到ngApp所属的DOM元素中。这是最基本的Angular应用,实际上它现在没有任何功能。所有的Angular应用都是用这样的方式定义的。

  3.2.4 添加控制器

  控制器:控制数据和业务逻辑

  我们需要添加一个控制器来控制应用中多个部分的业务逻辑,它不会改变浏览器中应用的样子,因为控制器只负责管理数据,不影响应用的视觉效果,不过我们需要在管理视觉元素之前搞定控制器。添加控制器之后,它就可以访问页面中的某个特定区域。

  下面我们来声明一个简单的控制器。首先你需要引用App模块并使用控制器方法来声明一个控制器。需要传入控制器的名字以及一个包含控制器逻辑的函数。创建文件js/editor.js:  

//编辑控制器 js/editor.js
angular.module('App')    //引入App模块并把它引入这个控制器中
.controller('EditorController',function($scope){    //声明EditorController控制器,传入一个包含依赖列表的函数
    $scope.state={    //创建模型的值并存储到$scope中
        editing:false
    };
});    

   这个控制器现在非常简单,只是创建了一个简单的模型state。$scope服务被注入,所以你可以设置它的属性。记住,$scope中的值被称为模型,可以在视图中访问。

  现在修改index.html文件,把控制器加入应用中,在</body>前引入editor.js文件:

<script src="js/editor.js"></script>

   最后将控制器附加到DOM中。这会给控制器创建一个新的子作用域。我们需要用一条Angular指令来盛勇控制器被附加的位置:

<div class="container" ng-controller="EditorController">

   注:$开头的服务

  Angular中的服务以$符号开头,Ionic的服务也是如此。以$开头的服务,按惯例是Angular核心服务或者Ionic服务。

  3.2.5 加载数据并将数据显示在应用中

  加载数据:使用控制器来加载数据并显示在视图中

  下面我们来加载数据并把它显示到应用中,在应用的基础模板左侧已经有一个创建好的空的笔记列表。然后我们加入一些简单的笔记,更新控制器从而把数据载入应用。要实现这个功能,需要使用Angular的$http服务,通过$http服务来请求Node服务器的数据。我们来具体操作一下:

  先修改控制器,通过HTTP请求访问服务器的笔记服务并把返回的数据赋值给作用域。打开js/editor.js文件,更新代码:

angular.module('App')
.controller('EditorController', function ($scope,$http) {	//把$http服务注入控制器
  $scope.editing = true;

  $http.get('/notes').success(function(data){	//使用$http.get加载笔记,如果成功,使用法内的数据
  	$scope.notes = data;	//把从http返回的数据赋值给$scope
  }).error(function(err){	//处理错误,存储错误
  	$scope.error = 'Could not load notes';
  });
});

   注意控制器函数中可以给函数声明任意数量的参数,Angular会通过名字来定位服务并注入控制器。上面代码中$http的使用方法叫做依赖注入(DI),是Angular一个非常强大的特性,可以让你的控制器使用各种服务。由于Angular的服务并不是全局的,所以必须先注入再使用。

  这时我们启动服务,但是在页面上看不到任何数据,而我们访问http://localhost:3000/notes,发现json数据可以访问没有问题,那是为什么我们看不到数据呢?因为我们需要更新index.html模板文件,用Angular指令把数据从$scope中显示出来,对index.html文件,我们需要进行如下修改:

<div class="col-sm-3">
  <div class="panel panel-default">
    <div class="panel-heading">
      <h3 class="panel-title"><button class="btn btn-primary btn-xs pull-right">New</button> My Notes</h3>
    </div>
    <div class="panel-body">
      <!-- ngIf会根据是否有笔记来判读是否把这个元素插入DOM -->
      <p ng-if="!notes.length">No notes</p>
      <ul class="list-group">
        <!-- ngRepeat会循环每个笔记并显示笔记标题 -->
        <li class="list-group-item" ng-repeat="note in notes">{{note.title}}><br />
        <!-- 绑定显示日期,使用过滤器显示较短的日期 -->
        <small>{{note.date | date:'short'}}</small></li>
      </ul>
    </div>
  </div>
</div>

  说明:模板中{{note.date | date:'short'}}有个| date:'short',这是一个过滤器,它会在不改动作用域值的前提下修改显示内容。在表达式中可以通过管道符来使用过滤器,过滤器可以串联,也就是说可以添加多个过滤器。

  加载数据后的笔记截图:

  3.2.1 配置构建Angular应用——简单的笔记存储应用——展示功能 

  3.2.6 处理选择笔记的单击事件

  如果需要单独查看某条笔记,需要单击左侧列表笔记时,将他们显示在右侧。

  使用ngClick可处理用户的单击事件,然后把笔记数据赋值给一个新的模型,用来进行显示,我们来打开模版index.html,修改笔记列表的部分,添加单击事件处理器:

<ul class="list-group">
    <!-- ngRepeat会循环每个笔记并显示笔记标题;ngClick会调用view()并传入下标;添加ngClass,如果笔记被选中就添加active类 -->
    <li class="list-group-item" ng-repeat="note in notes" ng-click="view($index)" ng-class="{active: note.id == content.id}">{{note.title}}><br />
    <!-- 绑定显示日期,使用过滤器显示较短的日期 -->
    <small>{{note.date | date:'short'}}</small></li>
</ul>

  当单击笔记时,Angular会尝试调用$scope.view()函数,ngClass指令可以根据情况向元素添加css类。$index值时ngRepeat提供的特殊变量,被传入视图函数里,作用就是告诉你当前被使用的数组元素的下标,此处指的是被单击元素的下标。

  下面我们来创建视图函数,打开editor.js文件,在控制器函数里添加视图函数:

$scope.view = function(index){        //声明一个名为view的新$scope方法,接受被点击元素的下标
      $scope.editing = false;        //把editing状态设置为false,因为此时用户要查看元素
      $scope.content = $scope.notes[index];        //给content模型设置一个新模型,包含被单击的笔记
  };

  此时,当我们点击笔记时,click时间会触发控制器中view()方法,它会根据传入的下标值找到被点击的笔记,并将笔记内容赋值给新的content模型,同时,editing模型也会被设置为false。

  3.2.7 更新模版显示被选择的笔记

  此时点击笔记,右侧面板不会有变化,因为我们还没有修改右侧显示的面板,右侧面板有两种状态,一个是展示笔记,一个是编辑笔记,$scope.editing属性将决定显示哪个面板。再次打开index.html文件作如下修改:

<!-- ngHide会在本条件为真时隐藏头部,在这里editing为true的时候条件为真 -->
<div class="panel panel-default" ng-hide="editing">
    <div class="panel-heading">
    <!-- 把title绑定到头部 -->
    <h3 class="panel-title">{{content.title}} <button class="btn btn-primary btn-xs pull-right">Edit</button></h3>
    </div>
    <!-- 把content绑定到正文 -->
    <div class="panel-body">{{content.content}}</div>
    <!-- 绑定笔记日期并把它传递给过滤器 -->
    <div class="panel-footer">{{content.date | date:'short'}}</div>
</div>
<!-- ngShow会在条件为假时隐藏底部,在这里editing为false的时候条件为假 -->
<form name="editor" class="panel panel-default" ng-show="editing">

  再次运行应用,此时单击笔记即可实现查看功能,如下图所示:

  3.2.1 配置构建Angular应用——简单的笔记存储应用——展示功能

  3.2.8 创建指令,解析Markdown格式的笔记

  要实现把Markdown格式的文本转换为HTML,需要使用Showdown这个js库。

  打开app.js文件,指令不是控制器的一部分,所以代码需要被存储到应用主文件中,具体代码如下:

//声明命令并命名为markdown
.directive('markdown',function(){
    //创建showdown转换器,下面会用到
    var converter = new Showdown.converter();
    //命令会返回一个对象,用来声明命令的设置
    return {
        //声明自定义作用于,等待值被赋给markdown属性
        scope:{
            markdown:'@'
        },
        //声明link函数,它会把markdown转换成html
        link:function(scope,element,attrs){
            //使用作用于观察器来同步模型改动
            scope.$watch('markdown',function(){
                //把markdown转换成html并存入content变量
                var content = converter.makeHtml(attrs.markdown);
                //把转换好的html内容注入到元素内
                element.html(content);
            });
        }
    }
});

  现在我们打开index.html文件,传入markdown内容:

<div class="panel-body" markdown="{{content.content}}"></div>

  改为markdown笔记格式的页面效果如下:

  3.2.1 配置构建Angular应用——简单的笔记存储应用——展示功能

  笔记展示功能已完成,下一节来实现编辑功能。