I'm trying to add options to a previously created Vnode (select element). The element is returned before the options are available, since they come from an async function.
Although I try to set the children upon the callback, that does not change the rendered element. And the options are not displayed in the select element.
My code bellow:
const getSelect = function (field: FieldDef, key: string) {
const elem = m('div', { class: 'field' }, [
m('label', { class: 'label' }, field.label ? field.label : field.name),
m(
'div',
{
class: field.controlClass
? 'select ' + field.controlClass
: 'select is-small',
onchange: (val: number | string) => {
console.log(val);
},
},
[
m(
'select',
{
class: field.class ? field.class : '',
name: field.name ? field.name : key,
id: key,
value: field.value,
},
[],
),
],
),
]);
if (field.options) {
void field.options().then(function (items) {
elem.children = [];
elem.children.push(
items.map((i) => {
return m('option', { value: i.value }, i.text);
}),
);
console.log('the options are:');
console.log(elem.children);
});
}
return elem;
};
Your code is using a mixture of expressions and statements to describe the view: something that Mithril and most Javascript view libraries since 2013 try to separate. The solution is to describe the view exclusively as an expression, by interpolating values from a model which can be changed by statements outside of the view when necessary.
The reason your code sample won't work is that vnodes cannot be manipulated: instead new vnodes must be generated containing the appropriate data.
The solution is to have:
In the code below I’ve approximated your problem case with a select element whose options will change later on. To simplify this example, the asynchronous change comes from user interaction with the view — instead of a promise like in your code — but the principles remain the same: the code that receives the new options, whatever it may be, changes the model and instructs the view to m.redraw
; when the view redraws, it interpolates its persistent reference from the model, receives a new value, and persists the change to DOM.
// 1. A dynamic data model
const model = {
options: [],
}
m.mount(document.body, {
// 2. A view expression that references the model for any dynamic values
view: () => [
m('select',
model.options.map(value =>
m('option', value),
),
),
m('hr'),
m('button', {
onclick : populate,
},
'Populate'
),
m('button', {
onclick : reset,
},
'Reset'
),
],
})
// 3. Control code which can change the model values and instruct the view to recompute
function populate(){
model.options = [1, 2, 3]
m.redraw()
}
function reset(){
model.options = []
m.redraw()
}
<script src="https://unpkg.com/mithril@2.0.4/mithril.min.js"></script>