Andreas Linnert
Andreas Linnert

Reputation: 577

Insert Angular directive into DOM from inside a Service

My goal is to create an Angular module that displays popup dialog messages. This module contains a directive (HTML, CSS and JavaScript) containing the internal logic (and markup and styles). Plus there's a service (factory) which acts as an API that can be used by other services.

Now this service of course has an openDialog() function which should insert the dialog directive into the DOM and present it to the user.

All solutions to this problem I have found so far make use of the $compile function. But it needs scope as a parameter. In a service where there's no scope though. They only exist in controller or link functions.

The reason I chose this implementation is for separation of concerns (directive's link and controller for internal usage, factory for external usage because it can be dependency injected). I know I could pass the scope when calling the function like this:

popupDialogService.openDialog({ /* options */ }, $scope);

But I don't see the point. It doesn't feel right. What if I call that function from inside another service which doesn't use scope either?

Is there a way to easily put the directive into the DOM from inside the service function or is there a better way to solve this problem?

Another solution I'm thinking about is calling a function of the directive's controller from inside the directive's factory. Is that possible?

Code

popupDialog.directive.js

angular.module('popupDialog').directive('popupDialog', directive);

function directive() {
    return { ... };
}

popupDialog.service.js

angular.module('popupDialog').factory('popupDialogService', factory);

function factory() {
    return { openDialog, closeDialog }; // *ES2015

    function openDialog(options) {
        // this function should put the `popupDialog` directive into the DOM
    }

    function closeDialog() {
        // and this one should remove it
    }
}

some.random.service.js

angular.module('myApp').factory('someRandomService', factory);

factory.$inject = ['popupDialogService'];
function factory(popupDialogService) {
    return { clickedButton };

    function clickedButton() {
        popupDialogService.openDialog({ /* options */ });
        // Sample implementation.
        // It shouldn't matter where this function is beeing called in the end.
    }
}

Upvotes: 2

Views: 676

Answers (1)

dfsq
dfsq

Reputation: 193281

I know I could pass the scope when calling the function ... And it doesn't feel right.

Well you anyway need scope for dialog HTML content, Angular needs to compile and render it in some scope, right? So you have to provide scope object for your template somehow.

I suggest you to take a look at popular modal implementations like they do it, for example Angular UI Bootstrap's $modal or this simple one I was creating for my needs. The common pattern is passing scope parameter with modal initialization or use new child scope of the $rootScope for dialog. This is the most flexible way that should work for your both cases.

After all, it's not necessarily has to be real scope instance. You can even make your service accept plain javascript object and use it to extend new $rootScope.$new() object with.

Upvotes: 1

Related Questions