Angular 学习笔记

作者 Zhs 日期 2016-05-15
Angular 学习笔记

angular应用骨架

ng-app定义的 位置,决定了angular的处理边界。

可以绑定在<html>、<body>或者任意的<div>标签上

MVC Model View Controlle

    • 用来容纳数据的模型,代表当前的状态。
    • 用来展示数据的视图
    • 用来管理模型和视图之间关系的控制器
  • 正确定义控制器:定义成模型的一部分
<html ng-app='myAPP'>
<body ng-controller='controllerName'>
<!--Here goes your HTML code-->
{{userName}}
<script src="js/angular.min.js"></script>
<script src="js/app.js"></script>
</body>
</html>
var myAppModel = angular.module('myApp',[]);
myAppModel.controller('controllerName',function($scope){
<!--Here goes your code-->
$scope.userName = "sisi"
});

上面的代码中,myApp代表html中模型的名称,通过调用Angular的.module方法,创建新的模型,并将控制器定义给模型。

模板和数据绑定 介绍基本元素

​ - 控制器中所有定义在$scope中的数据,都可以在模型中使用(上面代码中,的部分会显示sisi)

​ - 模型中通过指令ng-module=''进行数据绑定元素绑定到模型属性上

  • 显示文本

    • 双花括号的方式 <p></p>
    • 基于属性的指令 <p ng-bind="greeting"></p>,这条语句会绑定到上面的输入(假设有的话。)
  • 表单输入

    <form ng-submit="requestFunding()" ng-controller="StartUpController">
    Starting: <input ng-change="computeNeeded()" ng-model="startingEstimate">
    Recommendation:{{needed}}
    <button>Fund my startup!</button>
    <button ng-click="reset()">Reset</button>
    </form>
    function StartUpController($scope){
    $scope.computeNeeded=function(){
    $scope.need=$scope.startingEstimate*10;
    };
    $scope.requestFuning = function(){
    windown.alert("Sorry,please get more customers first.");
    };
    $scope.reset = function(){
    $scope.startingEstimate = 0;
    };
    }

    上面的代码实现下面的功能:

    1. 监视输入框,当检测到数据变化时计算推荐值
    2. 点击reset按钮后,重置数值。
  • 列表、表格&其他迭代型元素

    • ng-repeat 根据集合中的项目创建一组元素的多分拷贝

    • <table ng-controller = 'AlbumController'>
      <tr ng-repeat = 'track in album'>
      <td>{{$index+1}}</td>
      <td>{{track.name}}</td>
      <td>{{track.duration}}</td>
      </tr>
      </table>
  • 表达式

  • 区分UI和控制器的职责

    控制器职责

    • 为应用中的模型设置初始状态
    • 通过$scope对象把数据模型和函数暴露给视图(UI模板)
    • 监视模型其余部分的变化,采取相应的动作
  • $scope暴露模型数据

    • 显式的:$scopt.count=5
    • 隐式的:表达式ng-click="count = 5" / 指令ng-model
  • $watch监控数据模型的变化

    函数定义为$watch(watchFn,watchAction,deepWatch)

    watchFn可以是表达式或者函数的字符串,返回被监控的数据模型的当前值。

    watchAction可以是表达式或者函数,当watchFn发生变化时会被调用,接收三个参数,函数签 名如:function(newValue,oldValue,scope)

    deepWatch布尔值,当为true时,将监控数组的所有元素或者对象上的每一个属性。

  • $watch()的性能问题

    <div ng-repeat="item in items">
    <span>{{item.title}}</span>
    <input ng-model="item.quantity">
    <span>{{item.price|currency}}</span>
    <span >{{item.price*item.quantity|currency}} </span>
    <button ng-click="remove($index)">Remove</button>
    </div>
    <div>Total:{{totalCart()|currency}}</div>
    <div>Discount:{{discount|currency}}</div>
    <div>Subtotal:{{subtotal()|currency}}</div>
    $scope.totalCart=function(){
    var total = 0;
    for(var i = 0,len = $scope.items.length; i<len;i++){
    total = total+ $scope.items[i].price*$scope.items[i].quantity;
    }
    return total;
    }
    $scope.subtotal = function(){
    return $scope.totalCart() - $scope.discount;
    }
    function calculateDiscount(newV,oldV,scope){
    $scope.discount = newV > 100 ? 10:0;
    }
    $scope.$watch($scope.totalCart,calculateDiscount);

    上面的代码实现的功能:当总金额超过100时,给予10$的折扣。

    虽然功能可以实现,但是在实际运行中过多的调用了totalCart()函数,这不是良好的代码。

    有多种方法解决上述问题,其中一种是监控items数组

    $scope.bill={};
    var calculateTotals = function(){
    var total = 0;
    for(var i = 0,len = $scope.items.length; i<len;i++){
    total = total+ $scope.items[i].price*$scope.items[i].quantity;
    }
    $scope.bill.totalCart = total;
    $scope.bill.discount = total > 100? 10:0;
    $scope.bill.subtotal = total - $scope.bill.discount;
    }
    $scope.$watch('items',calculateTotals,true);
    <!--对应的模型中的元素要变为-->
    <div>Total:{{bill.totalCart|currency}}</div>
    <div>Discount:{{bill.discount|currency}}</div>
    <div>Subtotal:{{bill.subtotal|currency}}</div>

    这种策略可能会工作的很好,但是angular会制作一份数组的拷贝(为了监控其变化)。这对于大型数组来说就会很耗费资源,如果只需要重新计算 bill属性就会好很多。如下:

    $scope.bill={};
    $scope.$watch(function(){
    var total = 0;
    for(var i = 0,len = $scope.items.length; i<len;i++){
    total = total+ $scope.items[i].price*$scope.items[i].quantity;
    }
    $scope.bill.totalCart = total;
    $scope.bill.discount = total > 100? 10:0;
    $scope.bill.subtotal = total - $scope.bill.discount;
    });
  • 监控多个东西

    • 监控属性连接起来之后的值
    $scope.watch('things.a+things.b',callMe(...));
    • 放到数组或对象中,deepWatch设置为true
    $scope.watch('things',callMe(...),true);

使用Module 组织依赖关系

先看一个例子

function ItemsViewController( $scope){
// *向服务器发出请求*
...
// *解析响应并放入Items对象*
...
// 把Items数组设置到`$scope`上,交给视图显示
}

潜在问题

​ - Items不可复用

​ - 控制器边界难以界定,

​ - 不利于单元测试(启动服务器/monkey patching)

接下来是模块和模块注入功能

function ShoppingController($scope,Items){
$scope.Items.query();
}
//建立Items服务

// 创建模型
var shoppingModule = angular.module('ShoppingModule',[]);

//设置服务工厂,创建Items的接口。
shoppingModule.factory('Items',function(){
var items = {};
items.query = function(){
// 通过服务器拉取
var result = $http.get('/url').success(function(data,status,headers,config){
return data;
});;

return result;
}
return items;
});
  • 关于创建服务
provider(name,object or constructor() ) 一个可配置服务,若传递Object作为参数,则该对象必须带有一个$get函数,该函数需要返回服务的名称。否则,默认传递的是构造函数。
factory(name,$get Function() ) 不可配置服务,需要指定一个函数,当调用函数时返回服务的实例。
service(name,constructor) 不可配置服务,与provider类似。

使用过滤器格式化数据

语法

{{ express | filterName : parameter1 : ... parameterN }}
- 内置过滤器: ` currtency,date,number,uppercase`等。 比如:` {{12.9 | currency | number:0 }}` 会显示成`$13`
- 自定义过滤器:eg.首字母大写

function HomeController($scope) {
$scope.pageHeading = 'behold the majesty of your page title';
}

var homeModule = angular.module('HomeModule', []);
homeModule.filter('titleCase', function() {
var titleCaseFilter = function(input) {
var words = input.split(' ');
for (var i = 0; i < words.length; i++) {
words[i] = words[i].charAt(0).toUpperCase() + words[i].slice(1);
}
return words.join(' ');
};
return titleCaseFilter;
});

然后就可以愉快的使用他们了:

<h1>{{pageHeading | titleCase}}</h1>

路由和$location

作用 :对于浏览器所指向的特定URL,Angular 会加载并显示一个模板,并实例化一个控制器来为模板提供内容。
创建 :调用$routeProvider服务上的函数来创建路由,把需要创建的路由当成一个配置块传给这些函数即可。eg:

var someModule = angular.module('someModule',[.....dependencies...]);
someModule.config(someRouteConfig);
function someRouteConfig($routeProvider){
$routeProvider.
when('/',{controller:aController,templateUrl:'list.html'}).
when('/view/:id',{controller:bController,templateUrl:'detail.html'}).
otherwise({redirectTo:'/'});
}

与服务器交互

$http 服务

function ShoppingController($scope, $http) {
$http.get('/products').success(function(data, status, headers, config) {
$scope.items = data;
});
}

指令修改DOM

创建指令,就像创建服务一样: 通过模块对象的API来定义,只需要调用directive()函数即可

var appModule = angular.module('appModule',[]);
appModule.directive('directiveName',direvtiveFunction);

实际的小例子

      var appModule = angular.module('app', []);
//这里定义了新的指令,会调用对象的 focus() 方法
appModule.directive('ngbkFocus', function() {
return {
link: function(scope, element, attrs, controller) {
element[0].focus();
}
};
});

使用新定义的指令,则视图显示时,该按钮会处于聚焦状态

<button  ngbk-focus ng-click="clickUnfocused()">
Not focused
</button>

校验用户输入

示例如下:

<form name='addUserForm' ng-controller="AddUserController">
<div ng-show='message'>{{message}}</div>
<div>First name: <input name='firstName' ng-model='user.first' required></div>
<div>Last name: <input ng-model='user.last' required></div>
<div>Email: <input type='email' ng-model='user.email' required></div>
<div>Age: <input type='number' ng-model='user.age' ng-maxlength='3'ng-min='1'> </div>
<div>
<button ng-click='addUser()' ng-disabled='!addUserForm.$valid'>Submit</button>
</div>
</form>
function AddUserController($scope) {
$scope.message = '';

$scope.addUser = function () {
// actually save user to database...
$scope.message = 'Thanks, ' + $scope.user.first + ', we added you!';
};
}

以上所有的验证工作均可在模板内完成,而不需要控制器进行监视。