英文:
Stimulus JS: Changing stimulus class values?
问题
我有一个 Stimulus JS 控制器,其中有一个名为 colorKey
的目标。ColorKey 可以是 5 种不同的颜色之一,通过添加 5 种不同的 CSS 类来设置('bg-green'、'bg-blue' 等)。
根据我对 StimulusJS 文档中的 css classes 部分的阅读,似乎我应该使用 classList.add
/classList.remove
调用来更新颜色。这样做是有效的,但这意味着我要运行以下代码:
this.colorKeyTarget.classList.remove(this.orangeClass)
this.colorKeyTarget.classList.remove(this.redClass)
this.colorKeyTarget.classList.remove(this.blueClass)
this.colorKeyTarget.classList.remove(this.blackClass)
this.colorKeyTarget.classList.add(this.greenClass)
这显然不是一种优雅的方法。
我希望/假设我可以像这样在 DOM 中做一些事情:
<div data-bg-color-class="bg-orange"></div>
然后只需调用:
this.bgColorClass = "bg-green"
而不是上面的繁琐操作。我假设有一种类似于后一种方法的方式,但我找不到是什么。有人有关于通过 Stimulus 分配 5 种类之一给 DOM 的最佳实践建议,而不涉及在添加所需状态之前删除每种可能状态的建议吗?
英文:
I have a Stimulus JS controller with a target called colorKey
. ColorKey can be one of 5 different colors, set by adding 1 of 5 different CSS classes ('bg-green', 'bg-blue', etc)
From my reading of the css classes section of the stimulusJS docs, it seems I'm expected to update the color by using classList.add
/classList.remove
calls. That works, but it means I'm running:
this.colorKeyTarget.classList.remove(this.orangeClass)
this.colorKeyTarget.classList.remove(this.redClass)
this.colorKeyTarget.classList.remove(this.blueClass)
this.colorKeyTarget.classList.remove(this.blackClass)
this.colorKeyTarget.classlist.add(this.greenClass)
which isn't exactly elegant to put it mildly.
I was hoping/assuming I could do something like:
<div data-bg-color-class="bg-orange"></div>
in the dom, and then simply call:
this.bgColorClass = "bg-green"
instead. I assume there's some way to do something similar to the latter, but I can't figure out what it is. Anyone have any suggestions on best practices for assigning one of 5 classes to a DOM via stimulus in a way that doesn't involve removing every possible state before adding the one state you want?
答案1
得分: 2
根据你想要实现的目标,Stimulus CSS Classes 方法可能不太合适。看起来你更像是在设置一个背景预览选择器。
你可以在这里使用类,但正如注意到的那样,随着选项的增加,添加更多选项会变得有点复杂。类更适合用于诸如“加载/激活”类这样的东西,这是一小组离散状态,也可以通过类名更改来反映。
相反,你可能希望在这里使用 Stimulus 值的方法,以及一些更容易映射到关闭除所选项之外的所有其他选项的东西。
我建议使用 Stimulus Object
值,这是一个可以存储在控制器上的 JSON 字符串,可以轻松地映射键/值对。
示例
- 在我们的 HTML 中,我们设置了所有颜色键及其适用类的 JSON 值。提醒一下,这必须是正确格式的 JSON,如果你在属性中使用
"
,你需要正确转义它。你的渲染库应该会为你做这个,但如果你手工编写这个值,双重检查输出。 - 你也可以使用 Stimulus action params,这样可以更轻松地准备一个按钮或类似的东西来切换颜色。
- 最后,我建议将目标的名称从
colorKey
更改为更简单的名称,因为你很可能在 HTML 的许多地方已经有了单词color
。
<div data-controller="bg-color" data-bg-color-classes-value='{"red": "something-red","blue":"something-borrowed","orange":"something-new","black":"bg-000000","green":"i-choose-green"}'>
<div data-bg-color-target="container">
内容!
</div>
<button data-action="bg-color#show" data-bg-color-key-param="red">Go Red!</button>
<button data-action="bg-color#show" data-bg-color-key-param="green">Go Green!</button>
</div>
- 在 JS 中,我们从
show
方法中提取出key
,可以从 CustomEventdetail
或 Action params 中提取出来。这可以根据你计划触发的方式进行多种不同的方式来完成。 - 在
show
方法中,我们在尝试更改 DOM 之前对一些事情进行了一些检查,如果有什么东西丢失,就会提前返回。 - 我们读取整个对象值(Stimulus 会将其从 JSON 转换为对象),然后使用
Object.entries
映射键/值对,以添加类或删除类。这意味着如果你想要添加/删除颜色,唯一需要更改的代码就是值的 HTML 数据属性。 - 控制器的其余部分对你有多少个颜色是不可知的,因此这使得它更具可重用性和灵活性。
class BgColor extends Controller {
static targets = ['container'];
static values = { classes: Object };
show(event) {
const { key } = event?.params || event?.detail || {}; // 从 Stimulus 动作参数或分派事件详细信息中提取键。
const element = this.containerTarget;
const classes = this.hasClassesValue ? this.classesValue : {};
const currentColorClass = classes[colorKey];
if (!currentColorClass || !element) return; // 忽略不匹配的类(也可以抛出错误)
Object.entries(classes).forEach(([colorKey, value]) => {
if (key === colorKey) {
element.classList.add(value);
} else {
element.classList.remove(value);
}
});
}
}
- 提醒:如果你给出一个以空格分隔的字符串,
classList.add/remove
将不起作用,它只会查看该字符串的第一部分。如果你期望你的类是类似于 "color color--blue" 的东西,你需要更改你对classList.add/remove
的使用。类似element.classList.add(...value.split(" "));
的东西应该可以处理无论字符串以什么格式提供。
英文:
For what you are trying to achieve, the Stimulus CSS Classes approach may not be the most suitable. It seems like you are setting up something more like a background preview picker.
You could use classes here but as noted, it becomes a bit complex to add more and more options. Classes are better used for things like 'loading/active' classes, a small set of discrete states that can be reflected also by class name changes.
Instead, you may want a Stimulus value approach here and something that is easier to map through to switch all things off except something that is selected.
I recommend a Stimulus Object
value, this is a JSON string that can be stored on the controller and can easily map key/value pairs.
Example
- In our HTML we set up the JSON value for all the color keys and their applicable classes. A reminder that this MUST be correctly formatted JSON and if you are using
"
for attributes, you will need to escape this correctly. Your rendering library should do this for you but if you are hand-writing this value, double check the output. - You can use the Stimulus action params also, making it easier to prepare a button or similar to toggle the colours.
- Finally, I recommend revising the name of your target from
colorKey
to something simpler as you likely have the wordcolor
already in the HTML in many places.
<div data-controller="bg-color" data-bg-color-classes-value='{"red": "something-red","blue":"something-borrowed","orange":"something-new","black":"bg-000000","green":"i-choose-green"}'>
<div data-bg-color-target="container">
CONTENT!
</div>
<button data-action="bg-color#show" data-bg-color-key-param="red">Go Red!</button>
<button data-action="bg-color#show" data-bg-color-key-param="green">Go Green!</button>
</div>
- In the JS, we wull out the
key
from theshow
method either at the CustomEventdetail
or the Action params. This could be done a few different ways, depending on how you plan to trigger things. - In the
show
method, we do a few checks for things before we attempt to mutate the DOM, returning early if something is missing. - We read out the entire Object value (Stimulus will convert this from JSON to an object), then map through the key/value pairs using
Object.entries
to either add the class or remove the class. This means that the only code needing to be changed if you wanted to add/remove colours is the HTML data attribute for the value. - The rest of the Controller is agnostic to how many colours you have, making this more reusable and flexible in the process.
class BgColor extends Controller {
static targets = ['container'];
static values = { classes: Object };
show(event) {
const { key } = event?.params || event?.detail || {}; // destructure key from either Stimulus action params for dispatched event detail.
const element = this.containerTarget;
const classes = this.hasClassesValue ? this.classesValue : {};
const currentColorClass = classes[colorKey];
if (!currentColorClass || !element) return; // ignore unmatched class silently (could throw an error also)
Object.entries(classes).forEach(([colorKey, value]) => {
if (key === colorKey) {
element.classList.add(value);
} else {
element.classList.remove(value);
}
});
}
}
- Reminder:
classList.add/remove
will not work if you give a space separated string, it only will look at the first part of that string. If you expect that your classes will be something like "color color--blue" you will need to change your usage ofclassList.add/remove
. Something likeelement.classList.add(...value.split(" "));
should work no matter what format the string comes in.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论