Odyss3us
Odyss3us

Reputation: 6635

Angular $compile / $controller not binding scope to dynamic HTML

As the title states, I am trying to compile, and link an angular controller, to dynamic HTML.

My goal is to create a service, that takes a set of options, one of which would be a controller, that I could link to the dynamic HTML before appending it to the DOM, which would then give the user of this service acces to that bound controller's $scope as well, with the ability to optionally override the scope, should they so wish.

I've attempted this with the $controller and $compile services, but for some reason the template is being appended, but is not being processed, it does not seem like scope is being set, as once appended, it displays the template tags, as opposed to the actual values.

Should I be using either $controller or $compile, but not in conjunction?

I'm not entirely sure where I am going wrong, some guidance would be much appreciated.

panel_template.html

<div class="pathwindow">
    <div class="pathwindow_title">
        <h1>{{ title }}</h1>
    </div>
    <div class="pathwindow_content">
        {{ content }}
    </div>
</div>

app.controller.js

(function () {
    'use strict';

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

    appController.controller('appController', ['$state', '$scope', '$rootScope', 'PanelService', function ($state, $scope, $rootScope, PanelService) {

        $scope.title = 'A Awesome Title';
        $scope.content = 'This is some content...';

        $scope.clickMe = function () {

            PanelService.show('panel_template.html', 'appController');

        };

    }]);

})();

app.factory.js

(function () {
    'use strict';

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

    appFactory.factory('PanelService', ['$injector', '$controller', '$compile', '$rootScope', '$http', function ($injector, $controller, $compile, $rootScope, $http) {

        var self = {};

        self.show = function (template_url, controller, scope) {

            var $scope = angular.isObject(scope) ? scope.$new() : $rootScope.$new();

            $http.get(template_url).then(function(response){

                var html = response.data;

                angular.element('#panels').append(html);

                $controller(controller, { $scope: $scope, $element: html });

                $compile(angular.element(html))($scope);

            });

        };

        return self;

    }]);

})();

Upvotes: 0

Views: 2609

Answers (1)

Nikos Paraskevopoulos
Nikos Paraskevopoulos

Reputation: 40296

You are passing a detached element to $compile, not the element that was created by appending the response.data to the DOM. This element is thrown away right after the statement. The response.data string without compilation is attached to the DOM, that is why you are seeing the "uncompiled" template.

You will have more luck with the following:

$http.get(template_url).then(function(response) {
    var elem = $compile(response.data)($scope);
    angular.element('#panels').append(elem);
    $controller(controller, { $scope: $scope, $element: elem });
});

This is frightening code though: Does the compiled element get destroyed correctly when it should? Does the controller get destroyed correctly? Does the controller even apply, or are references to it lost after the $controller(...) statement and thus it ceases to exist?

You must check these out and may have to adjust the code, e.g. listen to the $destroy event and cleanup and/or keep a reference to the controller.

It also frightens me that a service does DOM manipulation like that. I am pretty sure there is another way (could be the nested views of ui-router - but I am not entirely sure of your exact use case).

Upvotes: 1

Related Questions