0

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;

    }]);

})();

1 Answer 1

1

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).

Sign up to request clarification or add additional context in comments.

2 Comments

Of course, I don't know how I missed that! Thanks Nikos. The controller as well as the element it's bound to does get destroyed correctly, along with other bits that should get cleaned up, I have updated the service to do so. Unfortunately I am not in a position where I can make use of a directive to do the DOM manipulation, as I am sure that would most likely be the better way of approaching this? Unfortunately I am working with a tightly coupled Twig / jQuery based system, that severely limits my options with how I approach this, as it needs to work in conjunction with existing jQuery code.
Yes, usually directives are the way to go. If you want a directive with dynamic template though, you would most probably have to resort to a similar solution (i.e. involving $compile and DOM manipulation of some kind). Directives can wrap jQuery widgets though.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.