AngularJS 在指令中操作父级的 DOM

Posted

技术标签:

【中文标题】AngularJS 在指令中操作父级的 DOM【英文标题】:AngularJS manipulate parent's DOM in directive 【发布时间】:2016-09-02 02:20:07 【问题描述】:

我是 AngularJS 的新手。我已经搜索了一些关于如何在指令中操作 DOM 的页面,但其中大多数都是在操作指令本身的 DOM。我的问题是在指令之外操作 DOM。

我的项目中有一个谷歌地图,地图上有一些标记来显示事件。我想要做的是,当单击标记时,应用程序应该在滑动条中显示事件的详细信息。根据下面的代码,要求是当我单击指令中触发 updateMarkers() 的标记时,它应该更改 html 中 detail 的值。我现在正在做的是尝试使用“=”来指令访问其控制器范围内的值,但这种方式不起作用。有知道的请帮帮忙,谢谢。

HTML:

<div id="wrapper" >
    <div class="container-fluid">
        <div class="row">
            <div class="col-lg-12">                  
                <app-map id="map" center="loc" markers="cities" details="details" show-detail="getDetails(directivedata)" on-init="gotoCurrentLocation(directiveData)"> </app-map>                  
            </div>
            <div>**********details</div>
        </div>
    </div>
</div>

部分控制器:

app.controller("appCtrl", function ($scope, $http) 
    $scope.nearbyEvent;
    $scope.details = "something";   //should be changed when click markers in map
    //code.....

指令:

app.directive("appMap", ['$http', function ($http) 
    return 
        restrict: "E",
        replace: true,
        template: "<div></div>",
        scope: 
            center: "=",        // Center point on the map (e.g. <code> latitude: 10, longitude: 10 </code>).
            markers: "=",       // Array of map markers (e.g. <code>[ lat: 10, lon: 10, name: "hello" ]</code>).
            details: "=",       //used for resend a REST api and store data in here
            width: "@",         // Map width in pixels.
            height: "@",        // Map height in pixels.
            zoom: "@",          // Zoom level (one is totally zoomed out, 25 is very much zoomed in).
            mapTypeId: "@",     // Type of tile to show on the map (roadmap, satellite, hybrid, terrain).
            panControl: "@",    // Whether to show a pan control on the map.
            zoomControl: "@",   // Whether to show a zoom control on the map.
            scaleControl: "@",   // Whether to show scale control on the map.
            onInit: "&",
            showDetail: "&"
        ,
        link: function (scope, element, attrs) 
        //some code.....
             function updateMarkers() 
                if (map && scope.markers.length > 0) 
                    window.alert("get me");
                    // clear old markers
                    if (currentMarkers != null) 
                        for (var i = 0; i < currentMarkers.length; i++) 

                            currentMarkers.setMap(null);
                            window.alert("get here1*****");
                        
                                              
                    // create new markers
                    currentMarkers = [];
                    var markers = scope.markers; 

                    function makeHappen(thi)
                        return function()
                            scope.details = thi;
                            window.alert("***" + "in directive    " + thi); 
                        
                    

                    for (var i = 1; i < markers.length-1; i++)                     
                        var m = markers[i];
                        if(m.venue == null || m.venue.lat == 0.0)
                            continue;
                        var loc = new google.maps.LatLng(m.venue.lat, m.venue.lon);
                        var eventName = m.description;  
                        var mm = new google.maps.Marker( position: loc, map: map, title: m.name, id: m.id, count: i);                                                 
                        mm.addListener('click', makeHappen(eventName), false);                     
                        currentMarkers.push(mm);

                    
                
            

【问题讨论】:

是细节还是细节?我可以看到它们都被使用了。 详情,抱歉。我不知道为什么这里的编辑是错误的。但在我的代码中,它在 HTML 和 JS 部分都被称为“细节”。我已经在这里改了。谢谢 要更改由details 属性定义的父范围变量的值,只需将其设置为scope.details = 'new value'; 【参考方案1】:

使用. 表示法正确启用双向数据绑定。 details 是控制器中的原始类型。尽管指令中的隔离作用域可以访问details,但当您在makeHappen 函数中为details 分配某些内容时,它会在隔离作用域中创建一个新的details

在这种情况下,不要在控制器中使用原始类型 $scope.details,而是将其设为对象,例如:$scope.details = msg: 'something' 。在模板中打印,即:details.msg。在隔离范围的makeHappen 函数中将details 更改为details.msg

更新

对不起我的错误。看起来,您不需要将 scope.details 从字符串更改为对象。以下更改足以使其正常工作。

function makeHappen(thi)
    return function()
        scope.$apply(function() 
            scope.details = thi;  
        )
    

对于隔离作用域,每当在指令作用域中更改共享变量(控制器作用域 var 与指令作用域共享)时,控制器中的变量也会更新。它是原始的还是对象的并不重要,因为指令范围和控制器范围之间没有原型继承。但是,如果指令的范围属性是 true(scope: true) 而不是隔离范围,那么 scope.details 必须是一个对象才能看到控制器范围内的更改值(我之前解释过) .

所需的唯一更改是将scope.details = thi 包装在scope.$apply(function()//code) 中,或者在makeHappen 返回函数的末尾调用scope.$apply()

那么,问题是,为什么需要调用scope.$apply() 或将某些代码放入scope.$apply(function() //code) 中?原因是:

$apply() 用于从外部以角度执行表达式 角度框架。 (例如来自浏览器 DOM 事件, setTimeout、XHR 或第三方库)。

在您的情况下,您附加了一个事件侦听器mm.addListener('click', makeHappen(eventName), false);。每当单击标记时,都会调用 makeHappen 函数。它发生在角度上下文之外。 Angular 不知道 scope.details 值已更改。因此,scope.details 的观察者不会被调用,因此您看不到任何更新。而且,解决方案scope.$apply() 来了。每当您调用 scope.$apply() 时,它都会在内部调用 scope.$digest(),这实际上会更新任何绑定或观察者。

那么,您什么时候需要致电scope.$apply()?在幕后,Angular 将您在 Angular 上下文中编写的几乎所有代码都包装在 $apply() 中。像ng-click$timeout$http 这样的事件回调被包装在scope.$apply() 中,因此您无需显式调用它。如果你自己调用 $apply,你会得到错误。

有关 $apply 的更多信息,您可以访问以下链接: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply http://jimhoskins.com/2012/12/17/angularjs-and-apply.html

我为您做了一个简短的说明,以便您更好地理解指令中的不同范围场景以及为什么在某些情况下需要$apply。请看看这个笨蛋。 http://plnkr.co/edit/96a646T6FuBhmjdLgJXq?p=preview

【讨论】:

您好,感谢您的帮助。将其作为对象起作用。换句话说,我在 makeHappen 函数中更改 details.msg 后添加了 scope.$apply()。。无论如何,感谢您的帮助,期待更多信息/链接。【参考方案2】:

我认为这与此处报告的问题类似: How to access parent scope from within a custom directive *with own scope* in AngularJS?

希望对你有帮助!

【讨论】:

感谢您的回复,您给我的链接是关于在指令中使用控制器中的属性,但我想做的是更改指令中控制器中的属性值。请问有没有其他的链接,谢谢 其实之前链接里的方法我都试过了。虽然我可以在指令中访问控制器的属性,但我无法更改它的值。就像我没有 $apply() 一样。

以上是关于AngularJS 在指令中操作父级的 DOM的主要内容,如果未能解决你的问题,请参考以下文章

在 AngularJS 组件中从父级到子级通信事件

附加到元素父级的模板上的隔离范围

如何在 React 中处理父级的 mouseMove 事件?

在父级的 ngOnDestroy 之后未调用 Angular ngOnChanges

javascript父级鼠标移入移出事件中的子集影响父级的处理方法

带有父级的 QSqlQueryModel - 应用程序崩溃