Get original transcluded content within angular directive

10,551

The $element.innerHTML should contain the original HTML. I am showing that it contains

  <div class="editable">
  <span class="glyphicon glyphicon-edit" ng-click="toggleEditor()"></span>

    <div class="editable-input" ng-show="showEditor">
       <b><p>Enter well-formed HTML content:</p></b>
       <p>E.g.<code>&lt;h1&gt;Hello&lt;/h1&gt;&lt;p&gt;some text&lt;/p&gt;&lt;clock&gt;&lt;/clock&gt;</code></p>
       <textarea ng-model="editContent"></textarea>
       <button class="btn btn-primary" ng-click="onEdit()">apply</button>
    </div>

    <div class="editable-output" ng-transclude=""></div>
  </div>
Share:
10,551

Related videos on Youtube

prototype
Author by

prototype

Updated on February 20, 2020

Comments

  • prototype
    prototype over 4 years

    My goal is to create an editable directive that allows a user to edit HTML of any element to which the attribute is attached (see Plunker: http://plnkr.co/edit/nIrr9Lu0PZN2PdnhQOC6)

    This almost works except I can't get the original raw HTML of the transcluded content to initialize the text area. I can get the text of it from clone.text(), but that's missing the HTML tags like <H1>, <div>, etc. so clicking apply with no edits is not idempotent.

    The method clone.html() throws an error, Cannot read property 'childNodes' of undefined

    app.directive("editable", function($rootScope) {
      return {
        restrict: "A",
        templateUrl: "mytemplate.html",
        transclude: true,
        scope: {
          content: "=editContent"
        },
    
        controller: function($scope, $element, $compile, $transclude, $sce) {
    
          // Initialize the text area with the original transcluded HTML...
          $transclude(function(clone, scope) {
    
            // This almost works but strips out tags like <h1>, <div>, etc.
            // $scope.editContent = clone.text().trim();
    
            // this works much better per @Emmentaler, tho contains expanded HTML
            var html = ""; 
            for (var i=0; i<clone.length; i++) {
                html += clone[i].outerHTML||'';}
            });
            $scope.editContent = html;
    
          $scope.onEdit = function() {
            // HACK? Using jQuery to place compiled content 
            $(".editable-output",$element).html(
              // compiling is necessary to render nested directives
              $compile($scope.editContent)($rootScope)
            );
          }
    
          $scope.showEditor = false;
    
          $scope.toggleEditor = function() {
            $scope.showEditor = !$scope.showEditor;
          }         
        }
      }
    });
    

    (This question is essentially a wholesale rewrite of the question and code after an earlier attempt to frame the question, Get original transcluded content in Angular directive)

    • Nathaniel Johnson
      Nathaniel Johnson over 10 years
      clone is a collection of elements. Have you been able to inspect it in a debugger?
    • prototype
      prototype over 10 years
      Aha! Iterating over them and appending outerHTML is much closer: var text = ""; for (var i=0; i<clone.length; i++) {text += clone[i].outerHTML||'';}. However, one remaining gap is the HTML of the nested directives is expanded, e.g. instead of just <clock></clock> it shows <clock><span class="clock"><div class="btn btn-success"><h1>Clock</h1><p>{{time}}</p></div></span></clo‌​ck>. In this example, the clock doesn't transclude content, so the net effect is the same. I wonder if it's possible to get the original HTML?
    • Nathaniel Johnson
      Nathaniel Johnson over 10 years
      I suspected it might be the case. Good deal. The original HTML might be sitting in the $element object in the outer scope. Transclusion is not my strong suit.
    • charlietfl
      charlietfl over 10 years
      why aren't you wrapping original content in an element with a class that you can use to find it and keep it isolated in one container?
    • charlietfl
      charlietfl over 10 years
      if it was me would just add textarea/editor on demand like with doubleclcik. Append when needed.
    • manikanta
      manikanta about 10 years
      @user645715 Did you able to figure how to get the 'original transcluded markup'?
    • prototype
      prototype about 10 years
      Sorry, no, despite a fair effort. I then implemented the properties editor using JSON, which mapped directly to jQuery "data-" properties. (And because the project had lots of nested views, ended up porting it to Backbone.js for simple control over how/when it was rendered)
  • prototype
    prototype over 10 years
    Very helpful. That contains the template text. Your answer did spark me to add the method compile(element, attrs) which has the transcluded HTML in its innerHTML. But it's after Angular expanded it into new DOM elements using the template. That'll break if any sub-elements have replace:true or transclude content themselves. I suspect there's no way to get Angular to give the HTML from before Angular processed it, and thus the starting content would need to be bootstrapped not via transclusion but passed say via an attribute or ajax.
  • Nathaniel Johnson
    Nathaniel Johnson over 10 years
    This gets into the guts of the digest cycle. By setting the priority arbitrarily high, you might be able to ensure that it is evaluated first.
  • pery mimon
    pery mimon almost 9 years
    How to get the uncompiled transclude content of an Angular directive if I using replace:true with template. it's mean element not contain the raw orginal content beacuse elementis template element not orginal element