angular问题总结

已经用angular开发了大概有三个月了,开始两周很艰难,慢慢的也渐渐熟悉一些了。在此整理一下这段时间遇到的各种坑。

一、Angular怎样停止请求

(1)首先在需要请求的controller中注入$q服务,

(2)在scope变量中声明一个变量,var canceler;

(3)在事件最上方写上

//取消之前的请求
if (canceler) canceler.resolve();
canceler = $q.defer();

(4)在请求的位置添加一个延时

$http.jsonp(drivingUrl,{
    timeout:canceler.promise 
}).success(response);

二、ng-repeat的渲染问题

业务中有时需要在异步获取数据并用ng-repeat遍历渲染完页面后执行某个操作,angular本身并没有提供监听ng-repeat渲染完成的指令,所以需要自己动手写。在ng-repeat模板实例内部会暴露出一些特殊属性$index/$first/$middle/$last/$odd/$even,$index会随着每次遍历(从0开始)递增,当遍历到最后一个时,$last的值为true,所以通过判断$last的值来监听ng-repeat的执行状态,可以自定义指令

//ng-repeat-finish //解决ng-repeat时间有一个渲染时间,加载延迟问题
routerApp.directive("ngRepeatFinish", function() {
    return {
        restrict: "A",
        scope: {},        
        link: function($scope, element, attr){
            if($scope.$parent.$last){
                //要处理的函数
            }
        }
    }

});

参考博客http://www.tuicool.com/articles/Fb2um2e

三、Directive指令中的scope(摘自https://segmentfault.com/a/1190000002773689)

AngularJS为我们指令的scope参数提供了三种选择,分别是:false,true,{};默认情况下是false

1.scope = false

我们就新创建了一个作用域,只不过这个作用域是继承了我们的父作用域;我觉得可以这样理解,我们新创建的作用域是一个新的作用域,只不过在初始化的时候,用了父作用域的属性和方法去填充我们这个新的作用域。它和父作用域不是同一个作用域。

2.scope = true

我们创建的指令和父作用域(其实是同一个作用域)共享同一个model模型,所以在指令中修改模型数据,它会反映到父作用域的模型中。

3.scope = {}

当我们将scope设置为{}时,意味着我们创建的一个新的与父作用域隔离的新的作用域,这使我们在不知道外部环境的情况下,就可以正常工作,不依赖外部环境。

我们使用了隔离的作用域,不代表我们不可以使用父作用域的属性和方法。

  1. 我们可以通过向scope{}中传入特殊的前缀标识符(即prefix),来进行数据的绑定。
  2. 在创建了隔离的作用域,我们可以通过@,&,=引用应用指令的元素的属性,如上面的代码那样,我们可以在<div class="my-directive" my-directive my-name="{{name}}" age="age" change-my-age="changeAge()"></div>这个元素中,利用前缀标识符通过使用属性my-name,age,change-my-age来引用这些属性的值。

下面我们来看看如何使用这些前缀标识符:

@

这是一个单项绑定的前缀标识符

使用方法:在元素中使用属性,好比这样<div my-directive my-name="{{name}}"></div>,注意,属性的名字要用-将两个单词连接,因为是数据的单项绑定所以要通过使用{{}}来绑定数据。

=

这是一个双向数据绑定前缀标识符

使用方法:在元素中使用属性,好比这样<div my-directive age="age"></div>,注意,数据的双向绑定要通过=前缀标识符实现,所以不可以使用{{}}

&

这是一个绑定函数方法的前缀标识符

使用方法:在元素中使用属性,好比这样<div my-directive change-my-age="changeAge()"></div>,注意,属性的名字要用-将多个个单词连接。

注意:在新创建指令的作用域对象中,使用属性的名字进行绑定时,要使用驼峰命名标准,比如下面的代码。

进一步说明,我们的指令是如何利用这些前缀标识符来寻找我们想要的属性或者函数的?

  • @ 当指令编译到模板的name时,就会到scope中寻找是否含有name的键值对,如果存在,就像上面那样,看到@就知道这是一个单向的数据绑定,然后寻找原来的那个使用指令的元素上(或者是指令元素本身)含有这个值的属性即my-name={{name}},然后在父作用域查找{{name}}的值,得到之后传递给模板中的name
  • =&@差不多,只不过=进行的是双向的数据绑定,不论模板还是父作用域上的属性的值发生改变都会使另一个值发生改变,而&是绑定函数而已。

原博主微博还有3个例子,很生动有助于理解。看完博主的文章,结合之前的开发经验有一种顿悟的感觉。

四、Filter

Angular自带的内置 filter有九种:

1) date(日期)

2) currency(货币)

3) limitTo(限制数组或字符串长度)

4) orderBy(排序)

5) lowercase(小写)

6) uppercase(大写)

7) number(格式化数字,加上千位分隔符,并接收参数限定小数点位数)

8) filter(处理一个数组,过滤出含有某个子串的元素)

9) json(格式化 json 对象)

1.filter 有两种使用方法,一种是直接在页面里:

<p>{{now | date : \'yyyy-MM-dd\'}}</p>

2.另一种是在 js 里面用:

// $filter(\'过滤器名称\')(需要过滤的对象, 参数1, 参数2,...)

$filter(\'date\')(now, \'yyyy-MM-dd hh:mm:ss\');

自定义 filter

// 形式

app.filter(\'过滤器名称\',function(){

    return function(需要过滤的对象,过滤器参数1,过滤器参数2,...){

        //...做一些事情  

        return 处理后的对象;

    }

});  

// 一个高亮的例子

routerApp.filter("highlight", function($sce, $log){

var fn = function(text, search){

    if (!search) {

        return $sce.trustAsHtml(text);

    }

    var regex = new RegExp(search, \'gi\');

    var result = text;   

    try{

     result = text.replace(regex, \'<span class="highlightedText red">$&</span>\');

}catch(e){

// 输入了特殊字符

}

    return $sce.trustAsHtml(result);

};

return fn;

}); 

五、factory、service 和 provider 关系

factory

把 service 的方法和数据放在一个对象里,并返回这个对象

app.factory(\'FooService\', function(){

    return {

        target: \'factory\',

        sayHello: function(){

            return \'hello \' + this.target;

        }

    }

}); 
service

通过构造函数方式创建 service,返回一个实例化对象

app.service(\'FooService\', function(){

    var self = this;

    this.target = \'service\';

    this.sayHello = function(){

        return \'hello \' + self.target;

    }

});
provider

创建一个可通过 config 配置的 service,$get 中返回的,就是用 factory 创建 service 的内容

app.provider(\'FooService\', function(){

    this.configData = \'init data\';

    this.setConfigData = function(data){

        if(data){

            this.configData = data;

        }

    }

    this.$get = function(){

        var self = this;

        return {

            target: \'provider\',

            sayHello: function(){

                return self.configData + \' hello \' + this.target;

            }

        }

    }

});

// 此处注入的是 FooService 的 provider

app.config(function(FooServiceProvider){

FooServiceProvider.setConfigData(\'config data\');

});

从底层实现上来看,service 调用了 factory,返回其实例;factory 调用了 provider,返回其 $get 中定义的内容。factory 和 service 功能类似,只不过 factory 是普通 function,可以返回任何东西(return 的都可以被访问,所以那些私有变量怎么写,你懂的);service 是构造器,可以不返回(绑定到 this 的都可以被访问);provider 是加强版 factory,返回一个可配置的 factory。

六、控制器之间的通信

在angularjs中提供了emit,broadcast和$on服务用于控制器之间基础事件的传递交流。

  1. $emit

    该服务贯穿作用域发出一个向上的事件,并通知哪些注册在rootScope.Scope上的监听器。该事件的生命周期开始于emit被启动的地方,事件一直朝着根作用域传递,传递期间并通知哪些注册在作用域上的监听器,如果这期间一个监听器接受到了这个事件,会注销这个事件,那么事件将会停止向上继续传播。

  2. $broadcast

    该服务发布一个向下的事件给作用域中的所有子节点以及以下的节点,并通知注册在

    rootScope.Scope上的监听器,该事件的生命周期也是从broadcast被启动开始。下面的所有作用域都会接收到通知。之后,事件向下传播,在这期间,作用域中的监听器接收到通知,获取事件,但是不会注销事件,事件继续往下传播。

  3. $on

    该服务监听指定类型的事件,获取从emit或者broadcast发布的事件。

    【提示】:

如果在作用域中没有父子关系存在,可以在控制器中注入$rootScope、使用$broadcast服务向下传播事件,但是不能通过$emit向上传播事件。在作用域中存在父子关系时,可以也仅可以由子控制器使用$emit服务向上传播事件,同时父作用域中的控制监听器可以注销事件。