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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.