微信公众号开发

近日摸索了公众号的开发流程特记录下来,以供大家参考。
首先最重要的是你的有一个公众号,具体怎么申请这里就不赘述了。嘿嘿,其实开发调试的时候可以使用临时的调试公众号,这样即使没有公众号也可以进行开发调试了。具体方法如下:打开http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login。 然后用自己的微信扫一下,确认即可。系统会自动给你生成一个测试公众号,有简单的管理功能。

当然你也可以直接在公众号上进行开发调试。登陆公众号平台后,在左侧菜单底部,选择开发者中心,配置服务器信息(同测试账号)。如需加密可以设置秘钥,需注意,公众账号主动调用API的情况将不受影响。只有被动回复用户的消息时,才需要进行消息加解密。参考:http://mp.weixin.qq.com/wiki/17/2d4265491f12608cd170a95559800f2d.html

服务端认证

添加服务器URL后,微信服务器会向你的公众号服务器发送一个认证请求。比如你的微信服务URL是:http://10.10.10.10/wx。微信服务器会发起一个get请求,http://10.10.10.10/wx/?signature=c4b6bed68f55e7bd09e2771674a1989619716054&echostr=3942832202382810879&timestamp=1441531611&nonce=1839804127。公众号服务器校验流程入。

  1. 将token、timestamp、nonce三个参数进行字典序排序
  2. 将三个参数字符串拼接成一个字符串进行sha1加密
  3. 开发者获得加密后的字符串可与signature对比,如果相同则校验通过
    校验通过后,返回echostr参数的内容给微信服务器,完成认证。

服务端PHP代码参考:http://mp.weixin.qq.com/wiki/17/2d4265491f12608cd170a95559800f2d.html。

nodeJS代码参考:http://my.oschina.net/fengcunhan/blog/97367

通过认证后,才能完成服务器的配置。

消息接收和自动回复

当用户向公众号发送消息时,微信服务器会自动讲消息转发到公众号服务器,发起一个post请求到http://10.10.10.10/wx。消息为xml字符串形式上传到服务器。公众号服务器解析出xml字符串中的消息类型,发送用户,以及发送的消息等信息。然后返回一个xml形式的字符串。API参考:http://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html

PHP使用$GLOBALS[“HTTP_RAW_POST_DATA”]获取消息内容。

nodejs无法自己获取,需要执行读取操作:

1
2
3
4
5
6
7
var formData="";
req.on("data",function(data){
formData+=data;
});
req.on("end",function(){
processMessage(formData,res);
});

通过API创建菜单

首先向微信服务器发起一个get请求:

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
获取到access_token,然后发起创建菜单请求。POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN

菜单信息以json格式封装。参考:http://mp.weixin.qq.com/wiki/13/43de8269be54a0a6f64413e4dfa94f39.html

Grunt--web前端构建工具

#简介
Grunt是一款使用javascript开发的开源的web前端构建工具,有着丰富的给类工具插件。Grunt及其插件运行在node.js环境上。可以完成诸如:html/css/js/image文件的压缩和合并,html/css/js文件的静态检查,js的自动化测试,引入css/js文件的版本号自动更新,文件的删除和复制等等,web前端开发所需要的各类工作。

Grunt官网:http://gruntjs.com/

非官方中文网站:http://www.gruntjs.net/

#Grunt的安装
Grunt在node.js环境上执行,使用前必须先安装node.js和npm。

##node.js的安装
直接到官网下载安装包,执行安装即可。下载地址:https://nodejs.org/en/

安装了node.js之后,打开命令行控制台,输入“node -v”来查看node。js的版本,已验证node.js是否安装成功。npm集成在node.js的安装包,安装完node.js即可。输入“npm -v”,查看npm版本。

##Grunt的安装
由于npm经常被墙,导致无法安装成功。幸好我们有了中兴自己的镜像。
执行“npm set registry http://mirrors.zte.com.cn/npm” 配置好镜像后,就能顺利完成安装了。执行“npm install -g grunt-cli” 安装Grunt,注意安装的是grunt-cli,-g表示全局安装,所有目录均可使用

安装完成后,执行“grunt -v”。验证安装是否成功。

#Grunt插件的安装
安装Grunt插件的时候可以采用两种方式,一种是单个插件安装(常用于新搭建项目的场景);一种是插件批量安装(常用于下载项目文件后搭建开发环境)。具体安装方法如下:

  1. 安装单个插件
    首先在命令行控制台里,切换到项目根目录。例如安装jshint插件,执行“npm install grunt-contrib-jshint –save-dev”命令即可。

  2. 批量安装
    首先新建package.json文件。将需要安装的插件名称和版本填写好(如果是配置库上下载,已有配置文件,此步骤跳过)。详细内容参考下一小节。
    命令行控制台切换到项目根目录。执行“npm install –save-dev”命令即可。执行成功后,根目录下会出现node_modules目录,下面会出现已安装的所有插件。
    ##package.json配置文件
    配置具体示例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    {
    "name": "wxcop",
    "version": "0.1.0",
    "devDependencies": {
    "grunt": "^0.4.5",
    "grunt-contrib-clean": "^0.6.0",
    "grunt-contrib-copy": "^0.8.2",
    "grunt-contrib-cssmin": "^0.14.0",
    "grunt-contrib-jshint": "^0.11.3",
    "grunt-contrib-uglify": "^0.9.2"
    }
    }

示例中name为当前工程名称,version工程版本信息,devDependencies为依赖项(即Grunt和相关插件)。注意,这里的版本号前面有一个^符号,表示该插件可以被Grunt自动更新到最新的子版本。
关于配置文件的说明,详见:https://docs.npmjs.com/files/package.json

#配置构建任务
在项目根目录下新建构建任务描述文件Gruntfile.js。具体示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
module.exports = function(grunt) {  
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'), //加载配置文件
uglify: {
options: { },
buildall: {//按原文件结构压缩js文件夹内所有js文件
options: {
preserveComments:false,
report: "min"//输出压缩率,可选的值有 false(不输出信息),gzip
},
files: [{
compress:false,
expand:true,
cwd:'js',//js目录下
src:'**/*.js',//所有js文件
dest: 'dist/js'//输出到此目录下
}]
},
ueditor: {//单独压缩ueditor.all.js文件
options: {
preserveComments:false,
report: "min"
},
files: [{
compress:false,
src:'vender/ueditor/ueditor.all.js',
dest: 'vender/ueditor/ueditor.all.min.js'
}]
}
},
cssmin: {
compress: {//按原文件结构压缩css文件夹内所有css文件
files: [{
expand: true,
cwd: 'css/',
src: '**/*.css',
dest: 'dist/css'
}]
},
adminlte: {//单独压缩AdminLTE.css文件
files: [{
src:'vender/AdminLTE/AdminLTE.css',
dest: 'vender/AdminLTE/AdminLTE.min.css'
}]
}
}
});
// 加载包含 "uglify" 任务的插件。
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-cssmin');
// 默认被执行的任务列表。
grunt.registerTask('default', ['uglify','cssmin:compress','cssmin:adminlte']);
};

上述代码实现了js目录下所有js文件的压缩,css目录下的所有css文件的压缩。文件主题是module.exports = function(grunt) { };函数内部主要有三部分:初始化参数,加载插件,注册构建任务。

  1. 初始化参数
    pkg属性,指定具体加载的配置文件,默认的必选项。uglify和cssmin是自己定义的任务,名称也是自定义的。
  2. 加载插件
    加载指定的插件。
  3. 注册构建任务
    default是任务名称,[]中是具体构建项,按照先后顺序执行。可以是一个任务集合’uglify’,也可以是一个具体的任务’cssmin:compress’。
    #执行构建
    在命令行控制台里,切换到项目根目录。执行grunt,即可执行default任务。执行“grunt cssmin:adminlte”,即可执行具体的一个任务。执行“grunt uglify”即可执行一个任务集合。

这里只是对grunt的一个概括介绍,配置的详细参数,具体插件的配置,后续在其他帖子里描述。

BeyongCompare跨操作系统比较文件

问题现象

一份代码在windows上,一份在linux上。直接用BC比较,js和css文件,结果文件全部红色,被判为有差异。实际文件没有修改,必须挨个文件打开,才能知道是否有变化。其他文件无此问题。如果两份代码全部在windows上也没有此问题。

问题原因

windows和linux上对换行符的定义是不一样的,所以导致BC因为有变化。

解决办法

在BC里面选择基于规则比较。

打开一个JS文件选择规则,设置“比较行结尾”

Chrome无法搜索和安装插件的解决办法

由于你懂的原因,Google Chrome插件无法搜索和安装。这里提供一个临时解决方案。

  1. 打开 http://www.cnplugins.com 或者 http://www.chromeextensions.org 网站(类似网站很多,可以自己搜索)。 搜索您需要的插件,下载到本地。
  2. 在chrome地址栏输入 chrome://extensions/ 或者通过菜单打开扩展程序。
  3. 将下载的.crx文件拖放到扩展程序页面,然后确认安装即可。

web前端开发常用的插件推荐

  1. 网页调试HTTP测试工具-Postman http://www.cnplugins.com/devtool/postman/#dlink
  2. web性能分析工具PageSpeed Insights (by Google) http://www.cnplugins.com/devtool/pagespeed-insights-by-goo/#dlink

备注:
pagespeed安装后执行分析会出现跑死现象,初步分析是改工具需要连接google获取资源,被墙了:(
可以考虑使用performance-analyser代替,进行性能分析 http://www.cnplugins.com/devtool/performance-analyser/download.html

补充postman无法安装的解决办法

  1. 使用解压缩软件将crx文件解压缩。将解压后的文件夹里的”_metadata”文件夹改名为”metadata”
  2. 打开chrome的扩展程序图片1.png
  3. 勾选开发者模式,然后点击“加载已解压的扩展程序”,选中.crx文件解压后的目录,即可安装成功图片2.png
  4. 使用时在扩展程序中点击“启动”,打开postman后,无需注册也可以使用图片3.png
  5. 以后每次打开chrome都会出现如下提醒,一定要选择“取消”,否则插件会停用图片4.png

spring boot开发经验汇总

本文记录了Spring boot开发过程中遇到的一些坑,整理一下,希望能够帮助大家

spring boot工程在Eclipse中发布

导出jar包方式(以demo工程为例)

  1. 工程右键,选择export-》java-》Runnbale JAR file
  2. 选择launch configuration,设置输出目标文件,勾选依赖库导入到单独目录(demo_lib)
  3. 将工程属性文件,例如application.properties拷贝到jar文件所在目录。
    完成的发布包应该包括:demo.jar,demo_lib,application.properties等文件
    执行 java -jar demo.jar即可启动整个项目

已有数据表如何导入入到项目的Entity

使用jpa工具导入
在 eclipse matketpalce里搜索jpa,选择 JPA Diagram Editor进行安装
在工程的属性里,选择 project facets 再选中JPA
工程右键,选择JPA Tools->Generate Entities from Tables。然后配置数据库连接字,选择数据表,生成entity

上传文件大小最大值设置

Spring boot默认最大上传文件是10Mb,如果要改变这个阈值需要自己设置。不同Spring boot版本存在差别。举例如下:

1
2
3
4
5
6
7
8
9
10
//例如在application.properties
//Spring Boot 1.3.x或者之前
multipart.maxFileSize=500Mb
multipart.maxRequestSize=1000Mb
//Spring Boot 1.4.x或者之后
spring.http.multipart.maxFileSize=500Mb
spring.http.multipart.maxRequestSize=1000Mb
//Spring Boot 2.0.x或者之后
spring.servlet.multipart.maxFileSize=500Mb
spring.servlet.multipart.maxRequestSize=1000Mb

注意:如果有使用zuul路由服务,还需要设置zuul的相关参数。

EntityNotFoundException

使用ManyToOne进行表关联且懒加载时,如果被关联的记录不存在时,序列化时报如下异常:

1
2
3
4
5
6
7
//关联定义如下
@JoinColumn(name="topic_id")
@JsonView(JsonViews.Public.class)
@ManyToOne(fetch=FetchType.LAZY)
public Topic getTopic() {
return topic;
}

异常信息如下:
Could not write content: Unable to find com.demo.entity.topic.Topic with id 14363
//跟踪代码,能发现
javax.persistence.EntityNotFoundException: Unable to find xxx
如果关联记录不存在,还需要返回结果,增加@NotFound(action= NotFoundAction.IGNORE),解决方案:

1
2
3
4
5
6
@JoinColumn(name="topic_id")
@ManyToOne(fetch=FetchType.LAZY)
@NotFound(action= NotFoundAction.IGNORE)
public Topic getTopic() {
return topic;
}

无法下载maven依赖库

现象
下载依赖库失败。 Failure to transfer org.apache.maven.plugins:maven-compiler-plugin:pom:2.0.2 from http://repo1.maven.org/maven2 was cached in the local repository

解决办法
在下面两个文件中的mirrors 组中增加自己公司里的私有仓库
The Maven install: $M2_HOME/conf/settings.xml
A user’s install: ${user.home}/.m2/settings.xml

1
2
3
4
5
6
7
8
<mirrors>
<mirror>
<id>nexus-demo</id>
<mirrorOf>*</mirrorOf>
<name>Nexus demo</name>
<url>http://maven.demo.com.cn/content/groups/public</url>
</mirror>
</mirrors>

然后重新update,注意勾上强制更新。
STS里如果更新失败,可以尝试直接命令行 mvn clean & mvn install

其他异常

Not an managed type: class com.demo.entity.Approve

class Approve定义没有添加注解@Entity

No qualifying bean of type [com.demo.service.ApplyService] found for dependency

没有在config/JPAConfig.java里正确配置package信息。或者是没有给加上@Service注解。

AngularJS学习--Resource

AngularJS提供了$resourceService来更方便地与RESTful服务进行交互,可以方便地定义一个REST资源,而不必手动声明CRUD方法。

如何使用Resource?

具体步骤如下:

  1. 引入angular-resource.min.js文件
  2. 在模块中依赖ngResourece,
  3. 在服务中注入$resource

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//定义
var Topic = angular.module('Topic',['ngResource']);
Topic.factory('topicResource',function($resource){
return $resource('/topic/:id',
{
id:'@id'
},
{
close:{
method:'POST',
params:{close:true},
isArray:false
}
}
);
});

//调用,对id为123的topic执行close操作
topicResource.close({id:123}, successFn, errorFn)

$resource的参数说明

$resource(url,{url参数},{自定义方法})

  1. url: 必填, 资源的基础url,url中带有’:’项的是根据第二个参数来进行配置的。
  2. url参数: 选填,配置url中的带有’:’ 项的参数。例如: (‘/card/user/:userID/:id’, {userID:123,id:’@id’}), 那么userID会被配置为123.。另外,在调用$resource()的方法的时候(比如get,query…),可以传入参数覆盖这里对url参数的配置。
  3. 自定义方法: 选填,使用$resource获取到的资源,或者通过$resource实例化的资源,资源本身会具有一些方法,比如$save,第三个参数用于给资源添加自定义的方法。

$resource的方法

$resource()默认有如下5个方法:

  1. get: {method:’GET’} 一般用于获取某个资源.
  2. query: {method:’GET’,isArray:true},一般用于获取一整套的资源,isArray参数,用以设置返回数据格式,是否是数组。默认是true。
  3. save: {method:’POST’},一般用于保存某个资源,有可能是新建的资源,也有可能是更新现有的资源
  4. remove: {method:’DELETE’},一般用于删除某个资源
  5. delete: {method:’DELETE’} , 一般用于删除某个资源

$resource的响应方法

Resource.action([parameters], [success], [error])

  1. [parameters]: 可选. 一个json对象,用于配置url里的参数,比如这里写了{id:cardID},那么提交的请求url就是 ‘/card/user/123/cardID’. 可以不填,不填就直接按照$resource()里的url来提交,注意,不填的话,不需要给个空,可以直接写success回调,angular能够判断出它没有填第一个参数,而不是死板的按照顺序来解读参数.
  2. [success]:可选. 请求成功后的回调函数.回调接受2个参数(注意这里和$http有所不同)function(data, responseHeaders)
  3. [error]:可选. 请求失败后的回调.回调接受1个参数 function(httpResponse){ }
    注意:凡是通过$resource返回的对象,一定是json格式的,如果后台返回的数据不是json,$resource也会按照自己的方式处理成json格式.不能返回null,因为$resource必须返回json格式,所以要返回{}
    #响应转换
    有时基于既定的后台设计,无法提供完全RESTful的API,比如返回的是一个分页器对象,而非数组。此时,我们仍然可以使用$resource,但需要设置响应转换回调。例如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    /*
    API返回的是一个分页对象,需要读取其中的数组数据。
    {
    currentPage : 1,
    totalPage : 20,
    pageSize : 2,
    content : [
    {
    id : 1,
    body : 'hello'
    },
    {
    id : 2,
    body : 'world'
    }
    ]
    }
    */

    //定义
    var Topics = $resouce('/topic/list/page/:pageid', null, {
    page: {
    method : 'GET',
    transformResponse: function(data, headers){
    var pager = JSON.parse(data);
    return pager.content;
    }
    }
    });

    //调用
    Topics.page(function(data){
    //此时data已经是转换过的数组了
    });

当然上述转换,也可以在响应函数中进行相关的数据格式处理。

类似响应重写,你还可以设置请求转换transformRequest。

AngularJS学习--UI-Router

什么是UI-Router?

AngularJS 是一种富客户端单页面应用框架,所以要在一个页面呈现不同的视图,路由起到了至关重要的作用。 UI-Router是Angular-UI提供的客户端路由框架,它解决了原生的ng-route的不足(视图不能嵌套,同一URL下不支持多个视图)。例如实际的单页面应用中,导航栏用一个视图,内容部分用另一个视图,就是比较常见的。
UI-Router提出了$state的概念。一个$state是一个当前导航和UI的状态,每个$state需要绑定一个URL Pattern。 在控制器和模板中,通过改变$state来进行URL的跳转和路由。

如何使用UI-Router

UI-Router模块是一个可选的angularJS模块,如果需要使用,我们要单独引用js。具体应用示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<html>
<head>
<script src="lib/angular.js"></script>
<script src="lib/angular-ui-router.js"></script>
<script src="helloworld.js"></script>
<style>.active { color: red; font-weight: bold; }</style>
</head>
<body ng-app="helloworld">
<a ui-sref="hello" ui-sref-active="active">Hello</a>
<a ui-sref="about" ui-sref-active="active">About</a>
<ui-view></ui-view>
</body>
</html>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var myApp = angular.module('helloworld', ['ui.router']);

myApp.config(function($stateProvider) {
var helloState = {
name: 'hello',
url: '/hello',
template: '<h3>hello world!</h3>'
}

var aboutState = {
name: 'about',
url: '/about',
template: '<h3>Its the UI-Router hello world app!</h3>'
}

$stateProvider.state(helloState);
$stateProvider.state(aboutState);
});

路由加载的三种方法

1
2
3
4
5
6
7
8
9
10
//1、·调用$state.go方法   
$state.go('detail', {id: 1});
$state.go('index');

//2、点击包含ui-sref指令的链接
<a ui-sref="contacts">Contacts</a>
<a ui-sref="contacts.detail({contactId: 42})">Contact 42</a>

//3、url跳转
<a href="#/index" >index</a>

视图嵌套

ui-router的视图可以嵌套,视图嵌套通常对应着$state的嵌套。 index.detail是index的子$state,index_detail.html也将作为index.html的子页面。ui-view可以配合$state进行任意层级的嵌套, 即index_detail.html中仍然可以包含一个ui-view,它的$state可能是index.detail.hobbies。

视图命名

在ui-router中,一个$state下可以有多个视图,它们有各自的模板和控制器。如上面的示例代码,hello和about就是两个独立的视图,有各自的末班和控制器。

参数说明:

  • url:默认相对路径(以^开头的是绝对路径)
  • views:每个子视图可以包含自己的模板、控制器和预载入数据。 (后2项选填,控制器可以在view中绑定)
  • abstract:抽象模板不能被激活
  • template: HTML字符串或者返回HTML字符串的函数
  • templateUrl: HTML模板的路径或者返回HTML模板路径的函数
  • templateProvider:返回HTML字符串的函数
  • controller、controllerProvider:指定任何已经被注册的控制器或者一个作为控制器的函数
  • resolve:在路由到达前预载入一系列依赖或者数据,然后注入到控制器中。
  • data:数据不会被注入到控制器中,用途是从父状态传递数据到子状态。
  • onEnter/onExit:进入或者离开当前状态的视图时会调用这两个函数.
  • resolve 为控制器提供可选的依赖注入项。是由 key/value 组成的键值对象。
    key – {string}:注入控制器的依赖项名称。
    value - {string|function}:
        string:一个服务的别名
        function:函数的返回值将作为依赖注入项,如果函数是一个耗时的操作,那么控制器必须等待该函数执行完成(be resolved)才会被实例化。
        比如,视图都需要用户登录后才能访问,那么判断是否登录就可以做成一个控制器依赖
        
    1
    2
    3
    4
    5
    resolve: {authentication:['topicAuth', '$q', function(topicAuth, $q){
    return $q.when().then(function(){
    return topicAuth.authentication();
    });
    }]}

动态加载模板/内容

在实际使用时往往需要根据参数动态加载不同模板/内容。此时可以通过如下两种方式实现。

1
2
3
4
5
6
7
8
9
10
11
//1、用参数作为文件名,拼接文件名加载
templateUrl: function ($stateParams){
return '/topic/detail_' + $stateParams.type + '.html';
}

//2、用参数赋值,修改内容
templateProvider: function ($timeout, $stateParams) {
return $timeout(function () {
return '<h1>' + $stateParams.topicId + '</h1>'
}, 100);
}

路由参数:

ui-router同样支持切换视图时传递参数。默认有如下两种方式传递参数:

  1. 通过$stateParams传递。传递的参数是一个object。传递的方法有:
    • ui-sref=”index.detail({id:22})“
    • $state.go(‘index.detail’, {id: 22});
  2. 通过$location来获取和设置URL。直接在controller里引入$stateParams 和$location即可。

state 为view controller提供自定义数据。

1
2
3
4
5
6
7
$stateProvider.state('blog.index', {
templateUrl: ’templates/blog_index.html',
data: {
currentPage: 1,
pageSize: 20
}
})

上面 data 对象就是自定义数据,里面定义了页面的当前页和显示内容条数。在视图对应的 controller 中我们就可以通过下面的方法来获取自定义数据.

1
2
console.log($state.current.data.currentPage);  // 1
console.log($state.current.data.pageSize); // 20

如何判断选中视图选中

  1. $state.includes
    返回 true / false,查看当前状态是否在某父状态内,比如 $state.includes(‘contacts’)

    1
    2
    3
    4
    <!-- 包含在 /contacts 状态内部,即其作为 parant state -->
    <li ng-class="{active: $state.includes('contacts')}">
    <a ui-serif="contacts.list">Contacts</a>
    </li>
  2. ui-sref-active
    查看当前激活状态并设置 Class。例如

    1
    <li ui-sref-active="active"><a ui-sref="about">About</a></li>

控制器引入的集中方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$stateProvider.state('contacts', {//直接定义
template:'<h1>test</h1>',
controller: function($scope){
$scope.title = 'My Contacts';
}
}).state('contacts1', {//直接名称引用
template:'<h1>test</h1>',
controller: 'ContactsCtrl'
}).state('contacts2', {//直接定义并且定义别名
template:'<h1>test</h1>',
controller: function(){
this.title = 'My Contacts';
},
controllerAs: 'contact'
}).state('contacts3', { //控制器名称并且定义别名
template:'<h1>test</h1>',
controller: 'ContactsCtrl as contact'
}).state('contacts4', { //根据state动态加载
template:'<h1>test</h1>',
controllerProvider: function($stateParams) {
var ctrlName = $stateParams.type + "Controller";
return ctrlName;
}
});

注意:如果未定义template,controller将不会被实例化
更多说明:参考 https://github.com/angular-ui/ui-router/wiki

AngularJS学习--Directives

Directives是什么?

AngularJS Directives是DOM元素(例如属性,元素名,注释或CSS类)上的标记,它告诉AngularJS的 html 编译器($compile) 把特定的操作连接到DOM元素或转化为DOM元素及其子元素,使元素拥有某些特定的行为。指令是所有AngularJS应用最重要的部分。尽管AngularJS已经提供了非常丰富的指令,但还是经常需要创建应用特定的指令。

什么时候需要用Directives?

不要滥用Directives,如下一些场合需要考虑使用:

  1. 当某些元素或者元素组合以及其行为,需要在多个页面或者位置重复使用时;
  2. 引用其他jQuery组件,并且在加载数据后需要进行初始化时;

##使用Directives的好处

  1. 去除重复代码
  2. 使得HTML更具可读性 例如:
    1
    2
    3
    4
    //未使用指令时,无法知道该文本框的作用,需要查看JS代码。
    <input type="text" />
    //使用指令后,根据指令可得知是时间输入框。显然后者更清晰明了。
    <input type="text" data-date-picker />

注意:如果不涉及DOM定义,也可以考虑使用serivces

Directives有哪些形式?

AngularJS中有四种类型的自定义指令:

  1. 元素指令 E

    1
    <data-my-directive></ data-my-directive>
  2. 属性指令 A

    1
    <div data-my-attr></div>
  3. CSS class 指令 C

    1
    <div class="my-class"></div>
  4. 注释指令 M

    1
    <!– directive:my-comment -- >

Directives的命名

如果要符合HTML5的规范,可以在元素前面添加 x- 或者 data-的前缀。在匹配指令的时候,Angular会在元素或者属性的名字中剔除 x- 或者 data- 前缀。 然后将 – 或者 : 连接的字符串转换成驼峰(camelCase)现形式,然后再与注册过的指令进行匹配。例如:

1
2
3
4
5
//指令定义
app.directive('helloWorld', function() {})

//指令应用
<div data-hello-world></div>

当然,如果不需要符合HTML5规范,不可以加x- 或者 data- 前缀,一样可以正常使用。

Directives初体验

指令示例如下,

1
2
3
4
5
6
7
8
9
10
11
12
//指令定义
angular.module(‘app‘,[])
.directive('helloWorld', function() {
return {
restrict: 'AE',
replace: 'true',
template: '<h3>Hello World!!</h3>'
};
});

//指令应用
<div hello-world></div>

Directive的基本属性说明:

  • restrict – 这个属性用来指定指令的类型,可用类型:A E C M。
  • template – 这个属性规定了指令被Angular编译和链接后生成的HTML字符串。可以包含其他的指令,以及表达式({ { } })等。
  • templateUrl—模板文件(template属性的HTML字符串保存成HTML文件)的URL地址,元素内容复杂的情况下推荐使用。
  • replace – 这个属性指明生成的HTML内容是否会替换掉定义此指令的HTML元素。默认是false,templat内容直接插入到该元素里。
  • transclude –说明指令是否复制原始标记中的内容。默认为false。注意:当前指令元素无子元素只有文本内容时,会自动添加span元素。

    Directive的属性——link:

    如果我们要为DOM元素添加事件监听、监听模型属性变化、以及更新DOM等等行为时,就需要使用link属性。
    1
    2
    3
    link: function(scope, element , attrs)  {
    //dosomething
    }

参数说明:

  • scope – 指令的scope。在我们的例子中,指令的scope就是父controller的scope。
  • element – 指令的jQLite(jQuery的子集)包装DOM元素。如果你在引入AngularJS之前引入了jQuery,那么这个元素就是jQuery元素,而不是jQLite元素。由于这个元素已经被jQuery/jQLite包装了,所以我们就在进行DOM操作的时候就不需要再使用 $()来进行封装,可直接调用。 element .css(‘cursor’, ‘pointer’);
  • attrs 一个包含了指令所在元素的属性的标准化的参数对象。举个例子,你给一个HTML元素添加了一些属性,那么可以在 link 函数中通过 attrs.attributename 来使用它。

Directive的属性——compile:

1
2
3
compile: function(element, attrs) {
//dosomething
}

compile 函数在 link 函数被执行之前用来做一些DOM改造。它接收下面的参数:

  • element – 指令所在的元素
  • attrs– 元素上赋予的参数的标准化列表

要注意的是 compile 函数不能访问 scope,并且必须返回一个 link 函数。但是如果没有设置 compile 函数,你可以正常地配置 link 函数,(有了compile,就不能用link,link函数由compile返回)。

大多数的情况下,你只需要使用 link 函数。这是因为大部分的指令只需要考虑注册事件监听、监视模型、以及更新DOM等,这些都可以在 link 函数中完成。 但是对于像 ng-repeat 之类的指令,需要克隆和重复 DOM 元素多次,在 link 函数执行之前由 compile 函数来完成。这就带来了一个问题,为什么我们需要两个分开的函数来完成生成过程,为什么不能只使用一个?要回答好这个问题,我们需要理解指令在Angular中是如何被编译的。

指令是如何被编译的?

当应用引导启动的时候,Angular开始使用 $compile 服务遍历DOM元素。这个服务基于注册过的指令在标记文本中搜索指令。一旦所有的指令都被识别后,Angular执行他们的 compile 方法。如前面所讲的,compile 方法返回一个 link 函数,被添加到稍后执行的 link 函数列表中。这被称为编译阶段。如果一个指令需要被克隆很多次(比如 ng-repeat),compile函数只在编译阶段被执行一次,复制这些模板,但是link 函数会针对每个被复制的实例被执行。所以分开处理,让我们在性能上有一定的提高。这也说明了为什么在 compile 函数中不能访问到scope对象。 在编译阶段之后,就开始了链接(linking)阶段。在这个阶段,所有收集的 link 函数将被一一执行。指令创造出来的模板会在正确的scope下被解析和处理,然后返回具有事件响应的真实的DOM节点。

Directive的属性——scope:

创建指令的作用范围,scope在指令中作为属性标签传递。scope 是创建可以复用指令的必要条件,每个指令(不论是处于嵌套指令的哪一级)都有其唯一的作用域,它不依赖于父scope。
scope与父scope的关系可以分为隔离和继承两种。

  • 继承(默认就是继承): scope: true,
  • 完全隔离:scope : {},
  • 部分隔离: scope : {name=‘@userName’}

非完全隔离场景下又存在如下3种继承关系:

1
2
3
4
5
scope: {
name : '@',
age : '=',
doUpdate : '&'
}

  • “@” 单向绑定,数值可以从父scope传递到子scope,反之则不行。
  • “=” 双向绑定,数值可以在父scope,子scope之间互相传递
  • “&“ 引用,类似函数指针,使得子scope可以直接调用父scope函数,从而修改父scope的变量。
    上述举例中,是属性和scope成员变量名称一致的情况。如果不一致,需要在符号后添加原始属性名称。例如:name:’@enName’

注意:指令应用时,继承的属性名称必须使用-连接单词,不能用驼峰命名。

1
<div my-directive do-update="doUpdate"><div>

AngularJS学习--Services

Services是什么?

AngularJS里Services主要负责提供一个接口把特定功能的方法放在一起。类似封装好的类库供其他controller和directive调用。同时可以在应用中的不同作用域之间共享数据。Services作为单例对象在需要使用的时候被创建,只有在应用生命周期结束的时候(关闭浏览器)才会被清除。而controllers在不需要的时候就会被销毁。Service都是单例的,就是说在一个应用中,每一个Serice对象只会被实例化一次(用$injector服务)。

Services类型

AngularJS带来了很多类型的Services定义方法。每个方法都会它自己不同的使用场景。下面我们会分别介绍。

1、Constant

1
2
3
4
app.constant('myConfig', {
isLoad : true,
des : "default config"
});

Constant就是常量,赋值后不能被修改,常用于对directive等做配置信息。所以当你想创建一个directive,并且你希望能够做一些配置信息,同时给些默认的配置,constant是个不错的的选择。常量可以是基础类型或object对象。

2、Value

1
2
3
4
app.value('myConfig', {
isLoad : true,
des : "default config"
});

Value和Constant很相似,唯一区别是其在赋值后还可以被改变。它也被常用于directive配置信息。也可以理解为全局变量。

3、Factory

1
2
3
4
5
6
7
8
9
10
app.factory('myFactor', function() {
var name = "myFactor";
function getName() {
return name;
}
return {
des: "This is factory service",
getName: getName
};
});

Factory是我们最常用的service,会返回一个object对象。可以理解为工厂模式。Factory创建可以有多种方式,下面的代码就是另外一种创建方式。

1
2
3
4
5
6
7
8
9
10
app.factory('myFactor', function() {
var myInstance= {
var des = "This is factory service";
var name = "myFactor";
function getName() {
return name;
}
};
return myInstance;
});

4、Service

1
2
3
4
5
6
7
app.service('myService', function() {
var name = "myService";
this.des = "This is factory service";
this.getName = function() {
return name;
};
});

service和factory工作原理一样,只是service接收的是一个构造函数,当第一次使用service的时候,angular会new MyService() 来初始化这个对象。后续使用的时候返回的都是同一个对象。下面是factory等价的写法:

1
2
3
4
5
6
7
8
9
10
function MyService() {
var name = "myService";
this.des = "This is service";
this.getName = function() {
return this.name;
};
}
app.factory('myService', function() {
return new MyService();
});

5、Provider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
app.provider('myProvider', function() {
var name = "myProvider";
return {
//非$get返回内容,是唯一可以在config中访问的属性和方法
setName: function(newName) {
name = newName;
},
//$get返回内容,是唯一可以在控制器中访问的属性和方法
$get: function() {
function getName() {
return name;
}

return {
des: "This is provider service",
getName: getName
};
}
};
})

provider是唯一一种你可以传进 .config() 函数的service,factory和service都不可以。当你想要在 service 对象启用之前,先进行参数配置,那就应该用 provider。

注意:config是传入参数为provider的名称后面加provider。

1
2
3
.config(function(myProviderProvider) {
myProviderProvider.setName('myProviderProvider');
});

参考资料:
http://www.tuicool.com/articles/rABfUvm
http://www.cnblogs.com/whitewolf/p/angular-services.html

sublime如何设置不同语言的缩进

首先用sublime打开一个文件,比如要设置java语言的缩进,就打开一个java文件。

菜单栏: Preferences -> Settings - More -> Syntax Specific - User,打开属性编辑页面,录入如下内容。保存即可

1
2
3
4
{
'tab_size': 2,
'translate_tabs_to_spaces': true
}

注意:tab_size表示tab键转成几个空格,请根据自己团队的变成规范来设置。

该设置文件会保存在如下路径\Sublime Text 2.0.2\Data\Packages\User。文件名为语言名+“.sublime-settings”后缀。比如java语言的配置文件就是Java.sublime-settings。