angular的事件

在这里插入图片描述
这张图中有两个视图块A和B,它们分别又有两个子视图。这时候,如果子视图A1想要发出一个业务事件,使得B1和B2能够得到通知,过程就会是:

  • 沿着父作用域一路往上到达双方共同的祖先作用域
  • 从祖先作用域一级一级往下进行广播,直到到达需要的地方
    简化一下:
    在这里插入图片描述
    对于这种事件的传播方式,举个例子,1班班长想要给2班班长发个通知,他的通知方向是一级一级向上汇报,直到双方共同的老师,然后再沿着路线向下去通知。
  • 从作用域往上发送事件,使用scope.$emit
$scope.$emit("someEvent", {});
  • 从作用域往下发送事件,使用scope.$broadcast
$scope.$broadcast("someEvent", {});

这两个方法的第二个参数是要随事件带出的数据。
注意,这两种方式传播事件,事件的发送方自己也会收到一份。

事件的接收与阻止:
无论是$emit还是$broadcast发送的事件,都可以被接收,接收这两种事件的方式是一样的:

$scope.$on("someEvent", function(e) {
    // 这里从e上可以取到发送过来的数据
});

注意,事件被接收了,并不代表它就中止了,它仍然会沿着原来的方向继续传播,也就是:

  • $emit的事件将继续向上传播
  • $broadcast的事件将继续向下传播
    有时候,我们希望某一级收到事件之后,就让它停下来,不再传播,可以把事件中止。这时候,两种事件的区别就体现出来了,只有$emit发出的事件是可以被中止的,$broadcast发出的不可以。

如果想要阻止$emit事件的继续传播,可以调用事件对象的stopPropagation()方法。

$scope.$on("someEvent", function(e) {
    e.stopPropagation();
});

首先,调用事件对象的preventDefault()方法,然后,在收取这个事件对象的时候,判断它的defaultPrevented属性,如果为true,就忽略此事件。这个过程比较麻烦,其实我们一般是不需要管的,只要不监听对应的事件就可以了。在实际使用过程中,也应当尽量少使用事件的广播,尤其是从较高的层级进行广播。

上级作用域

$scope.$on("someEvent", function(e) {
    e.preventDefault();
});

下级作用域

$scope.$on("someEvent", function(e) {
    if (e.defaultPrevented) {
        return;
    }
});

事件总线
在Angular中,不同层级作用域之间的数据通信有多种方式,可以通过原型继承的一些特征,也可以收发事件,还可以使用服务来构造单例对象进行通信。
前面提到的这个军队的例子,有些时候沟通效率比较低,特别是层级多的时候。想象一下,刚才这个只有三层,如果更复杂,一个排长的消息都一定要报告到军长那边再下发到其他基层主官,必定贻误军情,更何况有很多下级根本不需要知道这个消息。
那怎么办呢,难道是直接打电话沟通吗?这个效率高是高,就是容易乱,这也就相当于界面块之间的直接通过id调用。
Angular的作用域树类似于传统的组织架构树,一个大型企业,一般都会有若干层级,近年来有很多管理的方法论,比如说组织架构的扁平化。
我们能不能这样:搞一个专门负责通讯的机构,大家的消息都发给它,然后由它发给相关人员,其他人员在理念上都是平级关系。
这就是一个很典型的订阅发布模式,接收方在这里订阅消息,发布方在这里发布消息。这个过程可以用这样的图形来表示:
在这里插入图片描述

app.factory("EventBus", function() {
    var eventMap = {};

    var EventBus = {
        on : function(eventType, handler) {
            //multiple event listener
            if (!eventMap[eventType]) {
                eventMap[eventType] = [];
            }
            eventMap[eventType].push(handler);
        },
        off : function(eventType, handler) {
            for (var i = 0; i < eventMap[eventType].length; i++) {
                if (eventMap[eventType][i] === handler) {
                    eventMap[eventType].splice(i, 1);
                    break;
                }
            }
        },
        fire : function(event) {
            var eventType = event.type;
            if (eventMap && eventMap[eventType]) {
                for (var i = 0; i < eventMap[eventType].length; i++) {
                    eventMap[eventType][i](event);
                }
            }
        }
    };
    return EventBus;
});

事件订阅代码:

EventBus.on("someEvent", function(event) {
    // 这里处理事件
    var c = event.data.a + event.data.b;
});

事件发布代码:

EventBus.fire({
    type: "someEvent",
    data: {
        aaa: 1,
        bbb: 2
    }
});

注意,如果在复杂的应用中使用事件总线,需要慎重规划事件名,推荐使用业务路径,比如:“portal.menu.selectedMenuChange”,以避免事件冲突。

发布了53 篇原创文章 · 获赞 33 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/weixin_42966484/article/details/87989087