英文:
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
colorKeyto something simpler as you likely have the wordcoloralready 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
keyfrom theshowmethod either at the CustomEventdetailor the Action params. This could be done a few different ways, depending on how you plan to trigger things. - In the
showmethod, 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.entriesto 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/removewill 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论