Render HTML in AngularJS templates using `ng-bind-html`

When you want to render HTML in AngularJS templates, you have to use the attribute ng-bind-html. For security reasons, AngularJS requires you to add as dependency angular-sanitize.
Below you can find an example on how to render HTML in the search results page.

Problem

You encounter the error Error: [$sce:unsafe] Attempting to use an unsafe value in a safe context when rendering HTML tags in an Angular JS template.
Example:

<div ng-repeat="record in vm.invenioSearchResults.hits.hits track by $index">
  <div ng-bind-html="'<p>This is <em>HTML</em></p>'"></div>
  <div class="well">
     <h4>{{ record.metadata.abstract }}</h4>
  </div>
</div>

Solution

Use angular-sanitize. This example will refer to a new Invenio 3.2 installation, with site name my_site.

  1. Create a new JavaScript file my_site/theme/assets/js/my_site/custom-search-app.js to override the default AngularJS app of invenio-search-js and inject ngSanitize:

    import angular from "angular";
    import ngSanitize from 'angular-sanitize';
    import "invenio-search-js/dist/invenio-search-js";
    
    angular.element(document).ready(function () {
        angular.bootstrap(document.getElementById("invenio-search-custom"), [
            "invenioSearch",
            "ngSanitize"
        ]);
    });
    

    Take note of the id that you have used here (invenio-search-custom), you will need it later.

  2. Add the new JS file and angular-sanitize to your my_site/theme/webpack.py or my_site/theme/bundle.py. Don’t forget it to update the Python entry points in the setup.py if this is a new file.

    from flask_webpackext import WebpackBundle
    
    theme = WebpackBundle(
        __name__,
        'assets',
        entry={
            'custom-theme': './scss/custom/theme.scss',
            'custom-search-app': './js/custom-search-app.js',
        },
        dependencies={
            # add any additional npm dependencies here...
            'angular-sanitize': '~1.4.9',
        }
    )
    
  3. Override the default search.html template in invenio-search-ui. You will have to create a new file, e.g. my_site/theme/templates/my_site/search_custom.html and copy/paste the content of the search.html file. Then, just change the HTML tag id attribute that contains the search AngularJS application:

    ... search.html code ...
    
    {%- block body_inner %}
    <div id="invenio-search-custom">
        <invenio-search
            search-endpoint="{{ config.SEARCH_UI_SEARCH_API }}"
            search-extra-params='{% if search_extra_params %}{{search_extra_params|tojson}}{% endif %}'
            search-hidden-params='{% if search_hidden_params %}{{search_hidden_params|tojson}}{% endif %}'
            search-headers='{"Accept": "{{ config.SEARCH_UI_SEARCH_MIMETYPE|default('application/json')}}"}'
            >
            {{super()}}
        </invenio-search>
    </div>
    {%- endblock body_inner %}
    
    ... search.html code ...
    
  4. Change your main config my_site/config.py to use this new template:

    SEARCH_UI_SEARCH_TEMPLATE = "my_site/search_custom.html"
    
  5. Run the build again and restart the server:

    ./script/bootstrap
    ./script/server
    

    or

    pipenv run invenio collect -v
    pipenv run invenio webpack buildall
    ./script/server
    

    or for older Invenio

    (myvirtualenv) invenio collect -v
    (myvirtualenv) invenio assets build
    ./script/server