Charlie S Charlie S - 3 months ago 16x
AngularJS Question

Can I use $compile in an Angular service directly on a templateUrl instead of on raw HTML or a raw angular.element?

Given the following service that is meant to create a "dialog" element (i.e. a modal):

app.service('dialog', ['$document', '$compile', '$rootScope',
function($document, $compile, $rootScope) {

var body = $document.find('body');
var scope = $rootScope.$new();

this.createDialog = function() {
var dialogElem = angular.element('<div ng-include="\'/dialog.html\'"></div>');


which can be utilized in a controller like so:

$scope.someFunction = function() {

Is there a way that I can use
or anything else to not have HTML in my service? I'd really prefer to just invoke a directive, so that running
immediately injects a directive into my DOM and thus the directive is responsible for linking a new controller and template together. If I'm going about this the wrong way I'm totally open to constructive ideas.


Of course you can!, here you go:

app.factory('modalService', function ($document, $compile, $rootScope, $templateCache, $http) {

    var body   = $document.find('body'),
        modals = [];

    var service = {
        show: function (template, data, modal) {

            // The template's url
            var url = 'template/modal/' + template + '.html';

            // A new scope for the modal using the passed data
            var scope = $rootScope.$new();
            angular.extend(scope, data);

            // Wrapping the template with some extra markup
            modal = modal || angular.element('<div class="modal"/>');

            // The modal api
            var api = {
                close: function () {

                    modals.splice(modals.indexOf(api), 1);

                replace: function (template, data) {

                    return angular.extend(api,, data, modal));

            // Adding the modal to the body

            // A close method
            scope.close = api.close;

            // Caching the template for future calls
            $http.get(url, {cache: $templateCache})
                .then(function (response) {

                    // Wrapping the template with some extra markup
                    modal.html('<div class="win">' + + '</div>');

                    // The important part


            return api;
        showOrReplaceLast: function (template, data) {

            return, data, modals.length > 0 ? modals[modals.length - 1] : null);

    return service;

Some notes:

  • You need to insert the modal somewhere in the DOM, that's why the $document is injected.
  • Yes, you can take the modal markup out of here.
  • Remember to create new scopes for the dialog and to destroy them ($rootScope.$new).
  • This is a WIP, I hope it's clear enough.