cra*_*dev 1 javascript vue.js vuejs3
我的数组中有对象(产品)。每个产品都有自己的评级,评级是来自数据库的数字。我在产品本身上显示每个产品的四舍五入平均评分。
\n{{ Math.round(Object.values(product.rating)[0]) }}\nRun Code Online (Sandbox Code Playgroud)\n我想显示人们用来评价产品的星星(单选按钮)的平均值。如果我点击对产品进行评分,则应检查与当前评分匹配的星星数量。在单个产品上,我可以只使用v-model,但是如果列表中有一堆产品并且每个产品都有不同的评级,我该怎么办?
每个单选按钮的值是 ID 和 value 属性。如何将单选按钮的 ID 或值与产品的当前评级相匹配?
\n没有this,所以我尝试这样做,但显然不起作用:
<div class="rating">\n <input\n type="radio"\n value="5"\n id="5"\n :checked="\n this.value ==\n Math.round(Object.values(product.rating)[0])\n "\n @change="rateproduct"\n /><label for="5">\xe2\x98\x86</label>\n <input\n type="radio"\n value="4"\n id="4"\n @change="rateproduct"\n :checked="\n this.value ==\n Math.round(Object.values(product.rating)[0])\n "\n /><label for="4">\xe2\x98\x86</label>\n <input\n type="radio"\n value="3"\n id="3"\n @change="rateproduct"\n :checked="\n this.value ==\n Math.round(Object.values(product.rating)[0])\n "\n /><label for="3">\xe2\x98\x86</label>\n <input\n type="radio"\n value="2"\n id="2"\n @change="rateproduct"\n :checked="\n this.value ==\n Math.round(Object.values(product.rating)[0])\n "\n /><label for="2">\xe2\x98\x86</label>\n <input\n type="radio"\n value="1"\n id="1"\n @change="rateproduct"\n :checked="\n this.value ==\n Math.round(Object.values(product.rating)[0])\n "\n /><label for="1">\xe2\x98\x86</label>\n</div> \nRun Code Online (Sandbox Code Playgroud)\n我尝试这样做,但它也不起作用:
\n<input\n type="radio"\n value="5"\n id="5"\n :checked="\n Math.round(Object.values(post.rating)[0])\n ? 'checked'\n : ''\n "\n @change="ratePost"\n /><label for="5">\xe2\x98\x86</label>\n</input>\nRun Code Online (Sandbox Code Playgroud)\n这是我想要完成的任务,但它不起作用,并且只选择了最后一个帖子评级
\n<template>\n <div class="postsList">\n <div class="post" v-for="post in posts" :key="post.id">\n <div class="title">Title: {{post.title}}</div>\n <div class="currentrating">Rating: {{post.rating}}</div>\n <div class="vote">\n <div class="rating">\n <div class="star" v-for="index in stars" :key="index">\n <input\n type="radio"\n name="stars"\n :value="index"\n v-model="post.rating"\n @change="ratePost"\n /><label>\xe2\x98\x86</label>\n </div>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script setup>\n import {ref} from 'vue'\n const stars = 5;\n const posts = ref([\n { id:1, title: "post1", rating: 2 },\n { id:2, title: "post2", rating: 3 },\n { id:3, title: "post3", rating: 2 },\n { id:4, title: "post4", rating: 5 },\n { id:5, title: "post5", rating: 2 },\n { id:6, title: "post6", rating: 1 },\n { id:7, title: "post7", rating: 2 },\n { id:8, title: "post8", rating: 3 },\n { id:9, title: "post9", rating: 4 },\n { id:10, title: "post10", rating: 1 },\n ]);\n</script>\n\n<style scoped>\n .post{\n padding:10px;\n }\n .rating{\n display:flex;\n margin-bottom: 10px;\n }\n .title{\n margin-bottom:10px;\n }\n .currentrating{\n margin-bottom:10px\n }\n</style>\nRun Code Online (Sandbox Code Playgroud)\n
一种可能的解决方案:
\n\n\n<template>\n <fieldset>\n <legend>Average Star Rating</legend>\n <div class="radios">\n <div class="radioGroup" v-for="index in MAX_VALUE" :key="index">\n <input \n type="radio" \n name="stars" \n :value="index" \n v-model="starValue" \n @click.prevent=""\n />\n <label>{{ index }} {{ starText(index) }}</label>\n </div>\n </div>\n <div>\n Average Rating: {{ roundedAverageValue }}\n </div>\n </fieldset>\n <fieldset>\n <legend>Individual Item Ratings</legend>\n <div v-for="(slider, index) in sliderValues" :key="index" >\n <label for="slider.name" class="sliderLabel">{{ slider.name }}</label>\n <input \n type="range" \n name="starSlider" \n :min="MIN_VALUE" \n :max="MAX_VALUE" \n v-model="slider.value"\n >\n Value: {{ slider.value }}\n </div>\n <div>\n Average: {{ averageValue }}\n </div>\n </fieldset>\n\n</template>\n\n<script setup>\nimport { ref, watch, computed } from \'vue\';\n\nconst MIN_VALUE = 1;\nconst MAX_VALUE = 5;\n\nconst starValue = ref(1);\nconst sliderValues = ref([\n { \n value: MIN_VALUE,\n name: \'Item 1\',\n },\n { \n value: MIN_VALUE,\n name: \'Item 2\',\n },\n { \n value: MIN_VALUE,\n name: \'Item 3\',\n },\n { \n value: MIN_VALUE,\n name: \'Item 4\',\n },\n]);\n\nconst starText = (index) => {\n return index > 1 ? \'stars\' : \'star\';\n}\n\nconst averageValue = computed(() => {\n let sum = 0;\n sliderValues.value.forEach((sv) => {\n sum += parseInt(sv.value);\n })\n return sum / sliderValues.value.length;\n});\n\nconst roundedAverageValue = computed(() => {\n return Math.round(averageValue.value);\n});\n\nwatch(roundedAverageValue, (newValue) => {\n starValue.value = newValue;\n})\n</script>\n\n<style scoped>\nfieldset {\n margin: 20px 70px;\n text-align: center;\n}\n\n.radioGroup {\n display: inline-block;\n text-align: center;\n margin: 10px;\n}\n\n.radioGroup label {\n display: block;\n}\n\n.sliderLabel {\n margin-right: 8px;\n}\n</style>\nRun Code Online (Sandbox Code Playgroud)\n解释:
\n这使用 v-for 从 1 到 5 创建无线电输入:v-for="index in 5"。每个radio的值是for循环的int索引。所有收音机的型号都是相同的,starValue
然后,我使用对象数组来创建滑块,并使用计算属性来获取平均值,并使用计算属性上的观察器来更改无线电的模型值(因为在计算属性中产生副作用是不好的做法)财产)。
\n请注意,您最近发布的代码在模板代码的这一部分中存在错误:
\n<div class="post" v-for="post in posts" :key="post.id">\n <div class="title">Title: {{post.title}}</div>\n <div class="currentrating">Rating: {{post.rating}}</div>\n <div class="vote">\n <div class="rating">\n <div class="star" v-for="index in stars" :key="index">\n <input\n type="radio"\n name="stars"\n :value="index"\n v-model="post.rating"\n @change="ratePost"\n /><label>\xe2\x98\x86</label>\n </div>\n </div>\n </div>\n</div>\nRun Code Online (Sandbox Code Playgroud)\n此行:name="stars"对于外部 v-for 循环的每次迭代都是相同的,这意味着您创建的每个无线电输入都是相同的无线电组。由于单选组一次只能显示一个选择,因此仅显示一颗星被选择。
解决方案很简单:为每个恒星集合提供一个唯一的无线电输入名称属性。您需要绑定 name 属性并可以使用 post.id 来确保唯一性,因此更改此:
\nname="stars"
对此:
\n:name="stars + post.id"
并且您发布的代码应该可以工作。
\n例如:
\n<div class="post" v-for="post in posts" :key="post.id">\n <div class="title">Title: {{post.title}}</div>\n <div class="currentrating">Rating: {{post.rating}}</div>\n <div class="vote">\n <div class="rating">\n <div class="star" v-for="index in stars" :key="index">\n <input\n type="radio"\n :name="stars + post.id"\n :value="index"\n v-model="post.rating"\n @change="ratePost"\n /><label>\xe2\x98\x86</label>\n </div>\n </div>\n </div>\n</div>\nRun Code Online (Sandbox Code Playgroud)\n我的答案的最新版本现在创建了一个单独的评级面板,显示 5 颗星悬停效果,并将填充所有星星,直至并包括所选评级。为了实现这一点,我必须从 using 更改为input[type="radio"]using input[type="checkbox"]It,然后允许其他组件使用该组件,将面板的标题作为 prop 传递,并将其值作为v-model.
<template>\n <div class="star-panel">\n <fieldset class="flex-row">\n <legend>{{ title }}</legend>\n <div class="radio-wrapper">\n <div class="radios">\n <div class="radioGroup" v-for="star in stars" :key="star.value">\n <input\n type="checkbox"\n name="stars"\n :value="star.value"\n v-model="star.checked"\n :id="title + star.text"\n @change="selectedItem(star.value)"\n /><label class="star-label" :for="title + star.text">\n {{ starChar(star.checked) }}</label\n >\n </div>\n </div>\n <div class="text-display">Rating: {{ stars[value - 1].text }}</div>\n </div>\n </fieldset>\n </div>\n</template>\nRun Code Online (Sandbox Code Playgroud)\n<script setup>\nimport { computed, onMounted, ref } from "vue";\n\nconst props = defineProps(["title", "modelValue"]);\nconst emit = defineEmits(["update:modelValue"]);\nconst value = computed({\n get() {\n return props.modelValue;\n },\n set(value) {\n emit("update:modelValue", value);\n },\n});\n\nconst stars = ref([\n {\n text: "1 stars",\n value: 1,\n checked: false,\n },\n {\n text: "2 stars",\n value: 2,\n checked: false,\n },\n {\n text: "3 stars",\n value: 3,\n checked: false,\n },\n {\n text: "4 stars",\n value: 4,\n checked: false,\n },\n {\n text: "5 stars",\n value: 5,\n checked: false,\n },\n]);\n\nconst selectedItem = (index) => {\n value.value = index;\n\n stars.value.forEach((star) => {\n star.checked = index >= star.value;\n });\n};\n\nconst FILLED_STAR = String.fromCharCode(9733);\nconst EMPTY_STAR = String.fromCharCode(9734);\n\nconst starChar = (checked) => {\n if (checked) {\n return FILLED_STAR;\n } else {\n return EMPTY_STAR;\n }\n};\n\nonMounted(() => {\n value.value = props.modelValue;\n\n stars.value.forEach((star) => {\n star.checked = props.modelValue >= star.value;\n });\n});\n</script>\nRun Code Online (Sandbox Code Playgroud)\n<style scoped>\n.star-panel {\n max-width: 400px;\n}\n.star-label {\n font-size: 30px;\n color: orange;\n}\n\n.star-label:hover {\n color: rgb(173, 113, 2);\n font-weight: bold;\n}\ninput[type="checkbox"] {\n display: none;\n}\n.radio-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n}\n.radios {\n display: flex;\n align-items: center;\n}\n.radioGroup {\n margin: 2px 15px;\n flex: 0 1 auto;\n}\n</style>\nRun Code Online (Sandbox Code Playgroud)\n然后是持有多个RatingPanel的组件:
\n<template>\n <div class="post-list" v-for="post in posts" :key="post.id">\n <SingleRatingPanel :title="post.title" v-model="post.rating" />\n </div>\n</template>\n\n<script setup>\nimport { ref, watch } from "vue";\nimport SingleRatingPanel from "./RatingPanel.vue";\n\nconst posts = ref([\n { id: 1, title: "Post 1", rating: 2 },\n { id: 2, title: "Post 2", rating: 3 },\n { id: 3, title: "Post 3", rating: 2 },\n { id: 4, title: "Post 4", rating: 5 },\n { id: 5, title: "Post 5", rating: 2 },\n { id: 6, title: "Post 6", rating: 1 },\n { id: 7, title: "Post 7", rating: 2 },\n { id: 8, title: "Post 8", rating: 3 },\n { id: 9, title: "Post 9", rating: 4 },\n { id: 10, title: "Post 10", rating: 1 },\n]);\n\n// to demonstrate efficacy:\nwatch(\n posts,\n (newValue) => {\n newValue.forEach((p) => console.log(p));\n },\n { deep: true }\n);\n</script>\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
974 次 |
| 最近记录: |