Make a link list component of a link component in Vue - vue.js

I've just made a Link component in Vue, which is new for me. Now I need to make a Link list component, which is basically an UL of LI elements of this Link component. I looked for a solution, but most most of the examples do work with a local array/object and I can't seem to figure it out.
So my question here is: What would a Link list component look like, making use of the existent Link component?
Here's the Link component:
<template>
<nuxt-link
:to="localePath(url)"
:class="$style.link"
>
<Icon
v-if="icon"
class="icon icon--size:2.5x"
:name="icon"
/>
<span
class="text"
>
{{ text }}
</span>
</nuxt-link>
</template>
<script src="./index.js"></script>
<style type="text/css" src="./index.module.scss" module lang="scss"></style>
import { Icon } from "#/components";
export default {
props: {
text: {
type: String,
required: true,
default: "",
},
url: {
type: String,
required: true,
default: "",
},
icon: {
type: String,
required: true,
default: "",
},
expert: {
type: Boolean,
required: false,
default: false,
},
},
components: {
Icon,
},
};
Should the Link list component receive an array as well like this?
export default {
props: {
list: {
type: Array,
required: true,
default: "",
},
},
Link,
},
};

Assuming you have an array of link objects that provide the props text, url, icon and optionally expert (because I can't see where you're using that), if your goal is to pass this array as a prop to your Link List component, then it would look something like this
<template>
<ul>
<!-- note the ":key" binding. This should uniquely identify each entry in the list -->
<li v-for="({ text, url, icon }) in list" :key="url">
<Link
:text="text"
:url="url"
:icon="icon"
/>
</li>
</ul>
</template>
<script>
import { Link } from '#/components' // or whatever makes sense
export default {
name: 'LinkList',
components: { Link },
props: {
list: Array
}
}
</script>
Using this would look something like
<template>
<LinkList :list="list" />
</template>
<script>
import { LinkList } from '#/components'
export default {
components: { LinkList },
data: () => ({
list: [{
text: 'Link #1',
url: 'https://example.com',
icon: 'whatever'
}, {
// and so on
}]
})
}
</script>

Related

Nuxt Dynamic V-model

I'm having difficulty binding my custom input to data(). I've tried several combinations to try to get it working and so far only the placeholder seems to work. I created an array called questions and its content is dynamically rendered to the page. On page load, my code determines if this is either a user or business account and then sets the value of the questions array based on the result which works fine. I created a test function to test if the v-model binding is working but I get an empty alert. I find it strange that the placeholder works just fine but not the v-modal bind.
<template>
<section>
<form>
<BaseInput v-for="question in questions"
v-model="question.bind" :placeholder="question.placeholder"/>
</form>
<button #click="test"></button>
</section>
</template>
<script>
import BaseInput from '../BaseInput.vue'
export default {
components: {
BaseInput,
},
data(){
return{
firstName: '',
lastName: '',
commercialName: '',
businessName: '',
questions: [],
userQuestionsArray: [
{ bind: 'firstName', placeholder: 'First Name' },
{ bind: 'lastName', placeholder: 'Last Name' },
],
businessQuestionsArray: [
{ bind: 'commercialName', placeholder: 'Commercial Name' },
{ bind: 'businessName', placeholder: 'Business Name' },
]
}
}
},
methods: {
test(){
alert(this.password)
}
},
mounted() {
if(this.$store.state.userType === 'Personal'){
this.questions = this.userQuestionsArray;
}else {
this.questions = this.businessQuestionsArray;
}
},
computed: {
userType: {
get () {
return this.$store.state.userType
}
}
}
}
</script>
you cant use v-mode in v-for. you must use wrapper like template or tag over each input.
<template>
<section>
<form v-for="(question, index) in questions" :key="index">
<BaseInput v-model="question.bind" :placeholder="question.placeholder"/>
</form>
<button #click="test"></button>
</section>
</template>

VueJs Pass array of object to child component do not refresh on changes

I'm trying to pass an array of object to a childComponent as prop but when I add an object in it, it doesn't render. (Note: I'm working on vuejs 2.6)
I suppose it has a link with the "monitoring" of the items of the array and not the array itself? Stuff is that if I do not pass the prop and use the default value instead, it's working perfectly. I think I'm missing something here. Could someone help me ?
By curiosity is this kind of behavior still stand with vue3js ?
As you can see below:
App.vue:
<template>
<div id="app">
<Card
v-for="user in users"
:key="user.userId"
:userId="user.userId"
:username="getUsernameFromUserId(user.userId)"
:links="getUserLinksFromUserId(user.userId)"
/>
</div>
</template>
<script>
import Card from "./components/Card.vue";
export default {
name: "App",
components: {
Card,
},
data: function () {
return {
users: [
{ userId: 1, name: "Bob" },
{ userId: 2, name: "Alice" },
{ userId: 3, name: "Eliot" },
],
links: [
{ userId: 1, link: "hello->world" },
{ userId: 1, link: "world->!" },
{ userId: 3, link: "hello->back" },
{ userId: 4, link: "hello->you" },
],
};
},
methods: {
getUsernameFromUserId: function (userId) {
return this.users.filter((obj) => obj.userId == userId)?.[0]?.name ?? "Not found";
},
getUserLinksFromUserId: function (userId) {
return this.links.filter((obj) => obj.userId == userId);
},
},
};
</script>
Card.vue
<template>
<div class="card">
<h1>{{ username }}</h1>
<button #click="addLink">Add One link</button><br><br>
<span v-if="links.length == 0">No links</span>
<div class="links">
<Link v-for="link in links" :key="links.indexOf(link)" :link="link"></Link>
</div>
</div>
</template>
<script>
import Link from '../components/Link'
export default {
components:{Link},
props: {
userId: Number,
username: String,
links: { type: Array, default: () => [], required: false },
},
methods:{
addLink: function(){
this.links.push({
userId: this.userId,
link: 'newlink->cool'
});
}
}
}
</script>
Link.vue
<template>
<div>
<span>UserId: {{ this.link.userId }} Link: {{ this.link.link }</span>
</div>
</template>
<script>
export default {
props: {
link: { type: Object, default: () => [], required: false },
},
};
</script>
This is a bad way to work with props
Note: do not focus on Dev Tools too much as it can be "buggy" at times - especially if you use Vue in a wrong way. Focus on your app output
Your Card.vue component is modifying (push) a prop, which is not recommended but it sort of works if the prop is object/Array and you do not replace it, just modify it's content (as you do)
But in your case, the values passed to props are actually generated by a method! The getUserLinksFromUserId method is generating a new array every time it is called, and this array is NOT reactive. So by pushing to it, your component will not re-render and what is worse, parent's links array is not changed at all! (on top of that - if App.vue ever re-renders, it will generate new arrays, pass it to pros and your modified arrys will be forgoten)
So intead of modifying links prop in Card.vue, just emit an event and do the modification in App.vue

Add or removing a class to a element in a child component

I've recently hit a wall with vue. Currently we three components Parent.vue ChildOne.vue and ChildTwo.vue. Parent Passes some object data (from a state) to the ChildOne component.
The ChildOnecomponent has the ChildTwo component as a custom element. On that custom element <ChildTwo></ChildTwo> we have a v-for loop that loops through the data and the ChildTwo component is just for html formatting the data.
What I want to is on a click add a custom class .selected to a div and remove it from the other div if selected, much like my example here.
https://codepen.io/david-wh/pen/gOrzQVR
Here is a sandbox of the full app I'm using:
https://codesandbox.io/s/onclick-forked-tkkre
Parent
<template>
<div>
<ChildOne :container-slot-data="ObjectData"></ChildOne>
</div>
</template>
<script>
import ChildOne from "./childComponentOne.vue";
export default {
name: "HelloWorld",
components: {
ChildOne
},
data() {
return {
ObjectData: [
{
id: "1",
name: "foo",
type: "aug",
subType: "1"
},
{
id: "2",
name: "bar",
type: "aug",
subType: "2"
},
{
id: "3",
name: "foobar",
type: "gear",
subType: "6"
}
]
};
}
};
</script>
ChildOne
<template>
<div class="Testing">
<ChildTwo v-for="(itemData, index) in containerSlotData" :key="index" :item-data="itemData"></ChildTwo>
</div>
</template>
<script>
import ChildTwo from "./childComponentTwo.vue";
export default {
name: "ChildOne",
components: {
ChildTwo
},
data() {
return {
current: null
};
},
props: {
containerSlotData: {
type: Array,
default: null
}
}
};
</script>
ChildTwo
<template>
<!-- <div class="selected"> -->
<div>
<ul>
<li>{{ itemData.id }}</li>
<li>{{ itemData.name }}</li>
<li>{{ itemData.subType }}</li>
</ul>
<hr>
</div>
</template>
<script>
export default {
name: "Childtwo",
data() {
return { current: null };
},
props: {
itemData: {
type: Object,
default: null
}
}
};
</script>
If all you want to do is to toggle the class on the elements, I suggest:
In ChildOne add a selectedItem to data and set it with #click handler.
In ChildTwo add a #click hanlder that will emit click event to the ChildOne compontent.
You can see a working example forked from your CodeSandbox here
I have added font-weight: bold to the .selected class to make it visible.

Read props from parent component in a list of dynamic components

I have a problem to read the passed data via props in Vue.js from parent to child. I have a list of components
components: [
{
name: "Cart Overview",
component: "CartOverview",
props: this.shopperCart
},
{
name: "Bank Account Overview",
component: "BankAccountOverview",
props: {}
},
{ name: "Verification", component: "Verification", props: {} },
{
name: "Completion Message",
component: "CompletionMessage",
props: {}
}
]
The variable "shopperCart" is set by a request from a backend.
Template of the parent component
<div class="modal-body">
<component
:is="checkoutComponents[currentStep].component"
v-bind="checkoutComponents[currentStep].props"
></component>
</div>
The user can navigate through the components with a next step button who sets the variable currentStep.
Example of one child component
<template>
<div>
<h1>Cart Oerview</h1>
{{ shopperCart }}
</div>
</template>
<script>
export default {
name: "CartOverview",
props: {
shopperCart: { type: Object, default: () => {} }
},
mounted() {
console.log("shopperCart", this.shopperCart);
}
};
</script>
The components lie on a modal. The log output only shows up displaying undefined when I refresh the page, where I can open the modal.
Can someone please help me?
Best regards,
A. Mehlen
I found myself a solution. I changed in the parent component the v-bind with :data
<div class="modal-body">
<component
:is="checkoutComponents[currentStep].component"
:data="checkoutComponents[currentStep].props"
></component>
</div>
and in the child component the name of the prop
<template>
<div>
<h1>Cart Oerview</h1>
{{ data }}
</div>
</template>
<script>
export default {
name: "CartOverview",
props: {
data: { type: Object, default: () => {} }
},
mounted() {
console.log("shopperCart", this.data);
}
};
</script>
Now it works :-)

Kendo-UI Edit template not working with Vue Template Single File Component

I am using kendoui grid vue wrapper, I find out at kendoui vue documentation that I can use a single file Component for kendo templates, I am working on a editable grid with a popup editor, what I am doing is setting the property "editable" with the name of the method that return the template and template args, the same thing as the documentation of Vue Templates of KendoUi, but It just ignore the function and get inline editable.
The other way that I tried is to set the editable property like "{mode: 'popup', template: templateMethod}", and it popup the editing window, but at the content it just displays "[object] [object]".
Here is my Code:
RouterComponent:
<template>
<c-main>
<c-row>
<c-col xs="24" sm="8">
<c-panel title="Departamento<small>Workflow</small>" style="height:350px;">
<div class="u-mt-10"><strong>Nombre</strong></div>
<div class="u-mt-10">{{ $route.params.name }}</div>
<div class="u-mt-10"><strong>Descripción</strong></div>
<div class="u-mt-10">{{ $route.params.description }}</div>
</c-panel>
</c-col>
<c-col xs="24" sm="16">
<c-panel title="Usuarios<small>Administrar</small>" style="height:350px;">
<kendo-datasource
ref="usersdatasource"
:type="'aspnetmvc-ajax'"
:pageSize="20"
:server-filtering='true'
:transport-read-url= "baseServiceUrl+'/api/DepartmentUsers'"
:transport-read-type="'GET'"
:transport-read-data-type="'json'"
:transport-read-data="dataParameters"
:transport-destroy-url="baseServiceUrl+'/api/DepartmentUsers'"
:transport-create-url="baseServiceUrl+'/api/DepartmentUsers'"
:schema-data= "'Data'"
:schema-total= "'Total'"
:schema-errors= "'Errors'"
:schema-model-id="'Id'"
:schema-model-fields="usersSchemaModelFields"
></kendo-datasource>
<kendo-grid
id="usersdatagrid"
:height="'auto'"
:dataSourceRef="'usersdatasource'"
:toolbar="[ {name:'create', text: 'Agregar Usuario'}]"
:groupable='false'
:sortable='true'
:filterable='true'
:selectable='true'
:editable="{ mode: 'popup', template: popupTemplate }"
:pageable-refresh='true'
:pageable-page-sizes='true'
:pageable-button-count="5"
>
<kendo-grid-column
field="UserName"
title="Usuario"
>
</kendo-grid-column>
<kendo-grid-column
field="DisplayName"
title="Nombre"
>
</kendo-grid-column>
<kendo-grid-column
:width="150"
:command="[{name:'destroy', text:'Eliminar' }]" title="Acción">
</kendo-grid-column>
</kendo-grid>
</c-panel>
</c-col>
</c-row>
<c-row>
<c-col xs="24">
<c-panel title="Reglas<small>Departamento</small>" style="height:400px;">
<kendo-datasource
ref="rulesdatasource"
:type="'aspnetmvc-ajax'"
:pageSize="20"
:server-filtering='true'
:transport-read-url= "baseServiceUrl+'/api/Departments'"
:transport-read-type="'GET'"
:transport-read-data-type="'json'"
:transport-update-url="baseServiceUrl+'/api/Departments'"
:transport-update-type="'PUT'"
:transport-update-data-type="'json'"
:transport-destroy-url="baseServiceUrl+'/api/Departments'"
:transport-create-url="baseServiceUrl+'/api/Departments'"
:schema-data= "'Data'"
:schema-total= "'Total'"
:schema-errors= "'Errors'"
:schema-model-id="'Id'"
></kendo-datasource>
<kendo-grid
id="rulesdatagrid"
:height="'auto'"
:dataSourceRef="'rulesdatasource'"
:groupable='false'
:sortable='true'
:filterable='true'
:selectable='true'
:pageable-refresh='true'
:pageable-page-sizes='true'
:pageable-button-count="5"
>
<kendo-grid-column
field="UserName"
title="Usuario"
>
</kendo-grid-column>
<kendo-grid-column
field="Nombre"
title="DisplayName"
>
</kendo-grid-column>
</kendo-grid>
</c-panel>
</c-col>
</c-row>
</c-main>
</template>
<script>
import Vue from 'vue'
import UserTemplateVue from './departmentUserTemplate.vue'
var UserTemplate = Vue.component(UserTemplateVue.name, UserTemplateVue)
export default {
name: "AddUserDepartmentView",
data() {
return {
baseServiceUrl: window.baseServiceUrl,
dataParameters: {
department: $route.params.Id,
roles: "WORKFLOW;WORKFLOWADMIN"
},
filterConfiguration: [],
usersSchemaModelFields: {
Id: { editable: false, nullable: true },
DisplayName: { validation: { required: true } },
UserName: { validation: { required: true } },
Email: { validation: { required: true } },
TenantId: { validation: { required: true } },
IsAdmin: { validation: { required: true } }
}
}
},
methods: {
popupTemplate: function(e) {
return {
template: UserTemplate,
templateArgs: e
}
}
}
};
</script>
<style>
#usersdatagrid{
height:100% !important;
}
#rulesdatagrid{
height:100% !important;
}
</style>
Template Component:
<template>
<span>
<button #click="buttonClick">Click Me</button>
</span>
</template>
<script>
export default {
name: 'template1',
methods: {
buttonClick: function (e) {
alert("Button click")
}
},
data () {
return {
templateArgs: {}
}
}
}
</script>
Please Help me With this.
Thank You a lot
I know this is old, but I stumbled across this while looking for something related, so I thought I'd take a stab at it for future reference.
Two things I would suggest trying here:
Make sure that your component has a name specified.
Try using: editable-mode="popup;" along with editable-template="popupTemplate"
That did the trick for me. For whatever reason, it just wasn't happy when I was passing an object into editable.
Hope that helps!