Accessing AngularJS services from outside AngularJS

If you read an Angular book or read a howto, you will think that Angular is the greatest discovery since fire. Everything is neatly stacked in modules, controllers, templates, directives, factories, etc. The problem comes when you want to use some code of your own, using simple Javascript that does specific work, and then you want to link it nicely with AngularJS. It is not always easy. My example concerns the simple display of a dialog which edits an object. I want it to work on every page, so I added it to the general layout template. The layout does not have a controller. Even if I add it, the dialog engine I have been using was buggy and I've decided to just use jQuery.dialog.
So here is my conundrum: How to load the content of a dialog from an Angular template, display it with jQuery.dialog, load the information with jQuery.get, then bind its input elements to an Angular scope object. I've tried the obvious: just load the template in the dialog and expect Angular to notice a new DOM element was added and parse it and work its magic. It didn't work. Why can't I just call an angular.refresh(elem); function and get it over with, I thought. There are several other solutions. One is to not create the content dynamically at all, just add it to the layout, mark it with ng-controller="something" and then, in the controller, save the object you are interested in or the scope as some sort of globally accessible object that you instantiate from jQuery.get. The dialog would just move the element around, afterwards. That means you need to create a controller, maybe in another file, to be nice, then load it into your page. Another is to create some sort of directive or script tag that loads the Angular template dynamically and to hope it works.
Long story short, none of these solutions appealed to me. I wanted a simple refresh(elem) function. And there is one. It is called angular.injector. You call it with the names of the modules you need to load ('ng' one of them and usually the main application module the second). The result is a function that can use invoke to get the same results as a controller constructor. And that is saying something: if you can do the work that the controller does in your block of code, you don't need a zillion controllers making your life miserable, nor do you need to mark the HTML uselessly for very simple functionality.
Without further ado, here is a function that takes as parameters an element and a data object. The function will force angular to compile said element like it was part of the angular main application, then bind to the main scope the properties of the data object:
function angularCompile(elem, data) {
// create an injector
var $injector = angular.injector(['ng','app']);
// use the type inference to auto inject arguments, or use implicit injection
$injector.invoke(function($rootScope, $compile, $document){
var compiled = $compile(elem || $document);
compiled($rootScope);
if (data) {
for (var k in data) {
if (data.hasOwnProperty(k)) {
$rootScope[k]=data[k];
}
}
}
$rootScope.$digest();
});
}
Example usage:
angularCompile(dialog[0],{editedObject: obj}); // will take the jQuery dialog element, compile it, and add to the scope the editedObject property with the value of obj.
Full code:
OpenTranslationDialog=function(Rule, onProceed, onCancel) {
jQuery.ajax({
type: 'GET',
url: '/Content/ng-templates/dialogs/Translation.html',
data: Rule,
success: function(data) {
var dialog=jQuery('<div></div>')
.html(data)
.dialog({
resizable:true,
width:700,
modal:true,
buttons: {
"Save": function() {
var url='/some/api/url';
jQuery.ajax({
type:'PUT',
url:url,
data:Rule,
success:function() {
if (onProceed) onProceed();
$(this).dialog( "close" );
},
error:function() {
alert('There was an error saving the rule');
}
});
},
Cancel: function() {
if (onCancel) onCancel();
$(this).dialog( "close" );
}
}
});
angularCompile(dialog[0],{Rule:Rule});
},
error:function() {
alert('There was an error getting the dialog template');
}
});
}
Before you take my word on it, though, beware: I am an Angular noob and my desire here was to hack away at it in order to merge my own code with the nice structured code of my colleagues, who now hate me. Although they liked angular.injector when I showed it to them :)