Reputation: 12838
We're storing all our page content and blog posts etc inside WordPress, using its API to render the data in our Aurelia app. This is working well;
<div class="excerpt" innerhtml.bind="post.excerpt.rendered"></div>
Authors would now like to be able to link to popups or use route-href
or other custom Aurelia attributes from within their blog posts, however code added to the page using innerhtml.bind
doesn't get parsed by Aurelia.
I love that a normal <a href="...">
"just works" in Aurelia - but we have plenty of custom attributes (like <button popup="name-of-popup">...</button>
which can't be used by authors.
How can we overcome this?
Edit: With the comments from @bigopon I've started on something, but still can't quite get it to work. Either I suck at searching or the documentation is a little lacking on the TemplatingEngine.enhance()
method, but I tried creating a custom attribute like this:
import {Aurelia, inject, TemplatingEngine} from 'aurelia-framework';
@inject(Element, Aurelia, TemplatingEngine)
export class AureliaEnhanceCustomAttribute {
constructor (el, aurelia, templatingEngine) {
this.el = el;
this.aurelia = aurelia; // NOTE: I've never done this before - is it even correct?
this.templatingEngine = templatingEngine;
}
attached () {
this.el.innerHTML = this.value;
// NOTE: Without checking for this we get an endless loop of attached() calls
if (!this.el.classList.contains('au-target')) {
this.templatingEngine.enhance({
element: this.el,
container: Aurelia.container, // NOTE: I'm guessing here
resources: Aurelia.resources, // NOTE: Same here, but I need all my global resources to be available inside the enhanced element too
bindingContext: {} // NOTE: Not sure what to pass in here at all.. :/
});
}
}
}
And I'm using it like so:
<div aurelia-enhance.bind="exampleContent"></div>
Where exampleContent
is a string fetched from an API call that could look something like this: '<my-custom-element></my-custom-element><button my-custom-attribute="some-value">Log in</button>'
.
Upvotes: 2
Views: 1605
Reputation: 1964
You are on right track. There are few things to consider
bindingContext
/ overrideContext
: these two you can get by hooking into bind
lifecycle of the custom attribute. So you will be able to pass them to the enhance
instruction (requires only bindingContext
, but passing both is better, helps traversing the scope). About bindingContext
, 99% it will be the view model you are in, but you can always use a different object. In this case, this
(custom attribute view model), is the right one.
resources: depends on how you want the resource scope of the view returned by TemplatingEngine.prototype.enhance
will be. Passing global Aurelia
instance's resources
will not yield the local resources scope of the custom element it resides in. In order to have the same resources with the element the attribute annotates on, hook in to created
lifecycle of the attribute, store the first parameter as owningView
. This is the view of the custom element containing the attribute. Then you can access its resources by owningView.resources
cleaning: TemplatingEngine.prototype.enhance
returns a View
, you need to store this reference to detached
and unbind
in your attribute life cycle too
Sanitizing
An example: https://gist.run/?id=9dd32bc8a772526ae527f593e26b275b
The gist above is an example I of inheritance / enhance I made to answer another question on Discourse. You can have a look at the select-field
to see more example there. It's similar to what you did there.
P/S: If you are happy with the solution, maybe consider writing a blog post / article or open a topic on Aurelia Discourse forum to help others, who may also struggle there.
Updated example:
import {Aurelia, inject, TemplatingEngine, DOM} from 'aurelia-framework';
@inject(Element, TemplatingEngine)
export class AureliaEnhanceCustomAttribute {
constructor (el, aurelia, templatingEngine) {
this.el = el;
this.templatingEngine = templatingEngine;
}
// Custom attribute doesn't have its own view
// Only the view of the custom element that owns it
created(owningElementView, thisView) {
this.owningElementView = owningElementView;
}
bind(bindingContext, overrideContext) {
this.bindingContext = bindingContext;
this.overrideContext = overrideContext;
}
attached () {
this.el.innerHTML = this.value;
// NOTE: Without checking for this we get an endless loop of attached() calls
if (!this.el.classList.contains('au-target')) {
this.dynamicView = this. this.templatingEngine.enhance({
element: this.el,
// we have two choices here, either the container of owning element, or this attribute
// Lets go with the element, since it propbably has everything we need
container: this.owningElementView.container,
// the resources has information about its parent,
// So we just need the resources of the element containing this attribute
resources: this.owningElementView.resources,
// Or this.bindingContext (same)
bindingContext: this,
// this helps travel up
overrideContext: this.overrideContext
});
}
}
detached() {
this.dynamicView.detached();
}
unbind() {
if (this.dynamicView) {
this.dynamicView.unbind();
}
}
}
Upvotes: 3