1

I've never seen an API do this before, but I'm working with what I've got. This is part of the response body from the API I'm dealing with

"body": {
  "isRichText":true,
  "messageSegments":[
    {
      "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"
    }
  ]
}

I need to combine each one of those segments in order to create what will end up being one element inside of a paragraph tag(in this case).

HTML

<div ng-repeat="bodyElement in post.body.messageSegments">
  <!-- ng-if maybe? -->{{bodyElement.htmlTag}} {{bodyElement.text}}
</div>

What is the best way to complete this? Is it directly in the js files, or should I attempt in the templates?

14
  • That html all needs to be created manually outside of the view. API was obviously set up to be run through a templating script. Going to be a lot of work if you can't find the library used to parse it to html. Is it public API with reference docs? Commented Jan 28, 2016 at 3:44
  • @charlietfl you like to follow me around don't you :) Thank you. Documentation is public, but you won't be able to hit the end point. It's the Salesforce Chatter API developer.salesforce.com/docs/atlas.en-us.198.0.chatterapi.meta/… Commented Jan 28, 2016 at 3:47
  • Have to assume they have SDK's to parse that to html. I've done stuff like this from scratch myself and if you have to cover all tags it's a ton of work...especially if you will be including form tags ... and will take a while to get form controls sorted out and tested Commented Jan 28, 2016 at 3:51
  • @charlietfl could I do something generic like each.messageSegment(if htmlTag then '<' + htmlTag + '>' else text)? This is my first angular app so I'm not sure what the best way to handle this would be. The $http request is in a factory Commented Jan 28, 2016 at 3:54
  • Here's a proof of concept I played with years ago .... give you an idea jsfiddle.net/charlietfl/gsTrM/9 Dt structure is slightly different but concepts might help you. angular can't do much with this until you can create elements to insert in dom Commented Jan 28, 2016 at 4:01

2 Answers 2

1

My suggestion would be to create a service to parse the message segments, and a directive to display the results.

Here is a working example: JSFiddle

Service

The service $messageSegment has two methods: parse and parseHttpResponse. The latter can be used in the transformResponse config option of an $http request.

angular.module('myApp')
  .factory('$messageSegment', messageSegmentFactory);

function messageSegmentFactory() {
  var $messageSegment = {};

  $messageSegment.parse = function(arr) {
    var html = '';

    if (!angular.isArray(arr) || !arr.length)
      return html;

    do {
      var segment = arr.shift();
      switch (segment.type) {
        case 'Link':
          html += '<a href="' + segment.url + '">' + segment.text + '</a>';
          break;
        case 'Mention':
          html += '<a href="/users/' + segment.user.id + '">' + segment.text + '</a>';
          break;
        case 'Hashtag':
          html += '<a class="hashtag">' + segment.text + '</a>';
          break;
        case 'MarkupBegin':
          html += '<' + segment.htmlTag + '>';
          break;
        case 'MarkupEnd':
          html += '</' + segment.htmlTag + '>';
          break;
        default:
          html += segment.text;
      }
    } while (arr.length);

    return html;
  };

  $messageSegment.parseHttpResponse = function(data) {
    return $messageSegment.parse(data.body.messageSegments);
  };

  return $messageSegment;
}

Directive

This sfChatter directive observes its url attribute and, whenever that value changes, will make an $http request, parse the response and update its own inner HTML automatically.

angular.module('myApp')
  .directive('sfChatter', sfChatterDirective);

sfChatterDirective.$inject = ['$http', '$messageSegment'];
function sfChatterDirective($http, $messageSegment) {
  return {
    restrict: 'E',
    link: postLink
  };

  function postLink(scope, iElement, iAttrs) {
    iAttrs.$observe('url', function(value) {
      var url = scope.$eval(value);
      $http({
        url: url,
        method: 'GET',
        transformResponse: $messageSegment.parseHttpResponse
      }).then(function(res) {
        iElement.html(res.data);
      });
    });
  }
}

Usage

In your app, you would do something like <sf-chatter url="myUrl">, where myUrl is a scope variable that tells the directive what $http endpoint to hit.

Sign up to request clarification or add additional context in comments.

3 Comments

Wow, this is awesome! As I've never used transformResponse before, would you be able to provide an example of what that would look like? It seems there are a couple options inside of it from the documentation I've just read through.
Thanks! The 'sfChatter' directive in my answer uses transformResponse.
Hah! Totally missed that. Epic. Thank you Shaun
0

Here is a little bit of angular magic for you. The controller code (you'll need to inject $sce):

var markup = ''
for(var i = 0; i < $scope.messageSegments.length; i++){
  // console.log(markup)
  if($scope.messageSegments[i].type === 'MarkupBegin'){
    markup = markup + '<'+$scope.messageSegments[i].htmlTag+'>'
  }
  else if($scope.messageSegments[i].type === 'MarkupEnd'){
    markup  = markup + '</'+$scope.messageSegments[i].htmlTag+'>'
  }
  else{
    markup  = markup + $scope.messageSegments[i].text
  }  
}
$scope.markup = markup;
$scope.markup = 
 $sce.trustAsHtml(markup);

And bind in HTML like this:

<div  ng-bind-html="markup"></div>

Here is a plunker.

Comments

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.