I'm working in VueJs and I've created a few components, called SiteList.vue, Site.vue, and CreateSite.vue. These components make up a bigger component hosted in a file called ListContainer.vue. These components all together are a list of websites that the user can reactively add, change the title, and remove sites, and edit function. ListContainer.vue outputs the whole thing to App.vue. Inside App.vue I've created an html table in the template, and call the listcontainer element inside the table tags. I have a button element that I am trying to use to continue creating the listcontainer element inside the tabletags. So instead of just one listcontainer element, the button will be able to create many.
I attempted to make an array that had all the formatting for a template element inside it, but it did not work and felt hacky.
I read the section on dynamic components and I feel like that is much more close, but I just can't wrap my head around how I should do it. I'm genuinely stuck on something I think might be extremely easy...
App.vue
<template> <div id="app"> <!-- Injection goes here -->
<time-component/>
<table>
<tr>
<td><listcontainer/></td>
<td><listcontainer/></td>
<td><div><button @click="listcontainer++">MAKE NEW LIST</button></div></td>
</tr>
</table> </div> </template>
<script>
import TimeComponent from './components/Time.vue'
import listcontainer from "./components/ListContainer.vue"
export default { name: 'App', components: {
TimeComponent,
listcontainer, }, methods:{
addListContainer(){
}
} } </script>
<!-- CSS for global --> <style>
#app { font-family:serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; margin-top: 60px; background-color: #fff; } </style>
ListContainer.vue
<template>
<div class="large-container">
<sitelist @changeLabel="setSiteLabel" :siteListLabel="siteListLabel_d"/>
</div>
</template>
<script>
import sitelist from "./SiteList.vue"
export default {
name: "list-container",
data(){
return {
siteListLabel_d: "",
};
},
methods: {
setSiteLabel(siteListLabel){
this.siteListLabel_d = siteListLabel;
},
},
components: { sitelist }
};
</script>
<style scoped>
.large-container{
padding: 20px;
margin-left: 20px;
margin-top: 200px;
border: 1px solid;
max-width: 400px;
}
</style>
SiteList.vue
<template>
<div class="sitelist-container">
{{ siteListLabel }}
<form @submit.prevent="changeLabel()">
<input
type="text"
v-model="siteListLabel_d"
/>
<button @click="changeLabel"> Change </button>
</form>
<ul>
<create-site @on-new-site="addSite($event)" />
<site
v-for="(site, index) in sites"
:key="index"
:siteName="site.siteName"
:staticRef="site.staticRef"
@site-remove="removeSite(site)"
@edit-el="editSite(site, $event)"
/>
</ul>
</div>
</template>
<script>
import Site from "./Site.vue"
import createSite from "./CreateSite.vue"
export default {
name: "site-list",
props: {
siteListLabel: String,
},
data(){
return {
sites: [ { staticRef: "https://", siteName: "www.cnn.com" },
{ staticRef: "https://", siteName: "www.nyt.com" },
{ staticRef: "https://", siteName: "washingtonpost.com" }
],
// Shouldn't edit props so I made this which is based off
// the value of the prop but not using the prop directly.
siteListLabel_d: ""
};
},
methods: {
addSite(newSite){
this.sites.push({ staticRef: "https://", siteName: newSite });
},
// create a new array (callback)
removeSite(removedSite){
this.sites = this.sites.filter(site => site !== removedSite);
},
editSite(site, newSiteName){
site.siteName = newSiteName;
},
changeLabel(){
this.$emit("changeLabel", this.siteListLabel_d);
//this.siteListLabel = this.siteListLabel_d
}
},
components: { Site, createSite }
};
</script>
<style scoped>
</style>
Site.vue
<template>
<div class="container">
<li>
<span><a :href="staticRef + siteName">{{ siteName }}</a> <!-- array {{ siteName }} --> </span>
<button @click="doEdit() ; isHidden = !isHidden"> Toggle-Edit </button>
<button @click="$emit('site-remove')">Remove</button>
</li>
<form @submit.prevent="stopEdit()" v-if="!isHidden">
<input
type="text"
v-model="newSiteName"
@blur="stopEdit()"
/>
</form>
</div>
</template>
<script>
export default {
data: function() {
return{
isEditable: false,
isHidden: true,
newSiteName: ""
};
},
props: {
siteName: String,
staticRef: String,
},
methods: {
doEdit(){
if (this.isEditable){
this.stopEdit();
} else {
this.newSiteName = this.siteName;
this.isEditable = true;
}
},
stopEdit(){
this.isEditable = false;
this.$emit("edit-el", this.newSiteName)
}
}
};
</script>
<style scoped>
.container{
border: 1px solid;
}
</style>
CreateSite.vue
<template>
<form @submit.prevent="addSite()">
<input
v-model="newSite"
type="text"
placeholder="Add a new site..."
/>
</form>
</template>
<script>
export default {
data: function(){
return{
newSite: ""
};
},
methods: {
addSite(){
if (this.newSite.length > 0){
this.$emit("on-new-site", this.newSite);
}
}
}
}
</script>
<style scoped>
</style>
The goal is to add a new array item with all the data which is necessary to a single component when clicking onto the button. You do not put the template of each element into the array but only the data. Then, you'll loop through the array with v-for
and, like this, create a component for each element in the array. The data which is part of the array is bound to the component with a prop.
Here is an example:
<template>
<div id="app">
<button @click="addNewItem">Add</button>
<table>
<tr v-for="item in array" :key="item.id">
<td>
<listcontainer :item="item" />
</td>
</tr>
</table>
</div>
</template>
<script>
import Listcontainer from './components/ListContainer.vue';
export default {
name: 'App',
components: { Listcontainer },
data() {
return { array: [] };
},
methods:{
addNewItem() {
this.array.push({
id: Math.random(),
data: ...
});
}
}
</script>
The Listcontainer should then have a prop with all relevant information:
<template>
<div>{{ item }}</div>
</template>
<script>
export default {
name: 'Listcontainer',
props: {
item: {
type: Object,
required: true
}
}
};
</script>
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments