Angular 控制器之间通信初探

最近做Angular 项目,经常遇到的一个问题是控制器之间的通信问题。这篇文章打算总结一下这部分的使用经验,有借鉴别人的部分,也有自己的总结,我会尽量说的明白些~如果我的理解有纰漏,请帮忙之处,谢谢。

问题背景

我最开始写功能的时候,只是定义了一个 mainController,就只有这一个控制器。由于是一个单页面应用,所以也比较ok,各种功能的实现也没啥大的问题。但是随着代码的深入,我老是觉得把所有东西都写到$scope上面很不妥,这会导致 $scope 越来越臃肿,而且命名也是个问题,万一不小心名字重复覆盖了呢?接下来说下我用到的解决方案。。。

第一种解决方案

很简单,就是我在 $scope下面创建了几个不同的对象,比如 $scope.data 就用来存放用到的公共数据,$scope.global 就是使用的一些公共方法。这样倒是可行,但他们两个对象后面也会变得比较臃肿,感觉这并不是什么可靠的方式。

第二种

这个时候问了下我们组长,他的建议是mainController下面,再让我自己去分几个controller。这倒是提醒了我,于是我重新组织了页面,划分了各个不同的模块,每一个模块对应一个controller。比如购买页面主体部分有promoteController 和 payController,侧边栏是sidebarController等等。每个controller 就负责自己那部分的页面渲染和事件绑定。

这样分割模块下来感觉整体清晰了很多,不再想之前咋样各个部分的代码逻辑都杂糅在一起。这就是模块化编程的一种优势吧。

综上,我最终选择了分模块,设置控制器这样一种方式,然后他们上层有一个主模块,整体控制的方式。这样就出现本文涉及的主要问题,控制器之间如何通信。。。。

问题的出现

项目中这样一个小功能,点开一个小三角,弹出提示信息框,再点,就关闭这个提示框!相信前端朋友们很多都做过这个小效果,很简单。但是还有功能,就是除了这个小三角之外的地方,如果触发了点击事件,而且提示框在显示的话,要关闭这个提示框。再说下我的解决方案。。。

控制器继承方式

我把小弹窗的处理函数比如 A(),定义到 mainController,然后如果小三角点击事件触发,则在相应的子控制器中,出发A()。因为子控制器是可以继承得到 mainController 的方法,
所以可以子控制可以直接调用。

但是这种方式给我感觉有些混乱,最主要的还是,这应该是子控制器自己的方法,不应该上升到 mainController。

基于事件的方式

Angular 中提供了 $on, $broadcast, $emit 这几种方式,我当时也是主要参考了后面列出的几篇文章。一使用还是很容易就实现了。

通过这种方式,我把提示框显示留在了子控制器。具体实现代码类似:

mainController 直接调用
$broadcast( 'closeTip' );
子控制器接收
$on( 'closeTip', function( $event ) {
  $scope.hideTip = false;
  $event.stopPropagation(); // 停止冒泡
} );

这样当全局接收到点击事件的时候,就是把关闭这个提示框的信号广播出去。那么相应模块的子控制器接收到了之后,就会调用自己的方法关闭提示框。那么这里还用到了 $event.stopPropagation(); 就终止了事件继续冒泡,不让点击事件再次传递。。。( 这部分停止冒泡是为了处理单纯小三角的点击事件,因为它的点击会传递到mainController,而mainController上面默认的点击事件是关闭提示框。所以不能让这部分传递过去 )

总结

我这部分处理主要参考的就是参考资料1 && 3,我这里对1的学习做个总结。首先感谢这篇分享,学到了很多,还有实例代码!主要用了文章中前两种实现方式。

1 继承的通信方式

单独一个基本属性,如定义一个变量,子级控制器能够从父级继承得到,而且父级修改会影响子级,但子级修改并不影响父级,而且子级如果有属性和父级同名,那么子级默认使用自己的变量。这和js 的作用域一样。如果要父级和子级控制器共享一个值,既父级子级修改都能相互影响,这就需要定义一个对象。因为对象是引用类型。。。

2 基于事件 $broadcast, $emit 看参考资料2,使用 $broadcast 貌似有性能问题,不知现在的Angular版本是否解决。。。。感觉广播的方式确实会很降低效率,还是 $emit 好!

3 服务的方式,我虽然没用这种方式实现,但感觉这种更倾向于模块化,比上面的实现思路更加清晰。。。而且更加通用

这是目前关于控制器通信的一些认识。实现控制器通信方式这些文章介绍的很详细,大家可以参考下!欢迎指正我文章的错误~~

参考资料

  1. 控制器之间的通信

  2. AngularJS控制器controller正确的通信的方法

  3. AngularJs 在线教程 $on、$emit和$broadcast的使用

Angular指令 ng-if, ng-show/ng-hide, ng-switch 使用

开头

最近在做一个项目改版,第一次在项目中真正使用Angular,和平时自己写写小demo,做做练习的感觉还是非常不同的,感觉非常的新鲜。有几个指令是经常用到的,这里由于这几个有点共性,所以一起介绍一下 ng-if,ng-show/ng-hide,ng-switch 这几个指令。

共性

  1. 这里个指令都是Angular框架提供给我们的设置页面内容显示和隐藏的方法,使用起来非常方便,尤其是做业务逻辑。

  2. 都是通过一个表达式的值来实现切换显示的,只不过 ng-switch 可以是其他值,ng-if ng-show 就必须是 boolen了。

  3. 我在使用过程中发现一个小技巧,我们通过表达式设置 ng-if 或者 ng-show 直接在页面中定义一个表达式,这个时候它的值其实是undefined,由于 !== true 所以这部分默认也是隐藏。

那么既然是不同指令,就各自有专攻,那我们就来看看他们分别都有什么果实能力。。。(不看海贼的可以无视哈~~)

各自的果实能力

ng-show/ng-hide

在用原生js 或者 jquery的时候,我们一般都可以定义一个类,通过添加和删除这个类来实现元素的显示和隐藏切换。其实这部分从网上参考资料来看,Angular也是这样实现的,根据表达式正确与否,动态添加或者删除 ng-hide 这个Angualr预先定义好的class。调用方式具体如下:

可以是设置一个变量

<div ng-show='show'></div>

也可以是直接使用 true / false

<div ng-show='true'></div>

对于变量,我们在js 中直接设置这个值就可以。

这个指令的特性是,即使我们暂时隐藏这部分内容,它也会被dom 渲染。

ng-if

使用方式也是设置一个表达式:

可以是设置一个变量

<div ng-if='more'></div>

也可以是直接使用 true / false

<div ng-if='true'></div>

对于变量,我们在js 中直接设置这个值就可以。

这是一个能帮我们节省效率的指令,如果表达式值 === false , 则这部分不会在dom中渲染,或者原有的内容会被从dom中删除。所以如果有一部分内容,不需要一开始就显示,我们可以先用ng-if 让它隐藏。例如一个显示更多的下拉按钮,刚开始不显示的部分,可以ng-if 来设置,等我们点击了更多按钮,再设置 ng-if = true 既可。这样子减少了页面渲染事件,提高了效率呢。

还有一个特性,ng-if 或创建自己的 scope,它通过原型继承父级的scope。一个典型的例子来自于参考资源1。

还有一个小坑,$scope 上面我可以直接给一个属性赋值如:

$scope.showpage = 'abut'

但是如果直接赋值一个对象,对不起,需要先声明,再给对象添加属性

$scope.data = {};
$scope.data.showpage = 'about';

ng-swith

使用方式比前前两个复杂一点,不过也非常直观,类似原生js 中的switch 函数:

在外层父级元素设置 ng-switch 为一个表达式A ,那么他的子元素相当于几个不同的选项,表达式A 对应哪个子元素的 ng-switch-with 值,就显示那部分。

<div ng-switch='showpart'>
    <div ng-switch-default></div>
    <div ng-switch-with='home'></div>
    <div ng-switch-with='blog'></div>
    <div ng-switch-with='about'></div>
    <div ng-switch-with='contact'></div>
</div>

这也是一个能帮我们节省效率的指令。和 ng-if 一样,一开始如果不等于父级的 ng-switch 表达式的值,则不会在页面渲染的,而且我们也可以通过 ng-switch-default 来设置默认的显示部分。比如以往常见的 Tab 选项卡,用这个指令实现起来就非常的容易。

这里有一点疑问,因为我在项目中,做的单页面应用,所有不同部分都在一个页面里面。那么我经常需要根据不同的ajax返回值,显示不同的部分,(特殊原因不能使用路由),所以我这里就是用的 ng-switch 指令,根据不同返回值,显示不同部分。虽然也能实现按需显示,但看到有文章说这样使用 ng-switch 并不是很妥当,路过的朋友,有了解的可以指点我一下!我在这先谢过了~~

后面我会继续分享Angular在项目开发中的经验,以及遇到的坑!

参考资料中回答的非常精彩,也有实例,有兴趣也可以看下!

参考资源

  1. what is the difference between ng-if and ng-show/ng-hide

  2. When to favor ng-if vs. ng-show/ng-hide?

  3. AngularJS的学习–ng-show/ng-hide/ng-if和ng-switch

AngularJS实现路由以及去掉自带的#号

学习AngularJS又中断了一段时间,因为。。。。。年底忙啊!!!自己懒啊!!!!
算了,纠结这些都无果,不如,来个重新开始!昨天看了下AngularJS的路由部分,网上搜索的文章讲的都非常详细,所以,我这一片就来讲讲自己是如何一步一步是实现的吧!至于原理部分,我会分享几篇我参考的博文教程的!所以,我们今天主要讲讲实现,哎,原理等着深入了再去琢磨吧~先会用再说!

PS: 这部分一定要放到 服务器下,才能看到效果!!页面路由的嘛~~

实现AngularJS路由

AngularJS 路由部分有两种实现方法,一种是基于本身的 ngRoute,另外一种是使用 ui-router ,第二种我们后面介绍,今天的都是基于 ngRoute。

step 1 先来个基础页面

AngularJS比较适合做单页面应用,我所理解的单页面应用,就是一个项目的所有页面和功能实际都在一个页面展示,那么页面之间的逻辑,AngularJS提供了路由来实现跳转,一般之前用jQuery则是将当前需要的部分展示,其他部分暂时隐藏,等到需要切换的时候,再去设置显示隐藏,以及事件绑定等步骤。

那么这里的基础页面,实际上就是把各个页面的内容,填充进来的一个容器,我们展示给用户的,一直都是这个基础页面。AngularJS中,我们可以通过为标签添加 ng-view 属性来实现,如

或者 。我们在基本页面中,需要引入 angular.js, angular.route.js文件。除了作为AngularJS 标记的ng-app声明,还需要一个容器来加载其他的页面,那么index.html 的代码如下:

HTML

<!DOCTYPE html>
<html>
<head>
  <!-- SCROLLS -->
  <!-- load bootstrap and fontawesome via CDN -->
  <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" />
  <link rel="stylesheet" href="style.css" />

  <!-- SPELLS -->
  <!-- load angular via CDN -->
  <script src="../js/libs/angular.js"></script>
  <script src="../js/libs/angular-route.min.js"></script>
  <script src="../js/libs/angular-animate.min.js"></script>
  <script src="app.js"></script>

  <base href="/Angular/router-animation/">
</head>

<!-- apply our angular app -->
<body ng-app="animateApp">

  <!-- inject our views using ng-view -->
  <!-- each angular controller applies a different class here -->
  <div class="page " ng-view></div>

</body>
</html>

网页中实现复制到剪贴板的JavaScript插件

最近做项目,遇到了一个小功能,需要将网页中的链接地址,点击按钮进行复制,这样用户点击完了,直接粘贴即可使用。刚看到产品提供的UE的时候,我的注意力就集中到了这个功能,主要原因还是因为以前没有实现过,想想真要实现的话,估计还要请教度娘。。。经历了一些波折,总算还是把这个功能借助插件实现了,而且应该还是兼容大部分浏览器的,所以今天就把我折腾的过程进行一个总结,也让看到文章的大家,将来要用这个功能,少走点弯路~~

总共我经历了三个插件,不过因为复制到剪贴板这功能目前就那么几个插件:

  1. ZeroClipboard.js
  2. jQuery.ZeroClipboard.js
  3. Clipboard.js

文本框回车输入

之前在做Angular Todolist 项目的时候,遇到一个小问题是,输入框需要敲击回车,触发内容输入,这部分功能Angular 封装在了ng-submit 指令中了。ng-submit 默认的就是敲击回车提交表单。那么我们回到原生JS 如何实现呢,很简单,待我慢慢道来~~

本文实现了两种小效果,一种是输入框敲击回车,显示输入内容,一个是输入框输入内容,即可同步显示,有点类似Angular 的双向数据绑定

HTML

<div class="wrapper">
    <h3>单击回车,输入内容并显示</h3>
    <input type="text" class="type">
    <div class="show"></div>
    <h3>随时输入,随时显示</h3>
    <input type="text" class="type type1">
    <div class="show1"></div>
</div>
CSS
.wrapper{
    background-color: #eee;
    width: 600px;
    margin: 20px auto;
    text-align: center;
}
.type{
    display: inline-block;
    width: 300px;
    margin-bottom: 20px;
    font-size: 16px;
    padding: 8px;
}

代码非常的简单,这就是一个小的demo。接下来来到了JavaScript部分

Html5-localStorage 介绍和基本使用

这段时间一直在进行AngularJS的入门学习,前一段简单看几篇教程,以及敲了一些demo。
现在尝试做个小应用,TodoList。很眼熟吧,这是很多 MVC 入门经常被用到的小李子。这部分的经验也会在后面分享出来,今天先介绍下里面用到的本地存储方式 Html5 的localStorage。

为什么要用localStorage

功能实现方面的原因

TodoList 一个功能是,需要把之前的记录也能显示出来,由于页面刷新,以及关闭浏览器等原因,我们自己的js 存储的数据肯定会丢失,从后端获取又要重新请求数据,这里前端自己就可以“自给自足”~ 即,使用浏览器本地存储。

为什么不是cookie

提到比较流传广泛的本地存储,第一个想到的肯定不是 localStorage,而是cookie。这里参考了不少网络上牛人的文章,在末尾会有参考索引供大家查看,介绍的比我详细。cookie的优点就是应用广泛,所有浏览器都能实现—来自张鑫旭大神的博客,我没验证过~,但不足是容量比较小—-4kb,而且,读取和存储还要自己来,当然网上方法一大堆。

localStorage 有5M大小,而且IE8 都支持了,也不错,而且自身提供了读取存储和删除的方法,使用起来较为方便。

利用CSS 制作基本三角形及扩展实践

首先我们先利用CSS实现最基本的三角形,搜索百度,发现了如下的方式,对我来说是知道,却老是记不住的一些实现方式,
哪天自己打算单独实现了,就不是这个属性搞错就是其他,所以几天好好整理一下,带着自己的理解来记忆~

  • 上三角形
    .triangle-up{
        width: 0;
        height: 0;
        border-left: 100px solid transparent;
        border-right: 100px solid transparent;
        border-bottom: 100px solid red;

    }
  • 下三角形

    .triangle-down{
        width: 0;
        height: 0;
        border-left: 100px solid transparent;
        border-right: 100px solid transparent;
        border-top: 100px sold transparent;
    }
  • 左三角形
    .triangle-down{
        width: 0;
        height: 0;
        border-top: 100px sold transparent;
        border-bottom: 100px solid transparent;
        border-right: 100px solid red;

    }
  • 右三角形

    .triangle-down{
        width: 0;
        height: 0;
        border-top: 100px sold transparent;
        border-bottom: 100px solid transparent;
        border-right: 100px solid red;
    }

使用Hexo 快速搭建Github个人博客

需要准备的工具

  • Git
  • Node
  • Github账号
  • 有了Node,我们就可以利用它来安装 Hexo 打开控制台,输入npm i hexo -g 即可全局安装。

这里Mac下面还是最方便的,因为最新的XCode已经集成了Git。
因为要构建一个博客,所以首先你要在Github上新建一个项目仓库,如 myBlog

部署 Hexo

然后我们需要在本地在创建一个Hexo 项目,依次执行如下命令

  • hexo init 这里执行了Hexo 的初始化
  • npm install 安装需要使用的插件
  • hexo server 这里用Hexo 自己的服务,在网址 http://0.0.0.0:4000 可以查看目前搭建在本地的博客,这里有一个误区,访问地址并不是默认的 localhost:4000 ,看到有的同学在问,这里是个误区,不过Hexo会在命令行给出提示的。这里需要注意。
  • 然后我们可以选择看看Hexo的文件中,都提供给了我们什么,各个文件夹里面都包含了什么!网上很多教程都提供了详细解释,这里我就不说了!具体教程,可以参考我文末给出的链接。
  • 然后你也可以新添加文章,命令是 hexo new ‘new article’
  • 这里开始和网上许多教程,有些不一样了,这是因为Hexo版本不同导致的,首先我们需要安装Hexo-deployer-git插件,通过这个插件来实现我们的博客代码上传。然后还需要修改文件目录中的,_config.yml 文件中的deploy字段设置 其中的 type: git repo: https://github.com/yourname/Blog.git (注意这里的内容之前的空格不能少,因为这是Hexo的配置文件规范)。
  • 在这之后,就是要把你的添加内容提交到Github了,首先是 hexo clean
  • 然后是 hexo deploy –generate(也可以缩写为hexo d -g) ,这样再去我们的Github查看,就会看到我们的博客代码部署到了Github。

部署我们的博客到样式

  • 在我们的Github博客项目主页,将项目仓库切换到gh-pages ,点击setting,查看博客网址,类似http://benqy.github.io/hello-benqy。
    但目前的页面看上去还没有样式,我们还需要修改一点东西!
  • 去 _config.yml 修改url: http://yourname.github.io/myBlog 以及下面的root: /Blog ,
    然后再次用 hexo clean 以及 hexo deploy -generate 上传我们的文件,然后去我们的博客网址刷新页面,就可以看到Hexo 博客啦!!!