Search code examples
javascriptangularjsangular-ui-bootstrapangular-ui

AngularUI Bootstrap Carousel not working properly when binding HTML content with $compile


AngularUI Bootstrap version ^2.4.22

AngularJS version 1.6.4

Angular Sanitize version ^1.6.1

I'm having trouble using AngularUI Bootstrap's Carousel plugin. In my scenario, i need to read an external file containing some template paths, and load each of them as a slide. See example below:

index.html (where directive is being called)

     <body id="body" ng-app="homePage">
        <div id="miolo">
            <div example-directive class="ng-hide"></div>
            <div banner-rotativo></div>
            <div id="menu-footer"></div>
        </div>
     </body>

banner-rotativo.directive.js - Basically, in this directive, I have a template following the structure of AngularUI Bootstrap's demo, and i'm binding the response data to $scope.slides array. When i push into htmlContent property the $compile(objResponseInner)($scope) result, the carousel behavior works okay, but it renders [[object HTMLDivElement]] and things like that as an item.

enter image description here

angular.module('homePage')
.directive('bannerRotativo', ['$compile', '$http', 'moduleUrl', '$templateRequest', function ($compile, $http, moduleUrl, $templateRequest) {
    return {
        template:   '<div style="height: 305px" ng-controller="bannerHomeController" class="" >\
                        <div uib-carousel active="active" interval="myInterval" no-wrap="noWrapSlides">\
                            <div uib-slide ng-repeat="slide in slides track by slide.id" index="slide.id">\
                                <div ng-bind-html="slide.htmlContent">\
                                </div>\
                            </div>\
                        </div>\
                    </div>',

        link: function (scope, element, attributes, controller) {

            //Carousel
            scope.myInterval = 5000;
            scope.noWrapSlides = false;
            scope.active = 0;
            scope.slides = [];
            var intCurrentIndex = 0;

            $http({
                method: 'GET',
                url: moduleUrl.getUrl('homepage', '../config/banner-rotativo.conf')
            }).then(function success(objResponse, status, headers, config) {
                var objData = objResponse.data;
                if (objData.slides) {
                    angular.forEach(objData.slides, function (objItem, strObjectName) {
                        var strTemplatePath = moduleUrl.getUrl('homepage', '..' + objItem.caminho);
                        if (strTemplatePath) {
                            $templateRequest(strTemplatePath).then(function success(objResponseInner) {
                                var objContent = $compile(objResponseInner)(scope);
                                scope.slides.push({
                                    htmlContent: objContent,
                                    id: intCurrentIndex++
                                });
                            });
                        }
                    });
                }
            });
        }
    }


}]);

banner-rotativo.conf

{
"slides": {
    "banner-ex-one": {
        "titulo": "exone",
        "caminho-imagem": "assets/one.jpg",
        "caminho": "/html/components/banner-rotativo/banner-ex-one.view.html"
    },
    "banner-ex-two" : {
        "titulo": "extwo",
        "caminho-imagem": "assets/two.jpg",
        "caminho": "/html/components/banner-rotativo/banner-ex-two.view.html"
    },
    "banner-rav" : {
        "title": "rav",
        "caminho-imagem": "assets/rav.jpg",
        "caminho": "/html/components/banner-rotativo/banner-rav.view.html"
    },
    "banner-aviso" : {
        "title": "Quadro de comunicações 1",
        "caminho-imagem": "assets/aviso.jpg",
        "caminho": "/html/components/banner-rotativo/banner-aviso.view.html"
    },
    "banner-custom" : {
        "title": "Quadro de comunicações 2",
        "caminho-imagem": "assets/custom.jpg",
        "caminho": "/html/components/banner-rotativo/banner-custom.view.html"
    }
  }
}

Loaded template example:

<div id="frameOne" ng-controller="slideOneController" class="varejo-clique-promocao-one" title="Conteúdo Varejo - Quadro One">
<div class="item">
    <div id="dados">

        <!-- Imagem banner one -->
        <img id="one" ng-click="enviarFormOne()" class="one" alt="" ng-if="1==1" ng-src="caminhoImagem"
        />
        <!-- End imagem banner -->
        <span ng-if="hasText">{{bannerText}}</span>
    </div>
    <div id="excecao" class="excecao" ng-if="typeof(one.excecao) !== 'undefined'">
        DEU EXCECAO
    </div>
    <div class="carousel-title" id="tituloOne" ng-if="1==1" title="{{bannerTitle}}">
        {{bannerTitle}}
    </div>
</div>

Some important points:

  • I'm using $compile because my injected templates have controllers too. If i don't use it, my controllers aren't processed. When i simply insert the HTML without the $compile, it renders okay.

What's wrong?


Solution

  • The problem is that ng-bind-html expects html string and you are giving it the result from $compile that is actually an element object. You could take the HTML string from the compiled element and pass that instead, but you would probably run into all kind of trouble with that.

    But you can actually just skip the hassle with $compile by using ngInclude instead of ng-bind-html. It will handle both the template request and the compilation for you.

    So instead of requesting and compiling strTemplatePath, store that to the slide list:

    var strTemplatePath = moduleUrl.getUrl('homepage', '..' + objItem.caminho);
    if (strTemplatePath) {
        scope.slides.push({
            htmlUrl: strTemplatePath,
            id: intCurrentIndex++
        });
    }
    

    And then use that in the template:

    <div uib-slide ng-repeat="slide in slides track by slide.id" index="slide.id">
        <div ng-include="slide.htmlUrl"></div>
    </div>
    

    Here's a somewhat working fiddle: jsfiddle.net, although I had to fill in a few blanks here and there.