It is at times like these I feel the burden of my novice-level JS skills.
I have a UI requirement that goes something like this:
display a drop-down list with no value selected
display a red X next to the drop-down list when a value is selected
display a new drop-down list below the drop-down list with a value
if the number of âselected valuesâ equals the maximum amount, stop displaying new drop-down lists
if a red X is clicked remove the drop-down from the page and move lower selected values up
if the number of âselected valuesâ is less than the maximum, display a drop-down list at the end of the list
(hopefully I explained that clearly)
Iâve been wracking my brain on how to implement this and coming up with a complete blank.
How can I create a component that inserts itself into the DOM?
Is this one component or more?
What do I need to consider when implementing this behavior?
Any guidance/advice will be greatly appreciated. Links to online articles would be super cool, too. I want to learn how to implement this, which means understanding how it works.
Thanks!
P.S. I have additional requirements for similar âlistsâ that add additional fields (date fields, numeric fields) where filling out all the fields causes a new set of fields to get displayed.
So you can probably accomplish this with either an Ember Array or a hasMany relationship the same way, but letâs assume itâs the Array. Your template could look something like this:
Then in your actions you have to handle a few scenarios:
a dropdown value changes from null => a real value, push ânullâ to the end of the array (only if length < max values) and update the array item that changed (you have the index and the new value passed into the action)
a dropdown value changes from a real value to a different real value, just update it in the array (you have the index and the new value)
the dropdown cleared action is clicked, you have the index so just remove that value from the array
EDIT: to clarify this assumes you have a dropdown component that uses DDAU one-way binding, youâll definitely want that
EDIT 2: also assumes your clear button is a separate component but it could just as easily (probably more easily) be part of the dropdown component, same basic deal, you could even handle all scenarios with one action
It often helps to think about component design from the standpoint of data ownership.
In this case you have a dropdown component which is responsible for displaying a value, and utilizing actions that can update or clear the value.
Then you would want a âmanagerâ component (or controller if you chose) that manages the array of values (and therefore what gets rendered in the DOM). The manager is responsible for holding the array, rendering the array of dropdowns, and responding to actions from each subordinate dropdown to update/clear values.
Right now weâre using ember-power-select. Iâm not sure how to tell if it uses DDAU. I assume it does⌠shrugs
But, that aside, Thank you very much for your response. And I think I understand it.
I do have a model that defines a hasMany relationship that will used with the list of drop-downs. So dropdownValues is probably that relationship: model.codes.
my-dropdown is the component responsible for displaying the list of values, and dropdownChanged is implemented on the component that is the manager (from your next reply).
clear-value-button is the component that displays the red X and its action, dropdownCleared is also implemented in the manager component.
I know Iâll get better over time at this. Iâm feeling frustrated and inpatient. Thank you for your words of wisdom.
I hadnât thought of the âmanagerâ component until you mentioned it. That makes sense to me because I was visualizing/wanting something like that from the beginning. I just couldnât see how the pieces fit together.
Follow-up Question: how are the drop-downs added? removed?
I donât have time to dig into the question details (and @dknutsen is doing a great job there anyway), but I just want to say: stick with it! We were all there once, and the patterns click into place over time. Youâll get the hang of it!
Yeah the whole data ownership thing sorta (at least in my understanding) evolved from the same set of ideas that DDAU did (heavily influenced by React when it was first introduced). Basically clarifying what thing âownsâ the data (stores it and mutates it). DDAU stands for Data Down Actions Up, and basically means the ideal pattern for this sort of thing is figuring out who âownsâ the data (generally the âhighest upâ place that it is needed) and then passing data down to subcomponents, and then having those subcomponents passing actions back up to the owner rather than mutating the data themselves. Back in the day the manager component/controller would pass a value down to the dropdown and the dropdown would change the value by itself. This caused lots of issues in complex hierarchies because it was hard to figure out what had mutated the value and at what level. Itâs much clearer to establish data ownership and let the data owner manage the data. I havenât used power select in a while but Iâm sure you can use it in one-way binding mode even if it allows two way binding also. You basically just want to make sure that power select isnât change any values itself, itâs just passing a change action out and letting whatever is rendering it actually do the data mutation.
Follow-up Question: how are the drop-downs added? removed?
the dropdowns are added and removed as a function of the âmanagerâ adding or removing elements from the array. Since it is rendering each item in the array with a dropdown (via the template each helper) when you add a new ânullâ value to the array it would render an additional dropdown with no value selected. And when you remove the value that is âclearedâ from the array it will no longer render that dropdown. So really the âeachâ helper, rendered by the âmanagerâ component, is determining how many dropdowns to render. Does that make sense?
Good luck! And as @chriskrycho said, stick with it! Youâre doing great. IMO component design patterns are one of the hardest things to learn because theyâre more an art than a science. The nice thing is this subject really transcends specific frameworks so a lot of the ideas discovered in React/etc components can be applied to Ember apps. If youâre looking for blog posts to read anything written fairly recently about DDAU, contextual components, higher order components, etc. are probably good places to start. Also I think EmberMap and the rockânâroll with ember book both have pretty good component design examples.
Sorry to bother you but I was wondering if you help me over a sticking point. I have everything implemented, and mostly everything works!
I went with a component for my âlist itemâ because each âitemâ will have multiple elements (i.e. a drop-down, a drop-down plus a numeric field, a drop-down plus a date field, etc.).
Is there a way, in my âmanagerâ component, to force a refresh, to re-render the page?
I have the following code in my âmanagerâ and it does not work as expected:
conditionCodeChanged(index, option) {
debug(`${this.toString()}; conditionCodeChanged(${index}, ${option})`);
// NOTE: This does not update the display
const items = get(this, "items");
const conditionCode = items.objectAt( index );
const parts = option.split("-");
set(conditionCode, "code", parts[0].trim());
conditionCode.save();
const amt = 1;
items.replace(index, amt, [ conditionCode ]);
items.enumerableContentDidChange();
this.rerender();
},
I thought if I changed the items array this would trigger the refresh. It did not. I have a feeling ember-power-select might be thwarting my efforts too, but thatâs just a hunch, and one thatâs difficult to run down.
Second Question: How can I trigger my own events? I think I want to do this as my âitemsâ become more complex.