thanks for looking. I'll dive right in:
A JSON object has HTML links with ng-click
attributes, employing ng-bind-html
, with $sce
's trustAsHtml
to make the HTML safe. I have also used a custom angular-compile
directive to compile the angular click listener into the app after the json is loaded in a $q
promise.
All of this works as intended, at first glance...
JSON
{
"text" : "Sample of text with <a data-ng-click=\"__services._animation.openModal('g');\">modal trigger</a>?"
}
VIEW
<p data-ng-bind-html="__services.trustAsHTML(__data.steps[step.text])"
data-angular-compile></p>
DIRECTIVE
angular.module('app.directives.AngularCompile', [], ["$compileProvider", function($compileProvider) {
$compileProvider.directive('angularCompile', ["$compile", function($compile) {
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
return scope.$eval(attrs.angularCompile);
},
function(value) {
element.html(value);
$compile(element.contents())(scope);
}
);
};
}])
}]);
The Issue:
Okay, so it works. My app loads, it serves the safe HTML, and my ng-click
opens the modal, passing it's params. I see class='ng-binding'
on the surrounding p
tag, class="ng-scope"
on the a
tag in the generated html. Hooray!
The next order of business is to write that data in a another model that tracks progress, and run it through the same ng-bind
, trustAsHTML
, angular-compile treatment
in another view. Just copying the data into a sibling object.
Here's where it fails!
<p data-ng-bind-html="__services.trustAsHTML(__state.modal.text)"
data-angular-compile></p>
In the second view, which is a modal in the same scope ($rootScope) on the same page - the bind, and trustAsHTML Angular is applied. But the link is not clickable, and no class="ng-scope"
is generated on the a
tag.
If fFurther explanation of my set-up might help understand the issue, let me detail it here. All initial app is set up by the concierge and it store most data in $rootScope:
return angular
.module('app', [
'ngResource',
'ngSanitize',
'ui.router',
'oslerApp.controllers.GuideController',
'oslerApp.services.ConciergeService',
'oslerApp.services.DataService',
'oslerApp.services.LocationService',
'oslerApp.services.StatesService',
'oslerApp.directives.AngularCompile',
])
.config(function($stateProvider, $urlRouterProvider) {
// For any unmatched url, redirect to landing
$urlRouterProvider.otherwise("/");
// Now set up the states
$stateProvider
.state('guide', {
url: "/guide",
templateUrl: "views/guide.html",
controller: 'GuideController as guide'
})
.state('contact', {
url: "/contact",
templateUrl: "views/contact.html"
})
})
.run(function(ConciergeService) {
ConciergeService.init();
});
I've spent 2 days refactoring my whole site to see if it was because the modal was in it's own directive, but putting it within the same template and scope didn't seem to help me here.
Lesson: If you have to refactor everything and still make no strides, you're doing it wrong.
To solve this, 2 days of hair pulling later, I made a tiny little service and pass the problem text through that:
compiler: function(element, template, content, scope) {
element = $(element);
template = template[0] + content + template[1];
var linkFn = $compile(template);
content = linkFn(scope);
element.replaceWith(content);
},