如何在 Svelte 中使用对象正确地预先 select 多个 select 输入?
How can I correctly pre-select a multiple select input in Svelte with objects?
在此示例组件中...
<script>
let availableValues = [
{id: 1, name: "one"},
{id: 2, name: "two"},
{id: 3, name: "three"},
{id: 4, name: "four"}
]
let selectedValues = [
{id: 1, name: "one"},
{id: 2, name: "two"}
]
</script>
<select multiple bind:value={selectedValues}>
{#each availableValues as value}
<option value={value}>
{value.name}
</option>
{/each}
</select>
值已正确 selected 并绑定到 selectedValues
。
然而,当第一次加载组件时,selectedValues
并没有反映在 select
输入中——换句话说,它们没有被绑定,因为 select输入似乎无法确定 selectedValues
是否与任何 availableValues
匹配。尽管如此,当 selecting(单击)多个值时,selectedValues
数组会按预期填充。
在其他框架中,可以传入一个函数来帮助“导出”selected 值,类似于 (foo) => foo.id == value.id
来解决对象相等性的歧义。在 Svelte 中,有一个类似的替代方案,使用 selected
属性,但它似乎并不能很好地解决这种特殊情况。
在 Svelte 中处理类似情况的最佳方法是什么?
更进一步,当初始 selectedValues
也恰好是外部绑定的 prop(即 <MyMultipleSelect bind:selectedValues={vals} />
)时,处理此问题的合理方法是什么?
您的方法在版本 3.42.2 之前一直有效,然后发生了什么变化
Deselect all <option>
s in a where the bound value doesn't match any of them
所以现在您正在比较两个不同的对象,这就是它不再起作用的原因。
这是一个解决方法,当然,如果您愿意
,您可以创建一个通过 属性 查找的函数
<script>
let availableValues = [
{id: 1, name: "one"},
{id: 2, name: "two"},
{id: 3, name: "three"},
{id: 4, name: "four"}
]
let selectedValues = [
{id: 1, name: "one"},
{id: 2, name: "two"}
]
let selected = JSON.stringify(selectedValues)
</script>
<select multiple bind:value={selected}>
{#each availableValues as value}
<option value={JSON.stringify(value)}>
{value.name}
</option>
{/each}
</select>
这是因为 javascript objects 相等性基于 reference(= 内存位置)而不是值。作为一个快速而有启发性的实验,打开您的浏览器控制台并评估 { a: 'foo', b: 0 } === { a: 'foo', b: 0 }
。是的,false
,因为这两个对象虽然值相等,但实际上是存储在内存中不同地址的不同实体。
那么,在评估多个 select 输入中预先 select 的选项时,您如何解决这个问题?嗯,你为你的 option
value
属性使用标量,因为 标量 等式 是 基于 值.
Sherif 的解决方案是一种方法(javascript 字符串,这是当您获得对象的 JSON 表示时得到的,实际上是标量),但考虑到您的对象有一个 id
属性,假设是唯一标识符,我会用它来代替:
<script>
let availableValues = [
{id: 1, name: "one"},
{id: 2, name: "two"},
{id: 3, name: "three"},
{id: 4, name: "four"}
]
let selectedIds = [1, 2]
$: selectedValues = availableValues.filter((v) => selectedIds.includes(v.id))
$: console.log(selectedValues)
</script>
<select multiple bind:value={selectedIds}>
{#each availableValues as value}
<option value={value.id}>
{value.name}
</option>
{/each}
</select>
编辑:select编辑值作为外部绑定属性
原理保持不变,但不是从组件内部设置 selectedIds
,而是将它们作为 prop 传递(据推测连同整套选项)。所以像这样:
// App.svelte
<script>
import { onMount } from 'svelte'
import MySelect from './MySelect.svelte'
let options = []
let selectedIds = []
onMount(async () => {
// set of options fetched from wherever (API, store, local/session storage, etc.), returns an array of objects similar to your 'availableValues' (each object MUST have an 'id' attribute)
options = await getOptions()
// currently selected values fetched from wherever (API, store, local/session storage, etc.), returns an array of ids (integers, UUID strings, etc., whatever matches the 'id' attribute of your options array)
selectedIds = await getCurrent()
}
$: selectedValues = options.filter((o) => selectedIds.includes(o.id))
</script>
<MySelect {options} bind:selectedIds={selectedIds} />
<ul>
{#each selectedValues as sv (sv.id)}
<li>{sv.name} [id: {sv.id}]</li>
{/each}
</ul>
//MySelect.svelte
<script>
export let options = [] // or hardcoded default values
export let selectedIds = [] // or hardcoded default values
</script>
<select multiple bind:value={selectedIds}>
{#each options as option}
<option value={option.id}>
{option.name}
</option>
{/each}
</select>
在此示例组件中...
<script>
let availableValues = [
{id: 1, name: "one"},
{id: 2, name: "two"},
{id: 3, name: "three"},
{id: 4, name: "four"}
]
let selectedValues = [
{id: 1, name: "one"},
{id: 2, name: "two"}
]
</script>
<select multiple bind:value={selectedValues}>
{#each availableValues as value}
<option value={value}>
{value.name}
</option>
{/each}
</select>
值已正确 selected 并绑定到 selectedValues
。
然而,当第一次加载组件时,selectedValues
并没有反映在 select
输入中——换句话说,它们没有被绑定,因为 select输入似乎无法确定 selectedValues
是否与任何 availableValues
匹配。尽管如此,当 selecting(单击)多个值时,selectedValues
数组会按预期填充。
在其他框架中,可以传入一个函数来帮助“导出”selected 值,类似于 (foo) => foo.id == value.id
来解决对象相等性的歧义。在 Svelte 中,有一个类似的替代方案,使用 selected
属性,但它似乎并不能很好地解决这种特殊情况。
在 Svelte 中处理类似情况的最佳方法是什么?
更进一步,当初始 selectedValues
也恰好是外部绑定的 prop(即 <MyMultipleSelect bind:selectedValues={vals} />
)时,处理此问题的合理方法是什么?
您的方法在版本 3.42.2 之前一直有效,然后发生了什么变化
Deselect all
<option>
s in a where the bound value doesn't match any of them
所以现在您正在比较两个不同的对象,这就是它不再起作用的原因。
这是一个解决方法,当然,如果您愿意
,您可以创建一个通过 属性 查找的函数<script>
let availableValues = [
{id: 1, name: "one"},
{id: 2, name: "two"},
{id: 3, name: "three"},
{id: 4, name: "four"}
]
let selectedValues = [
{id: 1, name: "one"},
{id: 2, name: "two"}
]
let selected = JSON.stringify(selectedValues)
</script>
<select multiple bind:value={selected}>
{#each availableValues as value}
<option value={JSON.stringify(value)}>
{value.name}
</option>
{/each}
</select>
这是因为 javascript objects 相等性基于 reference(= 内存位置)而不是值。作为一个快速而有启发性的实验,打开您的浏览器控制台并评估 { a: 'foo', b: 0 } === { a: 'foo', b: 0 }
。是的,false
,因为这两个对象虽然值相等,但实际上是存储在内存中不同地址的不同实体。
那么,在评估多个 select 输入中预先 select 的选项时,您如何解决这个问题?嗯,你为你的 option
value
属性使用标量,因为 标量 等式 是 基于 值.
Sherif 的解决方案是一种方法(javascript 字符串,这是当您获得对象的 JSON 表示时得到的,实际上是标量),但考虑到您的对象有一个 id
属性,假设是唯一标识符,我会用它来代替:
<script>
let availableValues = [
{id: 1, name: "one"},
{id: 2, name: "two"},
{id: 3, name: "three"},
{id: 4, name: "four"}
]
let selectedIds = [1, 2]
$: selectedValues = availableValues.filter((v) => selectedIds.includes(v.id))
$: console.log(selectedValues)
</script>
<select multiple bind:value={selectedIds}>
{#each availableValues as value}
<option value={value.id}>
{value.name}
</option>
{/each}
</select>
编辑:select编辑值作为外部绑定属性
原理保持不变,但不是从组件内部设置 selectedIds
,而是将它们作为 prop 传递(据推测连同整套选项)。所以像这样:
// App.svelte
<script>
import { onMount } from 'svelte'
import MySelect from './MySelect.svelte'
let options = []
let selectedIds = []
onMount(async () => {
// set of options fetched from wherever (API, store, local/session storage, etc.), returns an array of objects similar to your 'availableValues' (each object MUST have an 'id' attribute)
options = await getOptions()
// currently selected values fetched from wherever (API, store, local/session storage, etc.), returns an array of ids (integers, UUID strings, etc., whatever matches the 'id' attribute of your options array)
selectedIds = await getCurrent()
}
$: selectedValues = options.filter((o) => selectedIds.includes(o.id))
</script>
<MySelect {options} bind:selectedIds={selectedIds} />
<ul>
{#each selectedValues as sv (sv.id)}
<li>{sv.name} [id: {sv.id}]</li>
{/each}
</ul>
//MySelect.svelte
<script>
export let options = [] // or hardcoded default values
export let selectedIds = [] // or hardcoded default values
</script>
<select multiple bind:value={selectedIds}>
{#each options as option}
<option value={option.id}>
{option.name}
</option>
{/each}
</select>