Angular is precompiled
Remember, Angular is precompiled. Angular template syntax is consumed and transformed when it is compiled, which means the DOM isn't actively checked for Angular syntax. In fact, one goal of Angular is to decouple your code from the DOM.
Think of it this way: adding a (click)
to an element isn't valid vanilla HTML syntax. When you add that binding to a template in an Angular project, compile it, and inspect the output through your DevTools, that (click)
attribute won't be on the element in the compiled HTML anymore. That's because the Angular compiler recognizes that (click)
syntax, removes it from the outputted HTML, and instead registers an onclick
handler behind the scenes that behaves as you'd expect in the scope it's assigned, and Angular manages this behavior in its own space. Once it's compiled as a component, simply altering the HTML won't add anything that Angular will be aware of -- the logic is there, but it's been consumed by the framework.
Raw HTML
Now, Angular shines with this idea of a template syntax, but when dealing with raw HTML things can be a bit trickier to do 'The Angular way.' The ideal solution would be to not parse raw HTML at all, but I'll assume for your case that it's a necessity.
Since Angular has the DOM wrapped up in its API, it's considered best practice to not directly manipulate it (note: binding potentially expensive function calls to inputs can also be bad...called on change detection cycles). Something you could try instead:
<app-link-comm [comm]="comm"></app-link-comm>
where app-link-comm
is a component:
@Component({
selector: 'app-link-comm',
template: '<p [innerHTML]="comm.Communication"></p>`,
styles: {some-style: stylish}
})
export class LinkCommComponent implements AfterViewInit {
// Allow the parent to bind a comm to the component
@Input('comm') comm;
constructor(public render: Renderer2, public router: Router) {}
// Wait until afterViewInit so the view is drawn and inputs are resolved
ngAfterViewInit() {
// Search within this node with a jQuery-like DOM querySelector
let footer = this.renderer.selectRootElement('footer');
// attach some Angular-aware behavior to it
this.renderer.listen(footer, 'click', (event: Event) => {
// do whatever you want on click here. We could emit an event that the parent component could listen to, but let's just assume we're changing routes and act on the router directly.
this.router.navigate(`#commID_${this.comm.CommunicationId}`);
}
/** That leaves us with a click listener on the footer.
/* If we wanted to append a child element, we just use the Renderer with the new element:
**/
let a = document.createElement('a');
// We could listen for clicks on the <a> like above, but let's just attach vanilla behavior:
a.href = `#commID_${this.comm.CommunicationId}`;
this.renderer.appendChild(footer, a);
}
}
The Renderer2 is an injectable service that both wraps up differences in browser and mobile APIs and also allows us to attach behaviors to elements that Angular can manage through its NgZone
.
Alternatively:
If you wanted to append an anchor element to the footer with an href
The same thing could potentially be achieved with directives, pipes, or even dynamically rendered components, but I think this is a good solution when dealing with raw HTML. The component gets a single binding, the [innerHTML]
allows Angular to sanitize the input, and DOM querying is done through the Angular Renderer
instead of manual parsing.
Since this is now its own component, other localized behaviors can be kept here as well.
As far as adding directives to raw HTML, it's not going to be possible. Angular needs to be aware of the directive before compile time to attach its behaviors correctly. Your best bet would be to make a host component/element with the directive on it, then bind the correct (parsed) html elements to that element.