Trying to iterate over a list of (imagePath, href) & display them with vue.js.
The href value of the images work, the link opens on click. Though the images aren't showing.
My code is as follows:
<div
v-for="(image,index) in socials"
:key="index"
>
<a :href="image.href">
<v-avatar
size="48"
tile
>
<img
:src="image.src"
/>
</v-avatar>
</a>
</div>
export default {
name: 'App',
data: () => ({
socials: [
{
id:"1",
src:"../assets/socials/discord.png",
href:"https://discord.gg/link_to_discord"
},
{
id:"2",
src:"../assets/socials/telegram.png",
href:"https://t.me//link_to_telegram"
},
{
id:"3",
src:"../assets/socials/medium.png",
href:"https://medium.com/#link_to_medium"
}
],
})
};
The images are named correctly and are in the correct dir. How can I change my code so that the images are shown properly ?
This code belongs to a footer, so the template & js is in App.vue
SOLUTION
Thanks to #Nikola and this question, I was able to solve it via getImgUrl method. Here's the updated code:
template
<div
v-for="image in socials"
:key="image.id"
>
<a :href="image.href">
<v-avatar
size="48"
tile
>
<img
:src="getImgUrl(image.src)"
/>
</v-avatar>
</a>
</div>
script
<script>
export default {
name: 'App',
data: function() {
return {
socials: [
{
id:"1",
src:"socials/discord.png",
href:"https://discord.gg/link_to_discord"
},
{
id:"2",
src:"socials/telegram.png",
href:"https://t.me//link_to_telegram"
},
{
id:"3",
src:"socials/medium.png",
href:"https://medium.com/#link_to_medium"
}
],
};
},
methods:{
getImgUrl: function (path) {
return require('#/assets/' + path);
}
}
};
</script>
You can make method:
export default {
name: 'App',
data: () => ({
socials: [
{
id:"1",
src:"socials/discord.png",
href:"https://discord.gg/link_to_discord"
},
{
id:"2",
src:"socials/telegram.png",
href:"https://t.me//link_to_telegram"
},
{
id:"3",
src:"socials/medium.png",
href:"https://medium.com/#link_to_medium"
}
],
}),
methods: {
getImgUrl: function (path) {
return require('#/assets/' + path);
}
}
};
Then in template call that method:
<div
v-for="(image,index) in socials"
:key="index"
>
<a :href="image.href">
<v-avatar
size="48"
tile
>
<img
:src="getImgUrl(image.src)"
/>
</v-avatar>
</a>
</div>
Related
i try to build a little clothing web shop with nuxtjs. You can choose the color on the details page. The details page represents a pice of clothing. The ColorMenu is a component. If you choose something a color, it will emit it back to the details page and will send a new details request to my backend.
However, changing the color only works if you don't choose another piece of clothing. If you choose another piece of clothing (so the route parameters will change) and choose another color in the menu, there is a always an error that it cannot load anything. it seems that it sends repeated requests until the request is blocked.
The details routes are built according to this scheme: localhost/details/{sellableId}/{ideaId}/{appearanceId}
Details Page:
<template>
<section class="section">
<div v-if="details">
<div class="columns">
<div class="column">
<ImageCaroussel :images="details.images"></ImageCaroussel>
</div>
<div class="column">
<h3>Farben</h3>
<ColorMenu
:appearances="productType.appearances"
:appearanceIds="details.appearanceIds"
></ColorMenu>
</div>
</div>
</div>
</section>
</template>
<script>
import { mapState } from 'vuex'
import Dropdown from '~/components/details/Dropdown.vue'
import ColorMenu from '~/components/details/ColorMenu.vue'
import ImageCaroussel from '~/components/details/ImageCaroussel.vue'
export default {
created() {
this.$nuxt.$on('selected', ($event) => (this.selected = $event))
this.$nuxt.$on('selectedColor', ($event) => this.setSelectedColor($event))
},
data() {
return {
modal: false,
selected: '',
selectedColor: '',
}
},
async asyncData({ store, params }) {
console.log('asyncfirst')
if (params.sellableId && params.appearanceId && params.ideaId) {
await store.dispatch('details/get_details', {
sellableId: params.sellableId,
appearanceId: params.appearanceId,
ideaId: params.ideaId,
})
let sellableId = params.sellableId
let appearanceId = params.appearanceId
let ideaId = params.ideaId
console.log('asyncsecond!')
return { sellableId, appearanceId, ideaId }
}
},
mounted() {
this.sellableId = this.$route.params.sellableId
this.appearanceId = this.$route.params.appearanceId
this.ideaId = this.$route.params.ideaId
console.log('Mounted!')
},
components: {
Dropdown,
ColorMenu,
ImageCaroussel,
},
computed: {
...mapState({
details: (state) => {
return state.details.details
},
currency: (state) => {
return state.sellable.currency
},
productType: (state) => {
return state.details.productType
},
}),
},
methods: {
checkout: async function (sellableId, size, appearanceId) {
let link = await this.$backendrepositories.basket.checkout(
sellableId,
size,
appearanceId
)
if (link.status === 200 && link.data) {
this.modal = true
setTimeout(() => {
window.location.href = link.data.link
}, 3000)
}
},
setSelectedColor: async function (event) {
this.selectedColor = event
await this.$store.dispatch('details/get_details', {
sellableId: this.sellableId,
appearanceId: this.selectedColor,
ideaId: this.ideaId,
})
},
},
}
</script>
ColorMenu Component:
<template>
<div>
<div
v-for="(cell, index) in appearances"
:key="index"
style="display: inline-block"
>
<label v-if="appearanceIds.includes(cell.id)" class="self-container">
<input type="radio" checked="checked" name="color" />
<span
class="checkmark"
:style="`background-color: ${cell.colors[0].value}`"
#click="select(cell.id)"
></span>
</label>
</div>
</div>
</template>
<script>
export default {
data: function () {
return {
selected: '',
}
},
props: ['appearances', 'appearanceIds'],
methods: {
select(select) {
this.selected = select
this.$nuxt.$emit('selectedColor', this.selected)
},
},
}
</script>
There is a live demo at https://akano-frontend.vercel.app/
In
https://codesandbox.io/s/rmh2n?file=/src/components/NestedDraggable.vue
, in:
<template>
<draggable class="dragArea" tag="ul" :list="tasks" :group="{ name: 'g1' }">
<li v-for="el in tasks" :key="el.name">
<generic-item :taskItem="el"> </generic-item>
<p>{{ el.name }}</p>
<nested-draggable :tasks="el.tasks" />
</li>
</draggable>
</template>
why isn't generic-item rendering el?
GenericItem:
<template>
<div v-if="taskItemLocal['itemSectionCategoryId']">
<p>
Section: {{taskItemLocal['itemSectionCategoryName']}}
</p>
</div>
<div v-else-if="taskItemLocal['itemSectionCategoryItemId']">
<p>
Item: {{taskItemLocal['itemSectionCategoryItemName']}}
</p>
</div>
<div v-else>
Not set
</div>
</template>
<script>
export default {
name: "generic-item",
props: {
taskItem: {
required: true,
type: Object,
default() {
return {};
},
},
},
components: {},
watch: {
taskItem: {
deep: true,
handler(val) {
this.taskItemLocal = Object.assign({}, val);
},
},
},
computed: {
getTaskItemLocal: {
get() {
if (!this.taskItemLocal) {
return Object.assign({}, this.taskItem);
}
return Object.values(this.taskItemLocal);
},
set(value) {
this.taskItemLocal = value;
},
},
},
data() {
return {
taskItemLocal: {
type: Object,
default: {},
},
};
},
};
</script>
<style scoped></style>
It looks like it isn't computing taskItemLocal. This component will have to modify props (they can't be used directly in the template). How can I "directly" copy props to a local variable and use it in the template "on the first run"?
I'm a beginner programmer and I create a spa like trello. Creates boards. In the board creates lists, they are displayed different with different id, but the list items are displayed with the same id and they are duplicated in each list. Sorry for my english:) Help me, please and tell in detail what the problem is.. Thank you very much
vuex file router.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
boards: JSON.parse(localStorage.getItem('boards') || '[]'),
lists: [],
items: []
// items: JSON.parse(localStorage.getItem('items') || '[]')
// lists: JSON.parse(localStorage.getItem('lists') || '[]')
},
mutations: {
addBoard(state, board) {
state.boards.push(board)
localStorage.setItem('boards', JSON.stringify(state.boards))
},
addList(state, list) {
state.lists.push(list)
// localStorage.setItem('lists', JSON.stringify(state.lists))
},
createItemListt(state, item) {
state.items.push(item)
// localStorage.setItem('items', JSON.stringify(state.items))
}
},
actions: {
addBoard({commit}, board) {
commit('addBoard', board)
},
addList({commit}, list) {
commit('addList', list)
},
createItemListt({commit}, item) {
commit('createItemListt', item)
}
},
getters: {
boards: s => s.boards,
taskById: s => id => s.boards.find(t => t.id === id),
lists: d => d.lists,
items: a => a.items
},
modules: {
}
})
the page on which the lists are created MyBoard.vue
<template>
<div>
<div class="wrapper">
<div class="row">
<h1>{{board.title}}</h1>
<div class="list " v-for="list in lists" :key="list.idList">
<div class="list__title">
<h3>{{list.titleList}}</h3>
</div>
<div class="list__card" v-for="item in items" :key="item.idItemList">
<span class="list__item">{{item.itemList}}</span>
<a class="btn-floating btn-tiny btn-check" tag="button">
<i class="material-icons">check</i>
</a>
</div>
<createItemList />
</div>
<createList />
</div>
</div>
</div>
</template>
<script>
export default {
computed: {
board() {
return this.$store.getters.taskById(+this.$route.params.id);
},
lists() {
return this.$store.getters.lists;
},
items() {
return this.$store.getters.items;
}
},
components: {
createList: () => import("../components/createList"),
createItemList: () => import("../components/createItemList")
}
};
</script>
CreateList.Vue
<template>
<div>
<div class="row">
<div class="new-list" v-show="isCreating">
<div class="list__title input-field">
<input
type="text"
required
id="list-title"
class="none validate"
tag="button"
autofocus
v-model="titleList"
v-on:keyup.enter="createList"
/>
<label for="list-title">Enter Title List</label>
</div>
<a class="btn-floating transparent btn-close" tag="button" #click="closeList">
<i class="material-icons">close</i>
</a>
</div>
<div class="create-list z-depth-2" v-show="!isCreating">
<p>Create list</p>
<a
class="btn-floating btn-large waves-effect waves-light deep-purple lighten-2 pulse"
tag="button"
#click="addList"
v-on:keyup.enter="addList"
>
<i class="material-icons">add</i>
</a>
</div>
</div>
</div>
</template>
<script>
export default {
data: () => ({
isCreating: false,
titleList: "",
idList: ""
}),
methods: {
addList() {
this.isCreating = true;
},
closeList() {
this.isCreating = false;
},
createList() {
if (this.titleList == "") {
return false;
}
const list = {
idList: Date.now(),
titleList: this.titleList
};
this.$store.dispatch("addList", list);
this.titleList = "";
this.isCreating = false;
console.log(list.titleList);
}
}
};
</script>
CreateItemList.vue
<template>
<div>
<div class="add-item">
<div class="textarea-item input-field" v-show="isAdding">
<input
type="text"
class="validate"
id="list-item"
v-model="itemList"
v-on:keyup.enter="createItemList"
autofocus
/>
<label for="list-item">Enter Item List</label>
</div>
<a class="waves-effect waves-light btn" v-show="!isAdding" #click="addCard">
<i class="material-icons right">add</i>Add Card
</a>
</div>
</div>
</template>
<script>
export default {
data: () => ({
isAdding: false,
itemList: "",
}),
methods: {
addCard() {
this.isAdding = true;
},
createItemList() {
if (this.itemList == "") {
return false;
}
const item = {
idItemList: Date.now(),
itemList: this.itemList
};
this.$store.dispatch("createItemListt", item);
this.itemList = "";
this.isAdding = false;
}
}
};
</script>
attach photo
Tried to go with the basic idea of the structure you laid out. I added:
id to all items, so they can be identifed
children to appropriate items, so you can keep track of what's in them
const store = new Vuex.Store({
state: {
tables: [
{ id: 1, children: ['1.1', '1.2'] },
{ id: 2, children: ['2.1'] }
],
lists: [
{ id: '1.1', children: ['1.1.1'] },
{ id: '1.2', children: ['1.2.1'] },
{ id: '2.1', children: ['2.1.1', '2.1.2'] },
],
cards: [
{ id: '1.1.1' },
{ id: '1.2.1' },
{ id: '2.1.1' },
{ id: '2.1.2' },
]
},
mutations: {
ADD_CARD(state, listId) {
const list = state.lists.find(e => e.id === listId)
const cards = state.cards
const card = { id: Date.now() }
cards.push( card )
list.children.push( card.id )
},
ADD_LIST(state, tableId) {
const table = state.tables.find(e => e.id === tableId)
const lists = state.lists
const list = { id: Date.now(), children: [] }
lists.push( list )
table.children.push( list.id )
},
ADD_TABLE(state) {
const tables = state.tables
const table = { id: Date.now(), children: [] }
tables.push( table )
},
TRY_MOVING_LIST(state) {
const table1 = state.tables.find(e => e.id === 1)
const table2 = state.tables.find(e => e.id === 2)
const item = table1.children.pop() // remove the last item
table2.children.push(item)
}
},
actions: {
addCard({ commit }, listId) {
commit('ADD_CARD', listId)
},
addList({ commit }, tableId) {
commit('ADD_LIST', tableId)
},
addTable({ commit }) {
commit('ADD_TABLE')
},
tryMovingList({ commit }) {
commit('TRY_MOVING_LIST')
}
},
getters: {
getTables: s => s.tables,
getListById: s => id => s.lists.find(e => e.id === id),
getCardById: s => id => s.cards.find(e => e.id === id),
}
})
Vue.component('CustomCard', {
props: ['card'],
template: `<div>
card ID: {{ card.id }}<br />
</div>`
})
Vue.component('CustomList', {
props: ['list'],
template: `<div>
list ID: {{ list.id }}<br />
<custom-card
v-for="item in list.children"
:key="item"
:card="getCard(item)"
/>
<button #click="addCard">ADD CARD +</button>
<hr />
</div>`,
methods: {
getCard(id) {
return this.$store.getters.getCardById(id)
},
addCard() {
this.$store.dispatch('addCard', this.list.id)
}
}
})
Vue.component('CustomTable', {
props: ['cTable'],
template: `<div>
table ID: {{ cTable.id }}<br />
<custom-list
v-for="item in cTable.children"
:key="item"
:list="getList(item)"
/>
<button #click="addList">ADD LIST +</button>
<hr />
</div>`,
methods: {
getList(id) {
return this.$store.getters.getListById(id)
},
addList(id) {
this.$store.dispatch('addList', this.cTable.id)
}
}
})
new Vue({
el: "#app",
store,
computed: {
tables() {
return this.$store.state.tables
}
},
methods: {
addTable() {
this.$store.dispatch('addTable')
},
tryMovingList() {
// this function will move the last list in table ID 1
// to the end of table ID 2's lists
// NOT FOOLPROOF - you should add error handling logic!
this.$store.dispatch('tryMovingList')
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuex"></script>
<div id="app">
<button #click="tryMovingList()">MOVE LIST</button><br />
<button #click="addTable()">ADD TABLE +</button>
<hr />
<custom-table v-for="item in tables" :key="'table-' + item.id" :c-table="item" />
</div>
With this setup you can change the hierarchy quite easily: just delete an ID from one Array of children and add it to another (e.g. remove '1.1' from table ID 1's children array and add it to table ID 2's children array - everything moved to table ID 2. tryMovingList() does exactly this - this method/action is not foolproof, just for you to try out moving a whole list)
There could be other patterns to solve this problem (like a real linked list data structure or the mediator pattern), but for smaller apps this is OK, I think (I would use it... :) ).
ONE PIECE OF ADVICE
If you want to store state in localStorage on mutations, don't do it by yourself - use Vuex's integrated subscribe mechanism: https://vuex.vuejs.org/api/#subscribe
I am trying to get the json to display in cards or list after search. The search part seems to be working since I can see the call to the API with the text written in the search box in the console. but nothing is displaying and I get this error TypeError: Cannot read property 'data' of undefined. I don't think I am getting the json path correct.
app.vue
<template>
<div id="app">
<Header/>
<SearchForm v-on:search="search"/>
<SearchResults
v-if="results.length > 0"
v-bind:results="results"
v-bind:reformattedSearchString="reformattedSearchString"
/>
<Pagination
v-if="results.length > 0"
v-bind:prevPageToken="api.prevPageToken"
v-bind:nextPageToken="api.nextPageToken"
v-on:prev-page="prevPage"
v-on:next-page="nextPage"
/>
</div>
</template>
<script>
import Header from './components/layout/Header';
import SearchForm from './components/SearchForm';
import SearchResults from './components/SearchResults';
import Pagination from './components/Pagination';
import axios from 'axios';
export default {
name: 'app',
components: {
Header,
SearchForm,
SearchResults,
Pagination
},
data() {
return {
results: [],
reformattedSearchString: '',
api: {
baseUrl: 'https://geodeepdive.org/api/v1/articles?',
max: 10,
q: '',
prevPageToken: '',
nextPageToken: ''
}
};
},
methods: {
search(searchParams) {
this.reformattedSearchString = searchParams.join(' ');
this.api.q = searchParams.join('+');
const { baseUrl, q, max} = this.api;
const apiUrl = `${baseUrl}&term=${q}&title=${q}&max=${max}`;
this.getData(apiUrl);
},
prevPage() {
const { baseUrl, q, max, prevPageToken } = this.api;
const apiUrl = `${baseUrl}&term=${q}&title=${q}&max=${max}&pageToken=${prevPageToken}`;
this.getData(apiUrl);
},
nextPage() {
const { baseUrl, q, max,nextPageToken } = this.api;
const apiUrl = `${baseUrl}&term=${q}&title=${q}&max=${max}&pageToken=${nextPageToken}`;
this.getData(apiUrl);
},
getData(apiUrl) {
axios
.get(apiUrl)
.then(res => {
this.results = res.success.data;
this.api.prevPageToken = res.success.data.prevPageToken;
this.api.nextPageToken = res.success.data.nextPageToken;
})
.catch(error => console.log(error))
}
}
};
</script>
searchresults
<template>
<div class="container mb-3">
<div class="d-flex mb-3">
<div class="mr-auto">
<h3>Search Results for "{{ reformattedSearchString }}"</h3>
</div>
<div class="btn-group ml-auto" role="group">
<button
#click="changeDisplayMode('grid')"
type="button"
class="btn btn-outline-secondary"
v-bind:class="{ active: displayMode === 'grid' }"
>
<i class="fas fa-th"></i>
</button>
<button
#click="changeDisplayMode('list')"
type="button"
class="btn btn-outline-secondary"
v-bind:class="{ active: displayMode === 'list' }"
>
<i class="fas fa-list"></i>
</button>
</div>
</div>
<div class="card-columns" v-if="displayMode === 'grid'">
<div class="card" v-bind:key="result.link.url" v-for="result in results">
<ArticleGridItem v-bind:result="result"/>
</div>
</div>
<div v-else>
<div class="card mb-2" v-bind:key="result.link.url" v-for="result in results">
<ArticleListItem v-bind:result="result"/>
</div>
</div>
</div>
</template>
<script>
import ArticleListItem from './ArticleListItem';
import ArticleGridItem from './ArticleGridItem';
export default {
name: 'SearchResults',
components: {
ArticleListItem,
ArticleGridItem
},
data() {
return {
title: 'Search Results',
displayMode: 'grid'
};
},
methods: {
changeDisplayMode(displayMode) {
this.displayMode = displayMode;
}
},
props: ['results', 'reformattedSearchString']
};
</script>
json
{
success: {
v: 1,
data: [
{
type: "article",
_gddid: "5ea0b2b3998e17af826b7f42",
title: "The current COVID-19 wave will likely be mitigated in the second-line European countries",
volume: "",
journal: "Cold Spring Harbor Laboratory Press",
link: [
{
url: "https://www.medrxiv.org/content/10.1101/2020.04.17.20069179v1",
type: "publisher"
}
],
publisher: "bioRxiv",
author: [
{
name: "Samuel Soubeyrand"
}
],
pages: "",
number: "",
identifier: [
{
type: "doi",
id: "10.1101/2020.04.17.20069179"
}
],
year: "2020"
}
]
}
}
you are missing res.data object
this.results = res.data.success.data;
this.api.prevPageToken = res.data.success.data.prevPageToken;
this.api.nextPageToken = res.data.success.data.nextPageToken;
I'm implementing Vue paper dashboard sidebar. So I have something like this:
Into Index I have
<template>
<div>
AdminIndex
<side-bar>
</side-bar>
</div>
</template>
<script>
import { faBox, faImages } from '#fortawesome/fontawesome-free-solid';
import Sidebar from '#/components/sidebar/SideBar';
export default {
name: 'admin-index-view',
components: {
SideBar,
},
data() {
return {
showSidebar: false,
sidebarLinks: [
{
name: 'admin.menu.products',
icon: faBoxes,
route: { name: 'adminProducts' },
},
{
name: 'admin.menu.sliders',
icon: faImages,
route: { name: '/admin/stats' },
},
],
};
},
methods: {
displaySidebar(value) {
this.showSidebar = value;
},
},
};
</script>
SideBar component:
<template>
<div :class="sidebarClasses"
:data-background-color="backgroundColor"
:data-active-color="activeColor">
<!--
Tip 1: you can change the color of the sidebar's background using: data-background-color="white | black | darkblue"
Tip 2: you can change the color of the active button using the data-active-color="primary | info | success | warning | danger"
-->
<!-- -->
<div class="sidebar-wrapper"
id="style-3">
<div class="logo">
<a href="#"
class="simple-text">
<div class="logo-img">
<img src="static/img/vue-logo.png"
alt="">
</div>
Paper Dashboard
</a>
</div>
<slot>
</slot>
<ul :class="navClasses">
<!--By default vue-router adds an active class to each route link. This way the links are colored when clicked-->
<router-link v-for="(link,index) in sidebarLinks"
:key="index"
:to="link.route"
tag="li"
:ref="link.name">
<a>
<font-awesome-icon :icon="link.icon" />
<p v-t="link.name" />
</a>
</router-link>
</ul>
<moving-arrow :move-y="arrowMovePx">
</moving-arrow>
</div>
</div>
</template>
<script>
import FontAwesomeIcon from '#fortawesome/vue-fontawesome';
import MovingArrow from './MovingArrow';
export default {
name: 'side-bar',
components: {
MovingArrow,
FontAwesomeIcon,
},
props: {
type: {
type: String,
default: 'sidebar',
validator: value => {
const acceptedValues = ['sidebar', 'navbar'];
return acceptedValues.indexOf(value) !== -1;
},
},
backgroundColor: {
type: String,
default: 'black',
validator: value => {
const acceptedValues = ['white', 'black', 'darkblue'];
return acceptedValues.indexOf(value) !== -1;
},
},
activeColor: {
type: String,
default: 'success',
validator: value => {
const acceptedValues = [
'primary',
'info',
'success',
'warning',
'danger',
];
return acceptedValues.indexOf(value) !== -1;
},
},
sidebarLinks: {
type: Array,
default: () => [],
},
},
data() {
return {
linkHeight: 60,
activeLinkIndex: 0,
windowWidth: 0,
isWindows: false,
hasAutoHeight: false,
};
},
computed: {
sidebarClasses() {
if (this.type === 'sidebar') {
return 'sidebar';
}
return 'collapse navbar-collapse off-canvas-sidebar';
},
navClasses() {
if (this.type === 'sidebar') {
return 'nav';
}
return 'nav navbar-nav';
},
/**
* Styles to animate the arrow near the current active sidebar link
* #returns {{transform: string}}
*/
arrowMovePx() {
return this.linkHeight * this.activeLinkIndex;
},
},
watch: {
$route() {
this.findActiveLink();
},
},
methods: {
findActiveLink() {
this.sidebarLinks.find((element, index) => {
const found = element.path === this.$route.path;
if (found) {
this.activeLinkIndex = index;
}
return found;
});
},
},
mounted() {
this.findActiveLink();
},
};
</script>
I dont receive any issues or vue errors, sidebar just don't display. In Chrome console just return empty: <side-bar data-v-66018f3c=""></side-bar> Someone knows why sidebar is not binded? What I need to do to get correctly implementation of it? Regards
Chrome console error:
[Vue warn]: Unknown custom element: - did you register the
component correctly? For recursive components, make sure to provide
the "name" option.