I am developing a custom attribute on aurelia to let user select from a list while typing in a textarea. For example, the usage will be something like this:
<textarea value.bind="description" placeholder="your description here" auto-complete></textarea>
and as you probably noticed, the auto-complete
is the attribute. Now when I want to show the hints, I want to do this in a custom-element to keep it simple. So the attached method of the attribute will be something like this:
attached() {
this.containerElement = document.createElement('div');
this.containerElement.style.position = 'relative';
const ce = document.createElement('autocomplete-menu');
this.containerElement.appendChild(ce);
const ceView = this.templatingEngine.enhance(ce);
ceView.attached();
const currentParrent = this.element.parentElement;
currentParrent.replaceChild(this.containerElement, this.element);
this.containerElement.appendChild(this.element);
}
now it opens and shows the hint area successfully. The screen-shot:
The problem gets started when I want to communicate to the generated element from the attribute view-model. For example, I want to send a data to its view-model or bind some object to a bindable property of that. For this issue I have found these solutions:
https://discourse.aurelia.io/t/dynamically-add-custom-attribute-to-element/1400/6 https://ilikekillnerds.com/2016/01/enhancing-at-will-using-aurelias-templating-engine-enhance-api/
and also read the last part of this:
https://aurelia.io/docs/binding/how-it-works#abstract-syntax-tree
and figured out that, I have to introduce an object for the element's view-model as its bindingContext or overrideContext. So if I am right, I have tested the solutions below:
this.containerElement.appendChild(ce);
let vm = { test: 1 }
const ceView = this.templatingEngine.enhance({ element: ce, bindingContext: vm });
ceView.addBinding(vm);
ceView.attached();
and
this.containerElement.appendChild(ce);
let vm = { test: 1 }
const ceView = this.templatingEngine.enhance(ce);
ceView.bind(vm);
ceView.attached();
console.log(ceView);
but on the attached lifecycle-hook of the element, I have logged the view-model and noticed that the bindingContext properties are not present on this
.
Now there are two questions:
bindable
s? I mean defining a bindable property on the element view-model and bind to it after the enhance method is done. Instead of working with bindingContext and overrideContext?Fortunately the problem has been solved. The solution was not that much complex but 1. it is very very useful for me (as I will describe next) 2. the lack of aurelia documentation made this simple issue hard to solve.
The problem was about I was misunderstanding the meanings of bindingContext and container. I was thinking that bindingContext will refer to view-model of the child element and I must point its container to the parent context (which is the attribute). But I found that I should point the bindingContext to the context of attribute. I still don't know enough about those two meanings but the solution is as simple and beautiful as the following sample:
this.containerElement = document.createElement('div');
this.containerElement.style.position = 'relative';
this.containerElement.style.display = 'flex';
this.containerElement.style.flexDirection = 'row-reverse';
this.ce = document.createElement('autocomplete-menu');
this.ce.setAttribute('filter.bind', 'filter');
this.ce.setAttribute('show.bind', 'showMentionPicker');
this.ce.setAttribute('parent-height', '${element.clientHeight}px');
this.ce.setAttribute('view-model.ref', 'mentionPickerViewModel');
this.ce.setAttribute('on-select.call', 'complete(mentionPickerViewModel.getRemainingOfHint())');
const ceView = this.templatingEngine.enhance({ element: this.ce, container: this.container });
ceView.bind(this);
ceView.attached();
this.containerElement.appendChild(this.ce);
const currentParrent = this.element.parentElement;
currentParrent.replaceChild(this.containerElement, this.element);
this.containerElement.appendChild(this.element);
and the bindings are referring to the attribute's this context which has the following properties:
filter = '';
showMentionPicker = false;
mentionPickerViewModel;
If the sample is not enough for your problem ask me for more information.