I'm struggling a bit with the proper pattern to use here. I have a model which represents a power selector called selector
, each selector
has a hasMany
with selectorOption
which makes up the options for the selector
I then have a dashboardItem
model which loops over each selector
and implements it.
route.js
export default Route.extend({
model(params) {
return RSVP.hash({
dashboard: get(this, 'store').findRecord('dashboard', params.dashboard_id),
selectors: get(this, 'store').findAll('selector'),
});
},
setupController(controller, models) {
controller.setProperties(models);
},
});
template.hbs
{{#each selectors as |selector|}}
<div class="column is-12 object-item">
<div class="card">
<header class="card-header">
<p class="card-header-title">
{{selector.title}}
</p>
</header>
<div class="card-content">
{{#power-select-multiple
placeholder="Vision"
options=selector.selectorOptions
searchEnabled=false
onchange=(action 'something...') as |option|}}
{{option.title}}
{{/power-select-multiple}}
</div>
</div>
</div>
{{/each}}
I'm not sure what to do on the onchange
, either with a custom function or using built in tools of power-select.
Each selector is a multi-selector.
This works correctly to the point that I can create any number of selectors and they display on the front end with their correct options as expected.
How should I go about saving the options the users choose against the dashboardItem
?
Here is a section from the database which shows the models and their relationships. Note there is currently no relationship between a selector
and a dashboardItem
(Maybe there should be though?)
{
"selectorOptions" : {
"-Kyc7on207d_IxnNw2iO" : {
"title" : "Apple",
"vision" : "-Kyc7nG9Bz3aEGLked8x"
},
"-Kyc7qC9_uxFgXP9c7hT" : {
"title" : "Orange",
"vision" : "-Kyc7nG9Bz3aEGLked8x"
},
"-Kyc7qqZPMikoG1r3r5g" : {
"title" : "Bannana",
"vision" : "-Kyc7nG9Bz3aEGLked8x"
},
"-Kyc7uZu8MTfUdH70cBR" : {
"title" : "Blue",
"vision" : "-Kyc7rtTPTMJxAPacg-L"
},
"-Kyc7vJC3ImzVOEraALx" : {
"title" : "Green",
"vision" : "-Kyc7rtTPTMJxAPacg-L"
},
"-Kyc7wCrqDz8CD_I-dYy" : {
"title" : "Red",
"vision" : "-Kyc7rtTPTMJxAPacg-L"
}
},
"selectors" : {
"-Kyc7nG9Bz3aEGLked8x" : {
"title" : "Fruits",
"selectorOptions" : {
"-Kyc7on207d_IxnNw2iO" : true,
"-Kyc7qC9_uxFgXP9c7hT" : true,
"-Kyc7qqZPMikoG1r3r5g" : true
}
},
"-Kyc7rtTPTMJxAPacg-L" : {
"title" : "Colours ",
"selectorOptions" : {
"-Kyc7uZu8MTfUdH70cBR" : true,
"-Kyc7vJC3ImzVOEraALx" : true,
"-Kyc7wCrqDz8CD_I-dYy" : true
}
}
}
}
The solution was to not fight against relationships with basic array storage.
For example
Base
export default Model.extend({
title: attr('string'),
visionOptions: hasMany('vision-option'),
});
Bases Options
export default Model.extend({
title: attr('string'),
vision: belongsTo('vision'),
});
The model to save the selected objects on
export default Model.extend({
//...
visionOptions: hasMany('vision-option', {async: true}),
//...
});
The component to handle saving, and selecting the correct objects
export default Component.extend({
tagName: "",
classNames: "",
selectedVisions: computed('dashboardItem.visionOptions', function () {
const visionId = this.get('vision.id');
const options = this.get('dashboardItem.visionOptions');
return options.filterBy('vision.id', visionId);
}),
actions: {
addVision(newList) {
let dashboardItem = get(this, 'dashboardItem');
let options = get(this, 'selectedVisions');
options.forEach(function (me) {
if (!newList.includes(me)) {
dashboardItem.get('visionOptions').removeObject(me);
}
});
newList.forEach(function (me) {
if (!options.includes(me)) {
dashboardItem.get('visionOptions').pushObject(me);
}
});
dashboardItem.save().then(() => {
dashboardItem.notifyPropertyChange('visionOptions')
});
}
}
});
Template to render power-select
{{#power-select-multiple
placeholder=""
options=vision.visionOptions
searchEnabled=false
selected=selectedVisions
onchange=(action 'addVision') as |vision|}}
{{vision.title}}
{{/power-select-multiple}}
This allows there to be an unknown number of "visions", with an unknown number of "visionObjects" to be loaded and saved.
The notifyPropertyChange
is required to update the computed property so the frontend renders when a user adds or removes a selected object. This is only awkward because there isn't a direct known database key.