基本的数据绑定:ng-model
<!DOCTYPE html> <html ng-app> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>无标题文档</title> <script src="js/angularjs-1.4.2.js"></script> </head> <body> <input type="text" ng-model="user" /> Hello,{{user}}! </body> </html>
输入的内容可以对应显示到双括号的user里,这样就实现了最简单的数据绑定,而且可以动态控制其他DOM元素里的内容。
控制器:ng-controller
现在我们加上controller试试,因为对于angularJS来说,controller是最基本的开发模块。
<!DOCTYPE html> <html ng-app> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>无标题文档</title> </head> <body> <ul ng-controller="phonesCtrl"> <li ng-repeat="phone in phones"> {{phone.name}} <p>{{phone.system}}</p> </li> </ul> <script src="js/angular-1.0.1.min.js"></script> <script src="js/controller.js"></script> </body> </html>
然后写一个controller.js,这个controller只是做一个json格式的数据定义。
注:这里改用了1.0.1的版本,是因为不知道为什么,1.4.2的版本不支持,上网查了一下说是1.4版本的写法改变了,我也搞不清,先凑合着用1.0version吧!
// JavaScript Document function phonesCtrl($scope){ $scope.phones = [ { "name":"Apple", "system":"iOS" }, { "name":"Moto", "system":"Android" }, { "name":"Smasung", "system":"Android" } ]; }
这样我们就可以在DOM中实现根据json数组里的个数来决定循环多少次li,动态生成了DOM节点,也控制了节点的内容。
过滤条件:filter
现在我们把刚才body里的代码修改成下面这样:
<input type="text" ng-model="query" /> <ul ng-controller="phonesCtrl"> <li ng-repeat="phone in phones | filter:query"> {{phone.name}} <p>{{phone.system}}</p> </li> </ul>
新增了一个input,绑定到query的数据模型上,在循环的时候代入这个模型,这样循环的时候就会根据输入的字符串去查找json里的数据有没有包含对应的字符串,这样我们就可以动态地过滤显示的信息了。
排序:orderBy
现在我们增加一个select用来给数据排序,把body里的内容改成如下:
<input type="text" ng-model="query" /> <select ng-model="order"> <option value="name">name</option> <option value="system">system</option> </select> <ul ng-controller="phonesCtrl"> <li ng-repeat="phone in phones | filter:query | orderBy:order"> {{phone.name}} <p>{{phone.system}}</p> </li> </ul>
这样我们就可以动态控制数据的排序了。
AJAX:$http
之前我们都是把json写在controller里头,但是这种方法太笨了,一般都不会这么用,这也是我如此排斥那些垃圾angularJS的教程的原因,总是讲些没用的。
如果后端传过来一个json文件,前端要去指定的url取,这个时候我们就要用到一个service叫http,这是一个服务组件,用于处理 XMLHttpRequest,具体用法如下:
$http.get({"url":"...","method":"GET"}) .success(function(data , status){ //success code }) .error(function(data , status){ //error code });
我尝试过去写demo,但是总是出现404的错误,上网查了一下说要布置在服务器上才会有效,貌似是需要后端REST一个url出来吧,不知道是真的假的,我也不知道REST是什么鬼也懒得查对后端没兴趣总之我放弃了,一个框架总是在这种问题上纠结,挺烦的。
虽然没成功,但还是先记录一下,当$http成功后success方法会返回一个promise($q)对象,在项目中我遇到过这个service,也可以写成$promise,它还有一个then()方法,表示拿到了承诺后接下来的操作。
2015年12月22日补充:
总算弄懂了一点,昨天拿到项目中去试了一下,所谓的“需要后端REST出一个url”就是指我的url其实是根据后端的controller来写的,最后controller会return一个json给我,如果是本地的json文件那就直接引入就好了,根本就不需要动用http服务。
另外,http服务其实就是ajax服务,既然是ajax,那么就可以接收response即get(),还可以发送request,那么就是post()
$http.post(postUrl,postData). success(function (data){ //success code }). error(function (){ //error code });
模块module:ng-app
说实话真的很少能在网上找到一个系统的资料能把angularJS的基础讲清楚的,我是按照angularJS中文网的入门教程结合着大漠穷秋和其他几位大牛的博客来做的这个笔记,但是始终没有一份资料能系统整理一下,按说,ng-app应该和ng-model、ng-controller放在一起讲才对的,像前文把controller单独放在一个方法里而不定义module这种方法一般都不会用的,也只有讲了module才能讲接下来的服务和依赖注入等等。好吧,都只是吐槽,我继续。
ng-app就是模块,定义的代码如下:
var module = angular.module( "module_name" , [] );
这个moduel_name也可以写成命名空间的形式,很多资料也不会提这个。
var module = angular.module( "my.new.module", [] );
多说一句,这个module的定义参数还可以多一个function,没看见有什么资料来解释这几个参数有什么不同,我真的不懂,是现在的人们太持才傲物不屑于解释这些简单的东西吗?反正我比较蠢,搞不明白,我还以为是回调函数呢!
var module = angular.module( "my.new.module", [] , function(){ //module code });
然后我们有了moduel才在模块下面定义控制器,这样看起来才是一个系统。
module.controller('myCtrl', function($scope) { //controller code });
标准服务组件
以下几个是angularJS的标准服务组件:
$http:用于处理 XMLHttpRequest
$location:提供当前URL的信息
$q: 异步请求使用,promise/deferred模块
$routeProvider:配置路由
$log:日志服务
好吧,其实我也不懂是干什么的,javascript的基础不好就是这样,没办法,一边学一边补咯!
$http有这几个短方法:$http.get() $http.head() $http.post() $http.put() $http.delete() $http.jsonp()
注:$routeProvider后面经常会跟个when()和otherwise(),写法大概如下:
when('json', { templateUrl: 'url', controller: 'controller' }). otherwise({ redirectTo: 'url' });
貌似也没人解释过这个,貌似是用来mapping的。
方法真是够多的,就不能纯粹点吗?无奈!好吧!貌似也解释不清这些乱七八糟的,先丢在一边,我们先来看看怎么创建服务然后进行依赖注入吧!
创建服务
//使用angular.Module api注册服务: var myModule = angular.module('myModule', []); myModule.factory('serviceId', function() { //service code }); //使用$provide服务: angular.module('myModule', [], function($provide) { $provide.factory('serviceId', function() { //service code }); });
我看不出这两种方法有什么区别,既然都用到了factory,为什么还要多用一个provide,而且这种嵌套的写法真的很丑!
然后我看了一下大漠穷秋的blog,还有这样一种写法,而且他压根就没写上面两种写法,我的大脑已经混乱了!
module.service( '', [ '$rootScope', function( $rootScope ) { var service = { //service code } return service; }]);
反正我最后还是没有搞懂这个service应该怎么写!具体service下面还要涉及到哪些服务?为什么要注入一个JSON对象进来?难道不是用http service去get到的吗?rootScope是什么鬼?我已经没兴趣google了!先这样吧!也许时间会解开一切!
指令:Directives
除了创建服务以外,我们还能创建指令。在我们创建好模块以后,我们可以给模块添加控制器,添加自定义的服务,添加自定义指令,然后把我们自定义好的东西注入到控制器中去方便控制器使用我们自定义好的服务和指令,这样理解会不会更全面一些?我们甚至可以在服务里使用$http来进行ajax的数据传输这样我们就能使用angularJS进行组件化的开发?这样表达会不会更好一点?程序猿啊!麻烦你们能多提升一点交流能力吗?不要老活在自己的世界里啊!
好吧!上代码!
module.directive( "directive_name", [ 'JSONObject', function( JSONObject ) { return { restrict: "A", link: function(scope , element , attrs) { element.bind( "click", function() { //directive code }); } } }]);
说实话我仍然没有搞懂,那个json对象是怎么传进来的?restrict又是什么鬼?为什么要在指令里面写方法?我们之前在自定义的service里面不是已经可以写很多方法了吗?这里的方法和那里的方法有什么不同?大哥你们给我解释清楚啊!为什么要在这里添加事件绑定?噢漏!
大漠穷秋的blog到这里就完结了,而我的思绪却久久不能平静,现在是凌晨一点,我努力地阅读着一堆我从头到尾都看不懂的东西,想要放弃却不能放弃,然而不放弃又有什么意义呢?我到底是不是弱智?我已经开始怀疑人生了!那些说angularJS很好用的大神们,你们到底是什么样的一种体验?
我点开我手头的另外一篇资料,看到里面还有一个$resource服务,说是依赖$http服务的服务组件,接着我们要引入一个新的js文件。。。叫做ngResource。。。
<script src="js/angular-resource.js"></script>
我能把这个js理解成angularJS的插件吗?你他喵的为什么不能写在一起?
然后我们可以进行这样的操作
{ 'get': {method:'GET'}, 'save': {method:'POST'}, 'query': {method:'GET', isArray:true}, 'remove': {method:'DELETE'}, 'delete': {method:'DELETE'} };
笔记写成这样太草率了吧!搞不懂了,真的搞不懂了!
配置:config()
虽然我已经搞不懂了,但是我还是要把整个angularJS的笔记整理完,好歹知道它整个系统是什么样子的,万一以后见鬼了搞懂了呢?
写法大概是这样的:
module.config(['$routeProvider', function($routeProvider) { //map code }]);
同样是挂载到module下的,却从没有人按照平级的关系来解释!
优化过滤器:filter()
我们之前写的filter也是可以被自定义的,当然是需要自定义的,要不然太死板了,至于怎么自定义呢?如下:
angular.module('myCtrl', []).filter('filterName', function() { return function(input) { return input ? 'u2713' : 'u2718'; }; });
好吧!不管你看没看懂,反正我是没看懂!
我也不知道angularJS的东西是否主要就是这些,整体架构会涉及到的就是这些吗?我想应该还不够!但是就这样吧!实在是太零散了!反正我已经不打算学下去了,我觉得这就是一套给后端用的前端框架,我只想做一个快乐地写着jQuery插件的逗比,仅此而已!我花了这么多时间来整理,却仍然没有弄明白我想弄明白的东西,也许是我的方法不够科学,也许是我的搜索方法不够先进,反正!再见吧angularJS,写了这么多,我够对得起你了!
================更新于2015年12月22日================
经过上周末的学习,虽然还有很多不懂,但angularJS一些基本的东西已经大概会使用了。我不得不承认,angularJS在数据绑定方面还是挺好用的,简单明了,上周我有些愤怒,一边整理一边生气,也许是对自己的无能感到愤怒吧!不过确实资料太少了些,入门门槛有些高。
昨天在项目中碰到一个问题,简单记录一下。
大概是这样的,我要绑定数据到一个select上面,一开始我是想直接循环option就好了,但是这样循环出来的结果会导致option没有value,没有默认的selected项,查了一下查到了这个方法,什么都有了
<select ng-model="item.BuyMedicalStatus" ng-options="item.Key as item.Value for item in global.AccessStatus"></select>
ng-model决定了默认selected项目,ng-options是angular专门为了select写的,其中是带有语法的,我一开始循环option的时候都读不到键值对对象的value,只有这么写才能保证取到值。
================更新于2015年12月24日================
做项目的时候碰到了数据验证的问题,查了一下发现rohelm的文章不错,结合自己的项目来谈谈吧!
首先,之前的html不是我写的,没有加form,我试着去找了下看能否不加form只针对单个的输入控件进行简单的验证,然而并没有,angular在这一块还是挺按规章办事的,有好有坏吧!那我们就先加上。
<form name="testForm" ng-submit="submitForm(testForm.$valid)" novalidate></form>
然后我们定义一下submitForm这个方法
$scope.submitForm = function (isValid) { if (!isValid) { alert('验证失败'); return; } };
=====================更新于12月30日==================
之前项目中用到了对input做验证的问题,没有说完,简单补充一下,我们在给表单写了一个ng-submit以后,表单会把自己的各方面的参数比如$valid/$invalid/$dirty等等传给Controller,但是一个表单下有很多控件,如果有一个控件不符合验证要求,那么这个$valid就会变为false,所以每个控件都可以对这个$valid进行修改,具体的修改方法如下。
<input type="text" class="test_class" value="test_value" name="test_name" ng-model="test_ng_bind" required>
当我们的控件有了name以后,我们便可以对这个$valid做验证
我们准备两张图片,一张表示正确,一张表示错误,放在images下面
当数据验证通过时,显示正确icon,反之则显示错误。
还有一点,当用户还没开始输入的时候,不需要显示验证结果。
代码如下
<span ng-show="testForm.test_name && testForm.test_name.$dirty"> <img src="images/ok.gif" ng-show="testForm.test_name.$valid" /> <img src="images/no.gif" ng-show="testForm.test_name.$invalid" /> </span>
我们通过验证input控件的$dirty $valid $invalid 来决定span以及span下的img的显示,这也是控制隐藏技术的一部分。
上面这部分是我前几天碰到的问题,我已经解决了。然而今天在项目中遇到需要对select做数据验证的问题,但是select在上面这几个参数里的情况有些不一样,具体怎么不一样。。。很难形容,还是得试一把才知道,下面这段是我在公司写的源码
<div style="200px;" ng-if="sd.NegotiationType == '单体医院'"> <select class="table-select" style="130px" name="BuyMedicalStatus" ng-model="item.BuyMedicalStatus" ng-options="item.Key as item.Value for item in global.AccessStatus" required> </select> <span class="validation-flag glyphicon glyphicon-ok form-control-feedback" ng-show="sdForm.BuyMedicalStatus && sdForm.BuyMedicalStatus.$pristine"> <img src="~/Content/ok.gif" ng-show="sdForm.BuyMedicalStatus.$valid" /> <img src="~/Content/no.gif" ng-show="sdForm.BuyMedicalStatus.$invalid" /> </span> </div>
注意我在span的ng-show上和之前input下的span的ng-show的不同,没错,就是$pristine和$dirty的区别,按说$pristine是没修改过时为true,同时$dirty为false,修改过后,两个值互换,然而当我一进入页面的时候,select会有一个默认状态认为它被点选了,所以$pristine应该为false才对,而且事实上,就算是有默认值,而用户并没有change这个select,$pristine就更加应该为false才对,然而当我把这个form下的name打印出来查看以后结果却是这样的
<div>{{sdForm.BuyMedicalStatus}}</div> <!-- {"$viewValue":"备库存临采","$modelValue":"备库存临采","$validators":{},"$asyncValidators":{},"$parsers":[],"$formatters":[],"$viewChangeListeners":[],"$untouched":true,"$touched":false,"$pristine":true,"$dirty":false,"$valid":true,"$invalid":false,"$error":{},"$name":"BuyMedicalStatus","$options":null} -->
无论我如何修改select里的值,$pristine都是一直为true,而$dirty一直为false。
作为权宜之计,我暂时把验证的标准设为$pristine,这样暂时是可以行得通的,然而我觉得这并不符合逻辑。
于是我摘抄了一份关于select change的代码,希望能对我的工作起到帮助。
首先是一个select的布局
<div ng-controller="Main"> <label>Select a Color:</label><br> <select multiple ng-model="selectedValues"> <option value="1">Blue</option> <option value="2">Green</option> <option value="3">Yellow</option> <option value="4">Red</option> </select> <br> <tt> selectedValues = {{selectedValues}}</tt> </div>
然后是change的angular代码
var fiddleApp = angular.module('fiddleApp', []); fiddleApp.controller('Main', ['$scope', function($scope) { // is angular loaded and working? // (reduce future debugging) $scope.greeting = 'Hola!'; $scope.selected = [ {id:1, name:"Blue"}, {id:4, name:"Red"} ]; $scope.selectedValues = []; $scope.$watch('selected', function(nowSelected){ $scope.selectedValues = []; if( ! nowSelected ){ // here we've initialized selected already // but sometimes that's not the case // then we get null or undefined return; } angular.forEach(nowSelected, function(val){ $scope.selectedValues.push( val.id.toString() ); }); }); }]);
地址在此:请狠狠地摸我 可以看到效果
我觉得我们可以把当前selected的项目存入到作用域下的selectedValues变量中(如果是多选就是数组,如果是单选字符串就够了),然后再写一个方法判断selectedValues,如果有值,则flag为true,如果没有值则flag为false,这样就可以控制span下的img是显示ok还是no了。
而span初始的时候直接写死为ng-display:none,而ng-change之后再显示出来。
=====================更新于2016年1月14日===========
再谈Directive
app.directive('autocomplete', function () { return { restrict: 'EA', replace : true, transclude : true, template: autocompleteTpl, link: function (scope, element, attr) { } } });
上述这段代码是项目当中的一个简单的directive,虽然简单,但是却发挥了很大的功效,我第一次感受到了组件化开发的好处。
这段代码的目的是想要自己定制一个autocomplete,directive()方法的第一个参数为标签名,
返回出来的结果中需要注意几个问题:
1.restrict是指令类型,我们自定义组件的类型主流上分为两种,一种是Element,一种是Attribute,还有其他几种,总共有五种。在这里我们要做个声明,可以一起声明。
2.template是模板的相关方法,这里为autocompleteTpl,所以下面我们也会有一个autocompleteTpl()的方法定义。
3.link是模板方法的定义原型,要用到什么参数在这里做个声明就好了。(话说Google的这位大神搭出这样的一套框架,真是奇思妙想啊!)
然后我们就可以拓展这个<autocomplete></autocomplete>标签的
var autocompleteTpl = function (tElement, tAttrs) { var key = tAttrs['autoModel'].substring(tAttrs['autoModel'].indexOf('.') + 1); var _html = '<div class="autoCompleteWrapper">' + '<input type="text" placeholder="请选择" name="' + key + '"'; if (tAttrs['autoInit']) _html += ' ng-init="' + tAttrs['autoInit'] + '"'; if (tAttrs['autoRemote'] && tAttrs['autoRemote'] == 'true') _html += ' style="border:1px solid #e1e5ea;100%; height:32px"'; if (tAttrs['autoRequired']) _html += ' ng-required="' + tAttrs['autoRequired'] + '"'; else _html += ' ng-required="true"'; if (tAttrs['autoTitle']) _html += ' title="' + tAttrs['autoTitle'] + '"'; _html += ' ng-model="' + tAttrs['autoModel'] + '" ng-disabled="!nego.Permissions.CanEditAll" class="table-text searchBox-txt"></input>'; if (tAttrs['autoLinkModel']) { var linkkey = tAttrs['autoLinkModel'].substring(tAttrs['autoLinkModel'].indexOf('.') + 1); _html += '<input type="hidden" name="' + linkkey + '" ng-model="' + tAttrs['autoLinkModel'] + '" />'; } if (tAttrs['autoRemote']) { // 远程模式,不生成 Select } else { // 在非远程数据源模式下,附加Select _html += '<select class="table-select" ng-disabled="!nego.Permissions.CanEditAll" name="' + key + '_Select" ng-model="' + tAttrs['autoModel'] + '"'; if (tAttrs['autoChange']) { _html += ' ng-change="' + tAttrs['autoChange'] + '"'; } _html += 'ng-options="' + tAttrs['autoOptions'] + '"></select>'; } _html += '</div>'; return _html; }
把这个方法精简出来就是这样的
var autocompleteTpl = function (tElement, tAttrs) { var _html = "<div class="...">..."; ... if (tAttrs['...']) _html += ' ng-...="' + tAttrs['...'] + '"'; ... _html += "</div>"; return _html; }
其实就是构造一串HTML标签来代替我们自定义的指令,在构造HTML标签的时候如果出现了我们自定义的指令,就给这个HTML标签加上原生HTML属性或者angularJS封装的指令,最后返回这串字符串实现替换效果。
=====================更新于2016年1月19日===============
与angularJS表单有关
前几天解决了一个项目里的问题,项目中有很多表单,在提交数据的时候,根据我们之前做的数据验证要给出提示哪个输入控件的数据验证不通过。我之前想了很久,应该怎样获得那些验证不通过的控件呢?我们提交的form只能拿到一个isValid值,没有办法拿到具体的控件的valid值。而这个时候,当我debug项目页面的HTML标签的时候我惊奇地发现,当我违法输入的时候,element标签里面会自动生成一句class="........ng-invalid......",而当我合法输入的时候,ng-invalid不见了,取而代之的是ng-valid,我惊喜地尝试了所有的控件,发现所有控件都是有这个特性的。上网查了一下,恍然大悟!
原来angularJS为表单预置了4种样式,这四种样式分别为
/*校验合法状态*/ .ng-valid{} /*校验非法状态*/ .ng-invalid{} /*如果要使用原生的form,需要设置这个值*/ .ng-pristine{} /*表单处于脏数据状态*/ .ng-dirty{}
于是我就可以在angularJS里面愉快地使用jQuery了
$(".ng-invalid:input")
我可以给所有的控件加上中文的title,然后attr()他们的title,这样就可以提示用户哪个控件输入有误。
我还可以get到控件的offsetLeft和offsetTop,这样如果在出错的时候,我还可以让整个屏幕的scrollTop缓冲移动到验证失败的控件的offsetTop。
一个看似简单的细节,解决了我很多问题呢!
文章来源:
http://www.jdon.com/idea/angularjs3.html
http://damoqiongqiu.iteye.com/blog/1971204
http://zhaoyanblog.com/archives/99.html
http://www.cnblogs.com/rohelm/p/4033513.html
http://www.cnblogs.com/rohelm/p/4039279.html
http://www.cnblogs.com/powertoolsteam/p/angularjs-custom-directive.html
http://my.oschina.net/u/1992917/blog/406421
http://segmentfault.com/a/1190000002632671