1

I have an AngularJS SPA which loads articles into the view. Some articles have code examples and I want to use highlight.js to highlight it.

In my example below I have simulated a get request, 'cause that's how I load my dynamic content in the actual app. The $scope.test is very similar to what my actual app could get returned; some regular HTML to print out which includes code examples.

My problem: it doesn't really seem to work.

Specifically, nothing gets highlighted. It seems to me like I am missing an init or something... Halp?

I've also tried <div hljs/> with the same (lack of) result. There are no console errors.

This answer provides a solution that uses ng-model in the template. However, I don't use ng-model anywhere.

EDIT: I've made some changes to my example code to further explain the problem.

Here's my app (simplified):

var app = angular.module('app', ['ngSanitize']);

app.controller('ctrl', ['$scope', '$http',
    function($scope, $http) {
        "use strict";
        $http.get('/echo/html').then(function successCallback(response) {
            $scope.title = 'Some Title';
            $scope.metaStuff = 'Written by Awesome MacFluffykins';
            $scope.articleBody = '<p>Here\'s an example of a simple SQL SELECT:</p><pre><code class="sql" highlight>SELECT * FROM table WHERE user = \'1\'</code></pre>';
        }, function errorCallback(response) {
            console.log("Error: %d %s", response.code, response.message);
        });
    }
]);

Here's my HTML:

<div ng-app="app" ng-controller="ctrl">
    <h2>{{ title }}</h2>
    <p><small>{{ metaStuff }}</small></p>
    <div ng-bind-html="articleBody"></div>
</div>

And finally a jsFiddle.

Community
  • 1
  • 1
Brian Emilius
  • 717
  • 1
  • 8
  • 31
  • Seeing any errors in the console? – xkcd149 Jan 25 '16 at 19:42
  • @xkcd149 nope, nothing – Brian Emilius Jan 25 '16 at 19:43
  • 1
    Possible duplicate of [Angular JS: Detect if ng-bind-html finished loading then highlight code syntax](http://stackoverflow.com/questions/34215052/angular-js-detect-if-ng-bind-html-finished-loading-then-highlight-code-syntax) – iH8 Jan 25 '16 at 20:04
  • @iH8 This might explain it, however I don't use ng-model anywhere in my code, and I can't seem to get the solution in the link you provided to work (nothing happens - no errors). https://jsfiddle.net/ye4sozjp/2/ – Brian Emilius Jan 25 '16 at 20:29
  • 1
    You forgot to add the `highlight` directive to the element and to change the ng-bind-html to ng-model. Plus, since you're setting the `test` value in a async function, the value isn't immediately available so you'll need to watch it for changes, then execute `hljs`: https://jsfiddle.net/1qy0j6qk/ – iH8 Jan 25 '16 at 20:48
  • @iH8 testing that now, I think this is the answer! – Brian Emilius Jan 25 '16 at 20:55
  • @iH8 Yup! This did the trick. Can you make it an answer so I can accept? Thanks a bunch for the help :) – Brian Emilius Jan 25 '16 at 20:57
  • No thanks, always welcome. Posted the answer – iH8 Jan 25 '16 at 21:04

2 Answers2

1

Fiddle https://jsfiddle.net/vg75ux6v/

var app = angular.module('app', ['hljs', 'ngSanitize']);

app.controller('ctrl', ['$scope', '$http',
    function($scope, $http) {
        "use strict";
        $http.get('/echo/html').then(function successCallback(response) {
            $scope.test = '<h2>Here\'s some code:</h2><pre><code hljs class="sql">SELECT * FROM table WHERE user = \'1\'</code></pre>';
        }, function errorCallback(response) {
            console.log("Error: %d %s", response.code, response.message);
        });
    }
]).directive('compile', ['$compile', function ($compile) {
    return function(scope, element, attrs) {
        scope.$watch(
            function(scope) {
                return scope.$eval(attrs.compile);
            },
            function(value) {
                element.html(value);

                $compile(element.contents())(scope);
            }
        );
    };
}]);
Serginho
  • 7,291
  • 2
  • 27
  • 52
  • No, this will highlight everything. I just want to highligt the parts of the HTML that is code examples. As I wrote in the problem description, articles contain both regular HTML and code examples. – Brian Emilius Jan 25 '16 at 20:13
  • Sure? Look at my fiddle – Serginho Jan 25 '16 at 20:21
  • 100% certain. The `

    Here's some code:

    ` part is not to be highlighted. In my example I made a mock article string to show that it contains both "regular" HTML (article blah blah), and code examples (the part that is to be highlighted).
    – Brian Emilius Jan 25 '16 at 20:23
1

In my opinion it's best to use a directive for DOM manipulations like this. Pass your sourcecode through ng-model (you could also use another attribute) and run HLJS in the directive. Since you're using a asynchronous method to supply the value to your scope, you'll need to use $watch to catch the value and then run HLJS:

HTML:

<div highlight ng-model="test"></div>

Directive:

.directive('highlight', [
    function () {
        return {
            replace: false,
            scope: {
                'ngModel': '='
            },
            link: function (scope, element, attributes) {
                scope.$watch('ngModel', function (newVal, oldVal) {
                    if (newVal !== oldVal) {
                        element.html(scope.ngModel);
                        var items = element[0].querySelectorAll('code,pre');
                        angular.forEach(items, function (item) {
                            hljs.highlightBlock(item);
                        });
                  }
                });    
            }
        };
    }
]);

Working JSFiddle: https://jsfiddle.net/1qy0j6qk/

iH8
  • 27,722
  • 4
  • 67
  • 76