简体   繁体   中英

AngularJS - Directive events and DOM rendering

In the code below, I am trying to use a template (with {{ value }} substitution) but I have been trying for two days to find a way of getting to the rendered code to set some properties on it.

I cannot use the style selector (which works fine), I need to use the div's id. In my real code I use a templateUrl: , not the inline code, but it has the same result.

Which event should I be listening for? At what point in time will the selectors work? I have read about compiling and linking and think the answer is out there somewhere?

Thanks in advance ...

<!doctype html>
<html lang="en" ng-app="myApp">
   <head>
      <meta charset="utf-8">
      <title>My AngularJS App</title>
      <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
   </head>
   <body style="height:100%; width: 100%;">
      <tls-window window-id="fred" window-width="600" window-label-width="150"></tls-window>
      <script src="lib/angular/angular.js"></script>
      <script src="js/app.js"></script>
      <script src="js/core/services.js"></script>
      <script src="js/core/controllers.js"></script>
      <script src="js/core/filters.js"></script>
      <script src="js/core/directives.js"></script>
      <script src="js/core/tlscore-directives.js"></script>
      <script type="text/javascript">

      var coreDirectives = angular.module('tlsCore.directives', []);
      coreDirectives.directive('tlsWindow', function() {
        return {
          restrict: 'E',
          scope: {
            windowId: '@windowId',
            windowWidth: '@windowWidth',
            labelWidth: '@windowLabelWidth'
          },
          replace: true,
          transclude: false,
          template: '<div id="{{windowId}}" class="tls-window" ng-cloak tls-draggable >' +
            '<div id="{{windowId}}-winBackground" class="tls-window-background" style="width: 300px; height: 200px" >' +
            '<div id="{{windowId}}-winToolbarBackground" class="tls-window-toolbar-background">' +
            '<div id="{{windowId}}-winToolbarContent" class="tls-window-toolbar-content" style="width: 300px; height: 100px">' +
            '</div>' +
            '</div>' +
            '</div>',
          link: function(scope, element, attrs) {
            var ele = element.get(element.index(scope.windowId));
            // this works and sets the colour (by class)
            $('.tls-window-toolbar-content').css("background-color", 'red');
            // this does not work as the id of the element is not yet substituted and rendered ??
            $('#fred-winBackground').css("background-color", 'green');
          }
        }
      });   

      </script>
   </body>
</html>

Option 1 - Better

Instead of using the template method, move the HTML into the link method. This enables us to manually $interpolate the bracketed terms before compiling, and then use the ID selector. Note that this would not be possible without using the = instead of @ isolate scope binding, because @ bindings are postponed until later and are undefined in the link method (See more on that here ).

app.directive('tlsWindow', function($interpolate,$compile) {
  return {
    restrict: 'E',
    scope: {
        windowId: '=',
        windowWidth: '=',
        labelWidth: '='
    },
    link: function (scope, element, attrs) {
        var template = '<div id="{{windowId}}" class="tls-window" ng-cloak tls-draggable >' +
        '<div id="{{windowId}}-winBackground" class="tls-window-background" style="width: 300px; height: 200px" >' +
            '<div id="{{windowId}}-winToolbarBackground" class="tls-window-toolbar-background">' +
                '<div id="{{windowId}}-winToolbarContent" class="tls-window-toolbar-content" style="width: 300px; height: 100px">' +
                '</div>' +
            '</div>' +
        '</div>';

        var interpolated = $interpolate(template)(scope);
        var html = $(interpolated);
        element.replaceWith($compile(html)(scope));
        
        $('.tls-window-toolbar-content').css('background-color', 'red');
        $('#fred-winBackground').css('background-color', 'green');
    }   
  }
}); 

Here is a fiddle


Option 2

Not the most robust solution but this would work also. Here the manipulation is postponed until after the rendering by using $timeout . This causes a slight delay as well.

$timeout(function(){
    $('#fred-winBackground').css("background-color", 'green');
},0);

This requires $timeout to be injected into the directive as well.

I see you have an answer about why the id isn't available at link time, but might I question the reason to use an id selector at all? Once you have your directive, you can get access to the top level div in your template via the children method of the jqLite/jquery element in the link function. See this example:

 angular.module('myApp', []) .directive('tlsWindow', function() { return { restrict: 'E', scope: { windowId: '@windowId', windowWidth: '@windowWidth', labelWidth: '@windowLabelWidth' }, replace: true, transclude: false, template: '<div id="{{windowId}}" class="tls-window" ng-cloak tls-draggable >' + '<div id="{{windowId}}-winBackground" class="tls-window-background" style="width: 300px; height: 200px" >' + '<div id="{{windowId}}-winToolbarBackground" class="tls-window-toolbar-background">' + '<div id="{{windowId}}-winToolbarContent" class="tls-window-toolbar-content" style="width: 300px; height: 100px">' + '</div>' + '</div>' + '</div>', link: function(scope, element, attrs) { // var ele = element.get(element.index(scope.windowId)); // this works and sets the colour (by class) // $('.tls-window-toolbar-content').css("background-color", 'red'); // this does not work as the id of the element is not yet substituted and rendered ?? // $('#fred-winBackground').css("background-color", 'green'); // Use the jqlite or jquery children method to locate the first child of your directive // element (from linkFn) is the element of tls-window and children() gets you access to the <div> container in your template element.children().css("background-color", "green"); } } });
 <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script> <div ng-app="myApp"> <tls-window window-id="fred" window-width="600" window-label-width="150"></tls-window> </div>

angularjs discourages referencing elements through id selectors. Make use of jqLite/jquery methods through the context of element that is passed to the link function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM