Reputation: 1493
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?
Upvotes: 1
Views: 121
Reputation: 3987
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.
Upvotes: 1
Reputation: 996
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.
Upvotes: 0