0

I'm working with an interesting API that returns each element of a resource in it's own object. I'm passing the data from an ng-repeat into a directive which I need to then return the completed HTML string. Brand new to lodash and having a bit of trouble figuring this one out. There are quite a few possibilities of segment types that I'll need to end up having cases for. View API Docs

  • Link
  • Mention
  • Hashtag
  • MarkupBegin
  • MarkupEnd
  • etc

NG Directive

angular.module('Community.directives.feedBody', [])
  .directive('feedBody', function feedBodyDirective() {
    return {
      restrict: 'E',
      link: convertSegments
    };

    function convertSegments($scope, elem, attrs) {
      var content = attrs.content;
    }

  }
);

JSON

[
    {
        "htmlTag": "p",
        "markupType": "Paragraph",
        "text": "",
        "type": "MarkupBegin"
    },
    {
        "text": "This is a ",
        "type": "Text"
    },
    {
        "htmlTag": "b",
        "markupType": "Bold",
        "text": "",
        "type": "MarkupBegin"
    },
    {
        "text": "post",
        "type": "Text"
    },
    {
        "htmlTag": "b",
        "markupType": "Bold",
        "text": "",
        "type": "MarkupEnd"
    },
    {
        "text": " from the standard ",
        "type": "Text"
    },
    {
        "htmlTag": "i",
        "markupType": "Italic",
        "text": "",
        "type": "MarkupBegin"
    },
    {
        "text": "chatter",
        "type": "Text"
    },
    {
        "htmlTag": "i",
        "markupType": "Italic",
        "text": "",
        "type": "MarkupEnd"
    },
    {
        "text": " UI with some HTML tags",
        "type": "Text"
    },
    {
        "htmlTag": "p",
        "markupType": "Paragraph",
        "text": "\n",
        "type": "MarkupEnd"
    }
]

UI Router Template

<feed-body content="{{comment.body.messageSegments}}"></feed-body>

Should Return

<p><b>post</b> from the standard <i>chatter</i> ui with some HTML tags</p>

1 Answer 1

1

You don't necessarily need lodash to build the html string. You can just use a for loop and accomplish the same:

EDIT: I created a plunker that demonstrates the behavior I think you're going for. Assuming you're passing the json data as the ``, you can set up an isolated scope in the directive that derives the content value from the controller (note that I removed the curly braces...):

<feed-body content="comment.body.messageSegments"></feed-body>

in your controller:

  var json = [
       ...
  ];

  $scope.comment = {
      body: {
          messageSegments: json
      }
  };

and in your directive:

return {
      restrict: 'E',
      scope: {
        content: '='
      },
      link: convertSegments
  };

  function convertSegments(scope, elem, attrs) {
      var content = scope.content;

      function concatenateJson(jsonData) {
          var html = []
          for (var i = 0; i < jsonData.length; i++) {
               if (jsonData[i].type === 'MarkupBegin') {
                  html.push('<' + jsonData[i].htmlTag + '>');
              } else if (jsonData[i].type === 'MarkupEnd') {
                  html.push('</' + jsonData[i].htmlTag + '>');
              } else if (jsonData[i].type === 'Text') {
                  html.push(jsonData[i].text);
              }
          }
          return html.join('');
      }

      var elemHtml = concatenateJson(content);

      elem.html(elemHtml);
  }

  // FYI: running the lodash method 100,000 times took 719 and 552 msecs, respectively
  // running the native for loop instead 100,000 times took 528 and 565 msecs
  // not a large sample size, obv, but not a huge difference between the two one way or the other

  // Using the lodash _.each method would just look like:
  // function concatenateJson(jsonData) {
  //     var html = [];
  //     _.each(jsonData, function(datum) {
  //         if (datum.type === 'MarkupBegin') {
  //             html.push('<' + datum.htmlTag + '>');
  //         } else if (datum.type === 'MarkupEnd') {
  //             html.push('</' + datum.htmlTag + '>');
  //         } else if (datum.type === 'Text') {
  //             html.push(datum.text);
  //         }
  //     });
  //     return html.join('');
  // }
Sign up to request clarification or add additional context in comments.

5 Comments

Pretty new to this, will this be as efficient? It was pretty strongly recommended that I use lodash
I really doubt you'll notice any significant difference in performance one way or the other, but someone with more experience than me may want to chime in. I've read that there's little advantage (and sometimes mild disadvantage) to foregoing the native JS for loop in favor of library functions. A standard for loop would probably perform better than the native .forEach method, but unless you're processing an immense amount of data, the performance difference is probably negligible. Nonetheless, I'll update the answer with the standard for loop.
I added in the lodash version of the function as well, and just for curiosity's sake, I ran each version twice in loops of 100,000 iterations. The difference in performance was slight, and if anything, the for loop seemed a little faster. But small sample sizes and your browser may vary...
You didn't show your controller, but if the comment.body.messageSegments you pass into the html directive is the json array set on the controller's scope, then you can set up the directive to have scope.content inherit from the controller, remove the curly braces from the content attribute value, grab it in the directive's link function as you're already doing with var content = scope.content;, then set var html = concatenateJson(content);, and finally insert the html with elem.html(html); Here's a plunker...
Yes, finally got it working just a minute after I had already posted the last comment. This was extremely helpful, and the comparison between the two options was great. Thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.