AngularJS 学习笔记-第一章:速成

本文章是针对于刚开始学习angularJS的菜鸟们,根据(精通angularJS一书)做的精简版读书笔记, 旨在通过笔记让更多的前端开发,在一周之内可以入门AngularJS。

第一章 速成

1.快速使用
a.导入angularJS 库文件 (线上库或者离线库都可以,如果本地测试建议使用离线库,因为速度不是快一点点哦)
b.在html或者body DOM标签上添加 ng-app,表示这是一个angularJS的应用
c.初始化一个模型,并使用。ng-init初始化模型,并通过表达式 {{name}}传递值
<!DOCTYPE html>
<html>

<head>
    <title></title>
    <!-- 线上版本:https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.js
    脱机版本,请从https://angularjs.org/ 下载angular.js并且放在html用目录下 -->
    <script type="text/javascript" src='angular.js'></script>
</head>

<body ng-app ng-init="name='World'">
<!-- input 的改变会直接反应在 下面的<p>-->
    <input type="text" name="" ng-model="name">
    <p>Hello, {{name}}</p>
</body>

</html>

hello world的例子没有明显的分层策略:数据初始化、逻辑和视图都混在了同一个文件中。通常情况我们希望把逻辑控制放倒一个controller里(ng-controller)来取而代之ng-init

<!DOCTYPE html>
<html>

<head>
    <title></title>
    <script type="text/javascript" src='angular.js'></script>
    <script type="text/javascript">
    // 1.3.0-beta.15 以后的版本不能直接定义controller,必须先定义model,再注入controller
    var myModel = angular.module('myapp1', []).controller('myController', function($scope) {
        $scope.name = 'eric';//$scope对象是末班的域模型
    });
    </script>
</head>
<!-- 必须要声明ng-app,不能使用匿名。myApp1对应于module名字-->
<body ng-app='myapp1'>
    <div ng-controller="myController">
        <input type="text" name="" ng-model="name">
        <p>Hello, {{name}}</p>
    </div>
</body>

</html>

2.作用域层级
  每个$scope都是Scope类实例,$scope类拥有很多方法,用于控制作用域的生命周期、提供事件传播功能,一级支持模板的渲染等。
  function($scope){} 只有参数,它是从何而来呢?
答案是ng-controller指令会去调用scope对象的$new()方法创建新的作用域$scope。看上去需要至少一个作用域实例才能创建新的作用域。确实如此,AngularJS拥有$rootScope,它是其他所有作用域的父作用域,将在新应用启动时自动创建
  ng-controller指令是作用域创建指令,DOM树中遇到作用域创建指令时,AngularJS都会闯将Scope类的新实例$scope,新创建的$scope会拥有$parent属性,并指向它的父作用域。
  某些指令会创建子作用域,如ng-repeat,对应每个country都有个新变量要暴露给$scope而且不能覆盖之前变量的值。AngularJS为了解决此问题,给集合中每个元素创建新的作用域。这些作用域会组成类似DOM树 层级结构,用Chrome的插件Batarang可以查看
  这里写图片描述

<!DOCTYPE html>
<html>

<head>
    <title></title>
    <script type="text/javascript" src="angular.js"></script>
    <script type="text/javascript">
    angular.module('myApp', []).controller('myController', function($scope) {
        $scope.countries = [{
            name: '中国',
            population: '13亿'
        }, {
            name: '日本',
            population: '1亿'
        }];
    });
    </script>
</head>

<body ng-app='myApp'>
    <div ng-controller='myController'>
        <ul>
            <li ng-repeat='country in countries'>
                {{country.name}} has {{country.population}}
            </li>
        </ul>
    </div>
</body>

</html>

  作用域中定义的属性对所有子作用域是可见的,只要子作用域没有定义同名属性。AngularJS中的作用域继承和Javascript的原型继承遵循同样的规则(沿继承树向上查找属性,直到倒找为止)。
  在进行read access的时候作用域层级的继承关系直观易懂。然而,进行write access是的情况就有些复杂了。访问父作用域的属性,就必须使用 parent.name(使 parent属性,因为塔在AngularJS表达式和模板创建的DOM结构间建立了强关联)
  推荐解决方案是将变量绑定为某个对象的属性,而不是直接绑定作用域的属性。如:

<!--错误的方式,子作用域的name和父作用域冲突覆盖之-->
<body ng-app='myapp1' ng-init="name='world'">
    <h1>{{name}}</h1>
    <div ng-controller="myController">
        <input type="text" name="" ng-model="name">
        <p>Hello, {{name}}</p>
    </div>
</body>
<!--正确的方式-->
<body ng-app='myapp1' ng-init="thing={name:'world'}">
    <h1>{{thing.name}}</h1>
    <div ng-controller="myController">
        <input type="text" name="" ng-model="thing.name">
        <p>Hello, {{thing.name}}</p>
    </div>
</body>

作用域通常在不需要的时候会被销毁(垃圾回收),也可以通过调用Scope类上的 new() destroy()方法,手动创建和销毁作用域。

3.事件系统
事件可以从任何作用域开始分发(dispatch)。然后向上分发( emit广( broadcast).
这里写图片描述
AngularJS的核心服务与指令通过这趟事件列车来发送信号,通知应用状态的重要变化。例如,可以监听$locationChangeSuccess事件(由4rootDvope广播),当浏览器url变化时我们可以收到如下通知。

$scope.$on('$locationChangeSuccess', function(event, newUrl, oldUrl){//做出相应变化});
//$on方法,用于注册作用域事件的处理器。与DOM事件一样event对象有preventDefault()和stopPropagation()方法。

AngularJS中,仅有三个事件可以被向上分发(emitted)

  1. $includeContentRequested
  2. $includeContentLoaded
  3. $viewContentLoaded
    七个事件可以被向下广播(broadcasted)
  4. $locationChangeStart
  5. $locationChangeSuccess
  6. $routeUpdate
  7. $routeChangeStart
  8. $routChangeSuccess
  9. $routeChangeError
  10. $destroy

4.模块与依赖注入
全局定义的控制器构造函数,只在简单代码示例和快速制作原型时才有用,永远不要在复杂的真是应用中使用全局定义的控制器。
让我们看看如何将丑陋的,全局定义的控制器模块化:

    //模块化之前,mycontroller是全局函数
    var mycontroller = function($scope) {
        $scope.name = 'eric';
    }
    //模块化之后,mycontroller只应用于ng-app=‘myapp1’的模块
    var myModel = angular.module('myapp1', []).controller('myController', function($scope) {
        $scope.name = 'eric';
    });

依赖注入第一步,是将对象注册在module上,我们不直接注册对象的实例,而是将对象创建的方案抛给依赖注入系统,而后AngularJS解释这些方案以初始化对象,并在之后连接它们,最后成为可运行的应用。

AngularJS的 provide injector服务会解释这些方案,生成玩呗二可用的对象实例(已经解决好所有的依赖关系)

$injector服务创建的对象称之为服务(service)。在整个应用的生命中,每种方案AngularJS仅解释一次,也就是说,每个对象仅有一个实例。
原型继承

    var NotificationService = function (){
        this.MAX_LEN = 10;
        this.notificationsArchive = new NotificationsArchive();
        this.notifications = [];
    };

    NotificationService.prototype.push = function(notification){
        var newLen, notificationToArchive;
        newLen = this.notification.unshift(notification);//unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。
        if(newLen > this.MAX_LEN){
            notificationToArchive = this.notifications.pop();
            this.notificationsArchive.archive(notificationToArchive);
        }
    };

    NotificationService.prototype.getCurrent = function(){
        return this.notification;
    };

Value注册
注册已经初始化好的对象,缺点是被注册的值必须是已经实例化好,并且不依赖于其他对象的

  //angularJS值(value)注入
  var myapp1 = angualr.module('myapp1',[]);
  myapp1.value('notificationsArchive', new NotificationsArchive());

Service注册
NotificationService依赖于notificationsArchive,所以他不能注册为值对象,

  //angularJS(serivce)服务注入,通过依赖注入,在NotificationService构造函数中可以消除new关键字,意味着此服务已经不依赖于其他对象的初始化,可以接受哦任何归档服务,这让应用变得非常灵活。
  myapp1.service('notificationService',NotificationService);
  //NotificationService构造函数
  var NotificationService = function(notificationsArchive){
    this.notificationsArchive = notificationsArchive;
  }

Factory注册
另一条注册对象创建方案的途径是使用factory方法,任何能够创建对象的函数都可以被注册,因此它想必service而言更加灵活。factory是让对象加入依赖注入系统最常用的方法,它十分灵活,又能实现复杂的对象创建逻辑。

    myapp1.factory('NotificationService', function(notificationsArchive) {
        this.MAX_LEN = 10;
        this.notifications = [];

        var ret = {
            push: function(notification) {
                var newLen, notificationToArchive;
                newLen = this.notification.unshift(notification); //unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。
                if (newLen > this.MAX_LEN) {
                    notificationToArchive = this.notifications.pop();
                    notificationsArchive.archive(notificationToArchive);
                }
            },

            getCurrent: function() {
                return this.notification;
            }
        }
    });

constant注册

//理想状态下应该这样子获取配置,而不是写死在函数中
myapp1.factory('NotificationService', function(notificationsArchive, MAX_LEN) {...});

myapp1.constant('MAX_LEN', 10);

Provider
以上所有注册方法,都是最通用方法provider的特殊类型,下面是provider注册notificationsService的例子

//provider是一个函数,它返回包含$get属性的对象,改属性是一个工厂函数,它被调用时会返回service的实例,在$get方法被调用前,还能设置配置信息。实际上,我们能够设置maxLen
    myapp1.provider('notificationsService', function() {
        var config = {
            maxLen: 10
        };

        var notifications = [];

        return {
            setMaxLen: function(max) {
                config.maxLen = max || config.maxLen;
            },

            $get: function(notificationsArchive) {

                return {
                    {
                        push: function(notification) {
                            var newLen, notificationToArchive;
                            newLen = this.notifications.unshift(notification); //unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。
                            if (newLen > config.MAX_LEN) {
                                notificationToArchive = this.notifications.pop();
                                notificationsArchive.archive(notificationToArchive);
                            }
                        },

                        getCurrent: function() {
                            return this.notification;
                        }
                    }
                }
            }

        }
    });

模块的生命周期
如前所属,angularJS支持多装对象创建方案,provider是其中的通用方法,它在创建对象实例前可以对其进行配置。为了支持provider,angularJS将模块生命周期分为两个阶段,
1. 配置阶段
2. 运行阶段

//这里config对notificationServiceProvider有依赖,表明他是即将执行的对象创建方案,配置阶段允许我们对他进行最后的调整
myapp1.config(function(notificationSerivceProvider){
    notificationSerivceProvider.setMaxLen(5);
});
//为了说明运行阶段的作用,我们假设要显示应用的已运行时间给用户。为了实现目标,我们将应用的已运行事假定义为$rootScope实例的属性,代码如下:
myapp1.run(function($rootScope){
    $rootScope.appstart = new Date();
})

模块的生命周期

-对象种类可以在配置阶段注入可以在运行阶段注入
Constant常量值YesYes
Variable变量值-Yes
Service构造函数创建的新对象-Yes
Factory工厂函数返回的新对象-Yes
Provider$get工厂函数创建的新对象Yes-

模块依赖注入

//通知服务和归档服务,可以迁移到它们自己的模块中去(分别命名为notifications和archive)
angular.module('application',['notifications','archive'])

跨模块的可见性
1. 定义在子模块的服务可以注入父模块

angular.module('app', ['engines'])
    .factory('car', function($log, dieselEngine) {
        return {
            start: function() {
                $log.info('starting' + dieselEngine.type);
            }
        }
    });


angular.module('engines', [])
    .factory('dieselEngine', function() {
        return { type: 'diesel' };
    });
  1. 兄弟模块的服务也互相可见

angular.module('app', ['engines', 'cars']);

angular.module('cars', [])
    .factory('car', function($log, dieselEngine) {
        return {
            start: function() {
                $log.info('starting' + dieselEngine.type);
            }
        }
    });


angular.module('engines', [])
    .factory('dieselEngine', function() {
        return { type: 'diesel' };
    });
  1. 应用中的服务是不能被重名的,父模块的服务会被子模块中的同名模块覆盖。

5. 总结AngularJS 于JQuery
AngularJS内嵌jQuery的一个简化版本jqLite(是一个微小子集,只专注于操纵DOM的例程routine),内嵌jqLite后,AngularJS不再依赖其他外部库。
jQuery与AngularJS虽然可以合作,但是AngularJS围绕模型(由模型的变化来驱动模板(声明式)来更新UI),jQuery围绕DOM,它们是彻底不同的开发范型。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值