angular实用手册

##angular使用iframe方法

主要利用$sce服务:

html:

iframe(name="iframe",width="100%",height="100%",frameborder="0",scrolling="auto",ng-src="{{trustSrc()}}")

js:

.controller('mainController', [
  '$scope', "$sce"
  ($scope, $sce)->
    $scope.frameSrc = "http://www.baidu.com/"

    #iframe解析src
    $scope.trustSrc = ()->
      $sce.trustAsResourceUrl($scope.frameSrc)
])

##angular双向数据绑定的原理

angular的双向数据绑定采用的是脏值检测,而不是重写setter和getter方法,主要利用的是$digest和$watch服务。

angular把要检测的模型配有各自的watcher,watchers把旧的值备份一副本。angular对常用的DOM事件、ajax事件等做了封装,在里面触发进入digest流程。在digest流程中,digest从rootScope开始遍历所有要检测的模型,当检测到新的值就打个dirty的标记,触发该watcher的listener,这个listener包括angular内置的更新数据和视图操作以及用户自定义的操作(如果有的话),然后再次循环digest,直到不再有变化为止。

总结起来一句话就是,通过angular指定的方式来触发angular对数据模型进行检测,如果检测到有变化则进行更新操作。

angular封装好的事件包括:

  • DOM事件,ng-click等
  • ajax事件,$http
  • Location变更事件,$location
  • Timer事件,$timeout, $interval,
  • 执行$digest,$apply

也就是说,要么使用angular原生的指令和服务,要么使用$digest或$apply去通知angular进行digest。

脏值检测、双向数据绑定

如何才能知道Model发生了变化:脏值检测$watch与$digest

angular之所以实现双向数据绑定,而其他MVC框架没有,主要因为:

  • 如果一个表单里有大量的元素,要是用jQuery的话,会有大量的代码去获取该DOM元素的值,然后去检验,再设置到model里,而angular则帮我们实现了这个烦琐的步骤,省了大量的代码
  • angular实现的双向数据绑定,主要是因为有大难点:「脏值检测」,「振荡问题」 所谓「振荡问题」是比如更改表单的值,这时表单的值的改变会同步更新到数据模型,而数据模型更新后,又要去刷新视图,造成了「依赖循环问题」

##指令exports API

对于指令向外exports API这个问题,我专门请教了一些专家,他们也给出了专业的回答:

我的问题是:

Sometimes I want to make custom directives export API in order to let parent controller update the directive ui.

然后@gkalpak 大神回答:

@luozt, there are several ways to implement this. Using events is one. Other ways include using a shared service or using directive communication (i.e. requiring a parent controller and calling functions on it). What to choose strongly depends on the exact usecase.

所以,有三种方法可以实现:1、通过$scope的$on, $emit, $broadcast事件;2、一个共享的service;3、指令内的交互如link的第4个参数parentController

我选择的是第一种events的方法,因为它能自动卸载。但要注意的是:需要给指令指定一个ID,在事件里判断触发事件的参数是否有该ID,因为同一页面可能同时存在多个同一指令,所以要用ID去区分。示例如下:

#bootstrap modal的通用方法,.modal指令
#包括$modal.modal("show")
#外部controller使用
.directive("modal", [
  ()->
    return {
      restrict: "C"
      scope: {}
      link: (scope, elem, attrs)->
        #exports API
        scope.$on("api.bsModal", (event, data)->
          if data.drId != attrs.drId
            return
          switch data.apiName
            when "show"
              elem.modal("show")
            when "hide"
              elem.modal("hide")
        )
    }
])

另外,由徐飞撰写的文章也提及到组件跟主文档的通讯, WebComponents文章得知,ng的指令通信都是用事件的方式来进行组件内外通信!!

##$templateCache缓存模板

app.run(function($templateCache){
    $templateCache.put("hello.html", "<div>what is app</div>")
})

app.directive("hello", function($templateCache){
    return {
        restrict: "AECM",
        template: $templateCache.get("hello.html"),
        replace: true
    }
})

##scope变量直接插入HTML的方法

方法1:ng-bind-html

  1. 引入angular-sanitize.js
  2. 注入ngSanitize模块
  3. 使用ng-bind-html即可绑定插入HTML:<div ng-bind-html="html"></div>

方法2:$sce服务

好处是无需引入额外的插件:

HTML:

<div ng-bind-html="get()"></div>

JS:

app.controller("control", function($scope, $sce){
    $scope.get = function(){
        return $sce.trustAsHtml(html);
    };
});

##触发$scope的destroy事件 ##$timeout的使用

angular切换视图时,会触发$scopedestroy事件

//当timeout被定义时,它返回一个promise对象
var timer = $timeout(
  function() {
    console.log("Timeout executed", Date.now());
  },
  2000
);

//将resolve/cancel处理函数绑定到timer promise
timer.then(
  function() {
    console.log("Timer resolved!", Date.now());
  },
  function() {
    console.log("Timer canceled!", Date.now());
  }
);

//当DOM元素从页面中被移除时,AngularJS将会在scope中触发$destory事件。这让我们可以有机会来cancel任何潜在的定时器
$scope.$on(
  "$destroy",
  function(event) {
    $timeout.cancel(timer);
  }
);

##指令的scope绑定策略

  • @绑定:传递字符串的值,对应的是字符串
  • =绑定:双向数据绑定,对应的是一个变量
  • &负责调用方法,对应的是一个函数的调用

jade部分:

div(ng-controller="MyCtrl")
    | Ctrl:
    input(type="text",ng-model="ctrlFlavor")

    | Directive:
    drink(flavor="ctrlFlavor", taste="{{ctrlFlavor}}", pour="fnPour(thing)")

javascript部分:

app.controller("MyCtrl", function($scope){
    $scope.ctrlFlavor = "百事可乐"
    $scope.fnPour = function(thing){
        console.log(thing)
    }
})

app.directive("drink", function(){
    return {
        restrict: "AE",
        scope: {
            // 实现的是字符串传递
            taste: "@",

            // 实现数据双向绑定
            // flavor属性上的ctrlFlavor,相互传递
            // flavor = ctrlFlavor
            flavor: "=",

            // =号也可这样写
            title: "=expanderTitle",

            // 方法调用
            pour: "&"
        },
        template: 'Taste: {{taste}}, <input type="text" ng-model="flavor" />'+
                  // pour方法传进去的是一个对象:thing绑定的是flavor
                  '<button ng-click="pour({thing:flavor})">提交</button>'
    }
})

##指令compile函数

  • 一般很少用compile函数
  • compile函数是用于对指令本身的模板进行转换
  • link作用是在模型和视图之间建立关联,包括在DOM上注册监听
  • compile函数和link函数不会同时出现。如果同时出现的话,link函数会无效,angular会执行的是compile函数返回的link函数
  • 对于同一指令的多个实例,compile函数只会执行一次,而link函数对每个实例都会执行一次
  • 最重要的一点是,compile函数必须返回link函数,这是angular的规定

html:

<div alotofhello="5">
  <p>hello, boy!</p>
</div>

js:

myApp.directive("alotofhello", function() {
  return {
    restrict: "A",
    compile: function(el, attrs, transclude) {
      // 对标签进行一些变换
      var tpl = el.children().clone()
      for (var i = 0; i < attrs.alotofhello - 1; i++) {
        el.append(tpl.clone())
      }
      return function($scope, element, attr, parentController) {
        //必须返回这个link函数,如果外部再定义了link函数,则外部的无效,以这个为准
      }
    }
  }
})

##指令写法:横杠和驼峰

指令写法:属性如果要对单词进行拆分,不要用驼峰式,要用横杠拆分,而在directive里用驼峰的方式传递,如:<hello some-attr>对应指令独立scope的scope:{someAttr:"="},data-前缀的属性指令需去掉data-才匹配

指令的ng-transclude使用

指令的controller

指令的require函数

指令的scope.$apply方法调用

HTML指令:

<hello>
    <p>这是指令里面的内容</p>
</hello>

JS:

app.directive("hello", function(){
    return {
        // 独立作用域,使每个指令都有自己的$scope
        // 如果去掉,那么指令内部封装的方法会同时封装在外部controller的$scope
        // 除非外面在调用指令时也用到了ng-if等指令,它会封闭指令的scope
        scope: {},

        restrict: "AECM",
        template: "<div>what's app: <div ng-transclude></div></div>",
        transclude: true,

        // 暴露一些方法出去给公共调用
        // 公共调用时须先使用require: "^hello"
        // 然后在link的第4个参数parentController调用
        // parentController.addSpeed()
        controller: function($scope){
            $scope.abilities = []
            // 使用this来暴露出去
            this.addSpeed = function(){
                $scope.abilities.push("speed")
            }
            this.addStrength = function(){
                $scope.abilities.push("strength")
            }
        },

        link: function($scope, element, attr, parentController){
            // element是jQuery封装后的DOM
            element.addClass("btn btn-primary")
            element.bind("click", function(){
                console.log(123)
            })

            // 调用方式1
            $scope.loadData();

            // 调用方式2,注意里面字符串要加()
            $scope.$apply("loadData()")

            // 调用方式3,调用DOM的attr,不用加()
            // 在attr不管是大写还是小写,在调用时一律都用小写
            $scope.$apply(attr.howtoload)
        }
    }
})

app.directive("world", function(){
    return {
        // 表明依赖于hello指令
        require: "^hello",

        // 也有些在前面加?的
        // require: "^?hello",

        link: function($scope, element, attr, parentController){
            parentController.addSpeed()
        }
    }
})

##支持RESTful的ngResource

比$http更强大和更灵活的服务,支持RESTful模式。注意需引入angular-resource.js插件

使用方法:

//引入模块依赖
var app = angular.module("myApp", ["ngResource"]);

app.controller("ctrl", ["$scope", "$resouce", function($scope, $resouce){
    //可以定义参数在获取的url上
    var obj = $resouce(":name.:type");

    //也可在$resouce的函数参数中指定参数
    // var obj = $resouce(":name.:type", {type:"json"});

    //然后在这调用的参数中传入
    $scope.data = obj.get({name:"result",type:"json"}, function(data){
        console.log(data);
    }, function(err){
        console.log(err);
    });
}]);

##自定义服务provider/service/factory

angular是个MVC框架。Provider相当于M层,controller相当于C层,而V层是在HTML中调用各种指令来实现

通过模块来自定义服务的方式:

var m1 = angular.module('myApp', []);

m1.provider('providerServices01', function() {
    this.$get = function() {
        return {
            message: 'this is providerServices01'
        }
    }
});

// service方法可直接return返回值
m1.service('serviceServices01', function() {
    return {
        message: 'this is serviceServices01'
    }
});

// service方法也可以直接通过this来定义方法
m1.service("serviceServices02", function(){
    var _name = "kobe";

    this.getName = function(){
        return _name;
    };

    this.setName = function(name){
        _name = name;
    };
});

m1.controller('firstController', ['$scope', 'providerServices01', 'serviceServices01', function($scope, providerServices01, serviceServices01) {
    console.log(providerServices01);
    console.log(serviceServices01);
    $scope.name = '张三';
}]);

通过在模块定义的回调函数中来自定义服务的方式:

var m1 = angular.module('myApp', [], function($provide) {
    $provide.provider('providerServices01', function() {
        this.$get = function() {
            return 'this is providerServices01';
        }
    });
    $provide.service('serviceServices01', function() {
        return {
            message: 'this is serviceServices01'
        }
    });
    $provide.factory('factoryServices01', function() {
        return {
            message: 'this is factoryServices01'
        };
    });
});

m1.controller('firstController', ['$scope', 'providerServices01', 'factoryServices01', 'factoryServices02', 'serviceServices01', function($scope, providerServices01, factoryServices01, factoryServices02, serviceServices01) {
    console.log(providerServices01);
    console.log(factoryServices01);
    console.log(factoryServices02);
    console.log(serviceServices01);
    $scope.name = '张三';
}]);

其中,service不能直接返回字符串等常量,须返回object的变量,而provider和factory可以直接返回字符串等常量。

Providers中唯一一种可以传进.config函数的服务,就是provider。这个作用是可以读取和更改在angularjs的配置。例如查看以下代码中的name的配置:

var myModule = angular.module("myApp", []);

myModule.config(function(providerServices01Provider){
    providerServices01Provider.name = "Kobe";
});

myModule.provider("providerServices01", function(){
    this.name = "";

    this.$get = function(){
        var _this = this;

        return {
            setName: function(newName){
                _this.name = newName;
            }
        };
    };
})

##filter

AngularJS内置了9个filter(参考文档):

currency, date, json, limitTo, lowercase, number, orderBy, uppercase

使用示例:

{{ 1304375948939 | date }}
{{ 1304375948939 | date: "MM/dd/yyyy @ h:mma" }}
{{ 1304375948939 | date: "yyyy-MM-dd hh:mm:ss" }}

自定义filter

HTML部分:

<div ng-controller="firstController">
    {{name | customFiler:1:2}}
</div>

js部分:

app.controller("firstController", function($scope){
    $scope.name = "hello, boys!";
});

app.filter("customFiler", function(){
    return function(input,n1,n2){
        console.log(input, n1, n2);
        return input.replace(/hello/,"你好");
    };
});

##ng-non-bindable

这个指令会使忽略其中的{{}}

<div ng-non-bindable>{{text}}</div>

##ng-value

<input value="{{text}}"><input ng-value="text">是等价的

##$apply、$timeout、$watch

查看以下代码,以一个timeout后改变$scope里的值来查看它是否生效,来学习以上的方法:

myModule.controller("controllerName", ["$scope", "$timeout", function($scope, $setTimeout(function() {}, 10);){
    $scope.name = "kobe";

    //这个是不会生效
    // setTimeout(function(){
    //     $scope.name = "t-mac";
    // },1000);

    // $apply方法:要这样才生效
    setTimeout(function(){
        $scope.$apply(function(){
            $scope.name = "jordan";
        });
    },1000);

    // $timeout方法:或者这样也生效
    $timeout(function(){
        $scope.age = "james";
    },1000);

    // $watch方法
    // 少用$watch,不然内存开销太大
    destroyWatch = $scope.$watch("name", function(newValue,oldValue){
        if("james" === newValue){
            return 1;
        }else{
            return 0;
        }
    });

    // 卸载$watch
    // destroyWatch();
}]);

angular的$digest cycle会在每次调用$scope.$apply或$scope.$digest的时候运行,$scope.$apply实际上是一个$rootScope的包装,它会从根$rootScope向下传播,而$scope.$digest只会在当前$scope中运行,并向下级$scope广播

$watch方法,第一个参数是返回需要监控模型的函数,如果传入字符串,angular还是会把它转换为函数,所以:

$scope.$watch("name", function(){});
//等同于
$scope.$watch(function(){return $scope.name;}, function(){});

##angular.bootstrap

动态的创建ng-app模块:

// 在HTML中不写ng-app
// 在JS中通过bootstrap方法来启动模块
angular.bootstrap(div1, ["myApp"]);

##Referencing DOM nodes in Angular expressions is disallowed!

因为CoffeeScript把默认最后一行会添加return,导致返回如jQueryDOM的元素,故在最后再增加一句return true即可。详情可查看:http://stackoverflow.com/questions/23838497/coffeescript-referencing-dom-nodes-in-angular-expressions-is-disallowed

转载于:https://my.oschina.net/luozt/blog/512964

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值