0

I have a problem where I retrieve a string with elements in them, I need to rewrite them a elements. The values I need are in attributes of the element. e.g.

<div>The way to find your answer is to go to <span rel="webResource" resource="http://www.stackflow.com">Stack Flows Home</span> webpage.</div>

I had done it in JQuery with a technique like:

jQuery.fn.makeA = function() {
    var attrs = {};
    $.each(this[0].attributes, function(idx, attr) {
         attrs[attr.nodeName] = attr.value;
    }
    this.replaceWith(function() {
          var jqThis = $(this);
          return $(document.createElement("a"))
                      .attr("href", encodeURI(jqThis.attr("resource")))
                      .attr(attrs)
                      .append(jqThis.contents());
    }); 
}

and then doing this to the string:

var element = $(spanText);
$("span[rel='webResource']",element).makeA();
return (element[0].outerHTML);

Since I have had to create a Custom Directive in AngularJS for this, is there a good way while in the directive code to implement the same technique without JQuery?

Well, some progress.. I created a customer directive for handling the insertion of the original string: .directive('displayString'...) and then after reading this: [http://blog.timsommer.be/using-compile-to-compile-html-strings-in-angular/], I recognize that doing an element.html(databaseString) will render correctly in the browser, but I needed to use $compile to get the string to be fully "jammed" into the DOM. Once I added the $compile, like Tim Sommer suggested!, the .directive('rel'...) is now firing! But, when the .directive gets the compiled the element value is now "empty", e.g.: <span rel="webResource" resource="http://www.stackflow.com">Stack Flows Home</span> is now <span rel="webResource" resource="http://www.stackflow.com"></span> Interestingly, the $compile actually triggers the 'rel' directive immediately and it runs before the next statement in the 'displayString' executes.

Any idea why the value/text of the has gone missing? So, progress, but still stuck!

  • Would your directive be on the `span` or on the `div`? – maged Jun 18 '15 at 08:45
  • `span`. Actually, I have a version that was `.directive('rel', [function(){...})`, hanging a custom directive on a standard attribute seemed like a bad idea. – Tom-WK Weiss Jun 19 '15 at 17:21
  • Well, some progress... Reading this: [http://blog.timsommer.be/using-compile-to-compile-html-strings-in-angular/}, I recognize that doing an element.html(databaseString) will render correctly in the browser, I needed to use $compile to get the string to be fully "jammed" into the DOM. Once I added the $compile, like Tim Sommer suggested!, the .directive('rel'...) is now firing! – Tom-WK Weiss Jun 21 '15 at 20:33
  • [ng-transclude](http://stackoverflow.com/a/24725520/1795795) might be what you're looking for – maged Jun 22 '15 at 08:42

1 Answers1

0

It takes two directives.

  1. $compile in the first directive to deal with the retrieve HTML from the database and also to trigger the pushing of the HTML elements in the answer text into the DOM…
  2. transclude:yes AND the use of template with the ng-transclude directive and use of {&} in the scope variable, so Angular knows to hold onto the contents of the fact string’s embedded s (and Angular does NOT like the ng-transclude directive in the strings passed to $compile!) and replace the value of the href from the specific context.

Putting a directive on @rel is very dangerous, because it’s a standard RDFa property and is used with and HTML instructions. However, with the guard conditionals it’s ok.

app.$module.directive('displayFact', ['$compile', function($compile){
   return {
      restrict: 'E', 
      scope: { fact : '@'},
      link: function (scope, element, attrs) {
            var templateInASense = $compile('<span>' + scope.fact + '</span>')(scope);  // this triggers the 'rel' directive below, <span> is required to allow $compile to deal with it..
            element.replaceWith(templateInASense);
          }
}}]);

and directive 2:

app.common.$module.directive('rel', [function(){
 return {
    restrict: 'A', 
    scope : { reference : '&'},
    transclude: true,
    template: '<span><a href="{{reference}}" target="_blank"><span ng-transclude /></a></span>', 
    link: function (scope, element, attrs) {
            if (typeof attrs.resource !== "undefined") {
                   scope.reference= attrs.resource; 
            }
          }
}}]);

The snippet of the Template HTML looks like this:

<td ng-repeat="fact in list"><display-fact fact={{fact.label}}></display-fact></td>

That seems to work pretty well. It handles multiple s in a single string. And it seems pretty quick.