Biding Vue-router params in a component - javascript

I have the following View :
<template>
<div class="home">
<Quiz :quizID="this.$route.params.id"/>
<p>{{this.$route.params.id}}</p>
</div>
</template>
The value of this.$route.params.id is correctly displayed between the <p> tags. But the component above doesn't get this value as a parameter. If I force it by writing <Quiz :quizID="3"/>, it works and the component Quizwith the corresponding ID is displayed.
But as soon I put a variable, nothing works anymore.
Edit : As suggested, here is the Quiz component
<div>
<div class="container flex justify-center mx-auto">
<div class="flex flex-col">
<div class="w-full">
<div class="border-b border-gray-200 shadow">
<table>
<tbody class="bg-white">
<tr class="whitespace-nowrap" v-for="player in players" :key="player.quizID">
<td class="px-6 py-4 text-sm text-gray-500" v-if="player.quizID === quizID">
{{player.quizID}}
</td>
<td class="px-6 py-4" v-if="player.quizID === quizID">
{{player.name}}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Quiz',
data(){
return {
quizzes: [],
players: []
}
},
props: [
'quizID'
],
methods: {
async fetchQuizzes() {
const res = await fetch(`http://127.0.0.1:8000/api/quizs`)
const data = await res.json()
return data.data
},
async fetchPlayers() {
const res = await fetch(`http://127.0.0.1:8000/api/players`)
const data = await res.json()
return data.data
}
},
async created(){
this.quizzes = await this.fetchQuizzes();
this.players = await this.fetchPlayers();
},
}
</script>
What's wrong?

Related

How to sync checkboxes between Vue 3 parent and child components

I have a Vue 3 InertiaJS app that contains a parent/child set of components where the parent component contains a form, along with a child component that searches for users and displays the results in a table where each row has a checkbox, so I can select users and then submit them as part of the form. What I need to do is sync the parent component with the checkbox values from the child component. Here is what I have.
Create.vue
<script setup>
import {Inertia} from '#inertiajs/inertia';
import {ref} from 'vue';
import {Head, Link, useForm} from '#inertiajs/inertia-vue3'
const form = useForm({
name: '',
checkedNames: []
});
const checkedNames = ref([])
const props = defineProps({
users: Object
})
</script>
<template>
<area-user-select
:users="props.users"
#searchUsers="searchUsers"
v-model:checkedNames="checkedNames"
></area-user-select>
</template>
AreaUserSelect.vue (child component)
<script setup>
import { Head } from '#inertiajs/inertia-vue3';
import Icon from '#/Components/Icon.vue';
import {ref} from 'vue';
const props = defineProps({
users: Object,
filters: Object,
checkedNames: Array
})
</script>
<template>
<div>
...
<div v-if="users.data.length > 0">
<table class="table-auto">
<thead>
<tr class="text-left font-bold">
<th class="pb-4 pt-6 px-6 text-sm">Select</th>
<th class="pb-4 pt-6 px-6 text-sm">Name</th>
<th class="pb-4 pt-6 px-6 text-sm">Last Name</th>
<th class="pb-4 pt-6 px-6 text-sm">Member Number</th>
<th class="pb-4 pt-6 px-6 text-sm">Status</th>
</tr>
</thead>
<tbody>
<tr v-for="user in users.data" :key="user.id" class="hover:bg-gray-100 focus-within:bg-gray-100">
<td class="border-t">
<input
type="checkbox"
name="selected"
:value="user.id"
#input="$emit('update:checkedNames', $event.target.value)"
/>
</td>
<td class="border-t">
{{ user.first_name }}
</td>
<td class="border-t">
{{ user.last_name }}
</td>
<td class="border-t">
{{ user.member_number }}
</td>
<td class="border-t">
{{ user.status }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
This works, sort of. In my parent component, if I display the value of checkedName it only contains the value of the last item checked, not an array of all selected values.
What do I need to change so that all selected items are synced back to the parent (Create.vue) component?
You can do it this way:
Bind your parent to your component with 'v-model':
<my-component ... v-model="checkedNames"/>
Docs: Component v-model
Bind your checkboxes to a internal component property:
<input type="checkbox" ... v-model="checked" />
Docs: Form Input Bindings - Checkbox
End send updates with watch to the parent:
watch(checked, (newVal) => { emit('update:modelValue', newVal) });
Docs: Watchers
Here is the playground:
const { createApp, ref, watch } = Vue;
const users = [
{ id: 1, name: "User 1" },
{ id: 2, name: "User 2" }
];
const myComponent = {
props: ['items'],
emits: ['update:modelValue'],
setup(props, { emit }) {
const checked = ref([]);
watch(checked, (newVal) => { emit('update:modelValue', newVal) });
return {emit, checked}
},
template: '#my-checkbox'
}
const App = {
components: {
myComponent
},
setup() {
const checkedNames = ref([]);
const setCheckedNames = (newVal) => checkedNames.value = newVal;
return { users, checkedNames, setCheckedNames }
}
}
const app = createApp(App)
app.mount('#app')
<div id="app">
Checked: {{checkedNames}}<br /><br />
<my-component :items="users" v-model="checkedNames"/>
</div>
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<script type="text/x-template" id="my-checkbox">
<div v-for="user in items">
<input type="checkbox" name="selected" :value="user.id" v-model="checked" />
<label>{{user.name}}</label>
</div>
</script>

Method "getChampionName" has type "undefined" in the component definition. Did you reference the function correctly?

I'm trying to use this function (src/api/)
function getChampionName(champId) {
axios.get('http://ddragon.leagueoflegends.com/cdn/12.5.1/data/en_US/champion.json')
.then(({ data }) => {
let list = data
let championList = list.data
for (var i in championList) {
if (championList[i].key == champId) {
return championList[i].id
}
}
})
.catch((err) => console.log(err))
}
export {getChampionName}
In this component (src/components/)
<template>
<div class="w-72">
<header class="rounded-tl-lg rounded-tr-lg bg-slate-400 p-0.5">
<p>Champion's Mastery</p>
</header>
<ul class="grid gap-1 rounded-bl-lg rounded-br-lg bg-slate-50 p-0.5">
<li v-for="champ in masteryData.slice(0,10)" :key="champ.championId">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<img src="https://ddragon.leagueoflegends.com/cdn/12.5.1/img/champion/Karma.png" alt="" class="rounded-lg h-14 w-14">
<div>
<p class="font-medium text-center">{{ getChampionName(champ.championId) }}</p>
<p>Level {{ champ.championLevel }}</p>
</div>
</div>
<p class="text-2xl font-medium">{{ champ.championPoints }} Pts</p>
</div>
</li>
</ul>
</div>
</template>
<script>
import getChampionName from '#/api/search'
export default{
name: 'MasteryInfo',
props: [
'masteryData'
],
methods: {
getChampionName
}
}
</script>
But I'm getting this error Method "getChampionName" has type "undefined" in the component definition. and don't know what does it mean.
It seems you didn't import the method properly.
Change the import into:
import { getChampionName } from '#/api/search';
You can read more about import export in javascript here:
https://javascript.info/import-export
If you think you almost have the same question as this question, you can also refer to this:
Method "showDate" has type "undefined" in the component definition

Unable to Update the details in the UI on Button Click using React

I am Having the Names in the table , So now I want to reverse the names(Reverse a String) and I want to update the names that in the UI
So, I have one function to reverse the names in table and It is working fine , When I console log it, I am getting the Reversed names...But I am Unable to update that in the UI...
Can Anyone please help me out in this...Thanks in Advance
const Arraymethods1 = () => {
const [data, setdata] = useState([]);
const [getData, setgetData] = useState([{}]);
useEffect(() => {
Axios.get('http://localhost:3000/results').then(res =>{
setdata(res.data);
const tempArray=[];
let allData =res.data;
//console.log(allData);
for(let i=0;i<allData.length;i++){
//console.log(allData[i])
const dataObject ={
name : `${allData[i].name.first}-${allData[i].name.last}`,
price : allData[i].price.money.wealth
}
// console.log(dataObject);
tempArray.push(dataObject);
//console.log(tempArray);
}
setgetData(tempArray);
})
}, [])
Thsi is the Function to revesre the names
const RevereName =() =>{
//console.log(getData);
for(let i=0;i<getData.length;i++){
let reversing =getData[i].name;
//console.log(reversing);
let rev ="";
for(let i=reversing.length-1;i>=0;i--){
rev+=reversing[i];
}
console.log(rev);
setgetData([rev]);
}
}
return (
<React.Fragment>
<h1 className="text-info m-3 true">LordShiva</h1>
<div className="container">
<div className="card">
<div className="row">
<div className="col md-col-4">
<div className="card-header font-weight-bold text-centre getsome">ArrayButtons</div>
<div className="bg-dark text-centre">
<button className="btn btn-warning" onClick={RevereName} >Reverse</button>
</div>
</div>
<div className="col md-col-6">
<div className="card-header font-weight-bold text-centre getsome">ArrayValues</div>
<div className="table bg-dark">
{/* <p className="text-primary font-weight-bold p-3 md-col-6 shaping">Person</p>
<p className="text-primary font-weight-bold p-3 md-col-6 shape">Wealth</p> */}
<table className="table">
<thead className="text-primary p-3 shaping ">
<tr>
<th><strong>Person</strong></th>
<th><strong>Wealth</strong></th>
</tr>
</thead>
<tbody>
<div className="text-primary">
<tr>
{
getData.map((user)=>{
return (
<div>
<td className="font-weight-bolder text-capitalize">{user.name}</td>
<td className="font-weight-bolder treu">{user.price}</td>
</div>
)
})
}
</tr>
</div>
</tbody>
<p className="text-primary font-weight-bolder p-3" ><strong>Total Wealth</strong>{total}</p>
</table>
</div>
</div>
</div>
</div>
</div>
</React.Fragment>
)
}
getData is an array of Objects, not an array of strings. If you set it to an array of strings, user.name and user.price will be undefined.
So a fix would be
const RevereName=()=>{
let getData2=[];
for(let i=0;i<getData.length;i++){
let reversing =getData[i].name;
//console.log(reversing);
let rev ="";
for(let i=reversing.length-1;i>=0;i--){
rev+=reversing[i];
}
console.log(rev);
getData2.push({name: rev, price: getData[i].price});
}
setgetData([...getData2]);
}

Checkbox functionality in VueJS

I'm trying to build an application in VueJS where I'm having some checkbox that gets selected on clicking.
My UI looks like this:
If we try to select/de-select individual checkbox it is working fine. If we check/uncheck via the select all button it working perfectly fine. But when we select all and try to uncheck with the individual checkbox it is slicing the actual checkbox list
As you can see in the above image I tried unchecking the RTY option and it got removed from the child component's variables array
My Concept:
I'm having a parent element where I'm passing the selections as props to this checkbox element:
<template>
<div>
<selection-component :selected_fields="selected_fields"></selection-component>
</div>
</template>
<script>
import selectionComponent from './../selection-component'
import {eventBus} from "../../../../../models/_events";
export default {
name: "MainLayout",
components: {
selectionComponent
},
data(){
return{
selected_fields:{},
}
},
created() {
//Fetch selections via API (axios) call
//capturing events from child components (selection-component)
eventBus.$on('admin_watercraft_filter', (data) => {
if(typeof data.var_name !== 'undefined') {
if(typeof data.data !== 'undefined' && data.data!=='' && data.data.length!==0)
this.selected_fields[data.var_name] = data.data;
else if(typeof this.selected_fields[data.var_name] !== 'undefined' || data.data === '' || data.data.length===0 )
delete this.selected_fields[data.var_name];
}
else this.selected_fields = data;
console.log(this.selected_fields);
});
eventBus.$on('admin_watercraft_create', (data) => {
if(typeof this.selected_fields[data.var_name] === 'undefined') {
this.selected_fields[data.var_name] = [];
this.selected_fields[data.var_name].push(data.data);
}
else this.selected_fields[data.var_name].push(data.data);
});
eventBus.$on('admin_watercraft_remove', (data) => {
var index = _.findIndex(this.selected_fields[data.var_name], (q) => {
return q.id === data.data.id
})
if(index > -1) this.selected_fields[data.var_name].splice(index, 1);
});
}
}
</script>
And I have child component selection-component as:
<template>
<div class="mt-3 lg:mt-0">
<div class="flex justify-between items-center mb-3">
<div class="flex items-center">
<input #click="selectAll($event)" :checked="selectAllChecked" type="checkbox" class="h-6 w-6 rounded-md bg-gray-200 border-none">
<div class="ml-2">
<div class="text-sm font-normal">Select Checkbox</div>
<div class="font-extralight text-gray-400" style="font-size: 11px">Assign by checking</div>
</div>
</div>
<div class="flex">
<div class="h-8 rounded-l-lg w-8 bg-gray-100 flex">
<div class="m-auto text-sm font-semibold ">$</div>
</div>
<input v-model="price" style="width: 70px" class="h-8 px-2 text-sm font-normal rounded-r-lg border focus:outline-none focus:border-gray-400">
</div>
</div>
<div class="">
<input v-model="search" type="search" class="text-sm font-normal px-4 py-2 my-3 border-none rounded-xl w-full bg-gray-100 focus:outline-none focus:bg-gray-200" placeholder="Search...">
<div class="overflow-y-auto h-40">
<div v-for="(ele, index) in tableData" class="flex items-center justify-between my-1 pr-3">
<div class="flex items-center">
<input :value="ele.id" #click="selectData(ele, $event)" :checked="ele.selected" type="checkbox" class="h-6 w-6 rounded-md bg-gray-200 border-none">
<div class="text-xs font-normal ml-2">{{ele.name}}</div>
</div>
<div class="flex">
<div class="h-8 rounded-l-lg w-10 bg-gray-100 flex">
<div class="m-auto text-sm font-semibold ">$</div>
</div>
<input v-model="ele.price" style="width: 75px" class="h-8 px-4 text-sm font-normal rounded-r-lg border focus:outline-none focus:border-gray-400">
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import {eventBus} from "../../../../../models/_events";
export default {
name: "SelectionComponent",
props:['selected_fields'],
data(){
return{
variables: [
{id:1, name:'ABC', price:''},
{id:2, name:'QWE', price:''},
{id:3, name:'RTY', price:''},
],
var_name:'source_file',
price:''
}
},
methods:{
selectData(ele, event) {
const dataset = {
var_name: this.var_name,
data: ele
}
if(event.target.checked) eventBus.$emit('admin_watercraft_create', dataset);
else eventBus.$emit('admin_watercraft_remove', dataset);
},
selectAll(event) {
let dataSet
if(event.target.checked) dataSet = {var_name: this.var_name, data: this.variables}
else dataSet = {var_name: this.var_name, data: []}
eventBus.$emit("admin_watercraft_filter", dataSet);
},
checked(element){
if(typeof this.selected_fields[this.var_name] === 'undefined') return false;
return this.selected_fields[this.var_name].some(el => el.id === element.id);
},
},
computed: {
tableData() {
if (typeof this.variables !== 'undefined' && this.variables.length) {
return this.variables.map(a => ({
name: a.name,
id: a.id,
price: a.price,
selected: this.checked(a)
}))
}
},
selectAllChecked(){
if(typeof this.selected_fields[this.var_name] === 'undefined') return false;
return this.variables.length === this.selected_fields[this.var_name].length
}
}
}
</script>
don't know why it is splicing the actual variable inside the child component. I need to keep the state of variables array defined inside the child component.

#click within v-for triggers all #click events

When a single #click is triggered from within the v-for the display property in all of the objects within the array is updated, rather than just by index. I only want that element that is click to receive the event and update the display property, not all of them.
<script>
import TransitionExpand from '#/transitions/transition-expand'
import ResourceCard from '#/components/resource-card'
export default {
components: {
TransitionExpand,
ResourceCard
},
data () {
return {
}
},
methods: {
toggle (evt, i) {
this.status[i].display = !this.status[i].display
}
},
async asyncData ({ app }) {
const { data } = await app.$axios.get('training_modules')
const status = Array(data.trainingModules.length).fill({ display: false })
return {
modules: data.trainingModules,
status
}
}
}
</script>
<template>
<div>
<div class="container px-4 mx-auto mt-16 lg:mt-32">
<div class="flex flex-wrap mb-20">
<h1 class="w-full lg:w-1/2 mb-6">Training Modules</h1>
<p class="w-full lg:w-1/2 leading-7 text-abbey"></p>
</div>
<div
v-for="(item, i) in modules"
:key="item.id"
class="mb-12"
>
<div class="flex items-center">
<h2>{{ item.title }}</h2>
<img #click="toggle($event, i)" class="ml-auto cursor-pointer" src="#/assets/images/icons/plus.svg" alt="open">
</div>
<TransitionExpand v-show="status[i].display">
<div class="">
<p class="mt-6 mb-12">{{ item.description }}</p>
<div class="flex flex-wrap -m-3">
<ResourceCard
v-for="resource in item.resources"
:key="resource.id"
:resource="resource"
/>
</div>
</div>
</TransitionExpand>
</div>
</div>
<BaseFooter />
</div>
</template>
Problem is in const status = Array(data.trainingModules.length).fill({ display: false }) code which is filing all array items with the same object which is observable hence change in display property will be applied to all elements in the array as status[0] === status[1].
Use map instead and it will work as expected:
const status = data.trainingModules.map(() => ({ display: false }));

Categories

Resources