How can I resolve this error when calling v-for? - npm

basically my error is that when I run npm run build on the project it points out that it can't find my manuals list
**npm error:
npm run build
vue-tsc --noEmit && vite build
src/views/ManuaisView.vue:11:80 - error TS2304: Cannot find name 'manuais'.
11 **
Found 1 error.
Code:
<div class="flex flex-wrap -mx-4">
<div class="w-full md:w-1/2 lg:w-1/3 px-4" v-for="(manual, idx) in manuais" :key="manual.key">
<div class="h-full p-8 text-center hover:bg-white rounded-md hover:shadow-xl transition duration-200">
<div class="inline-flex h-16 w-16 mb-6 mx-auto items-center justify-center text-white bg-vermelho rounded-lg">
<i class="far fa-file-pdf fa-2x"></i>
</div>
<h3 class="mb-4 text-xl md:text-2xl leading-tight font-bold">{{manual.nome}}</h3>
<p>{{manual.descritivo}}</p>
<p>{{manual.link}}</p>
</div>
</div>
</div>
</div>
</section>
</template>
<script lang="ts">
import { meudb } from '../db';
var post:any = [];
var items: any;
const postRef = meudb.ref('********').once('value', (snapshot) => {
const documents = snapshot.val();
snapshot.forEach(d => {
post.push({nome:d.val().nome, descritivo:d.val().descritivo, link:d.val().nivel})
});
items = documents
console.log(post);
// do something with documents
});
console.log(postRef);
export default {
data() {
return {
manuais: post,
// manuais: [],
}
},
firebase: {
manuais: {
source: meudb.ref('********'),
asObject: true
}
}
}
</script>

If you want to update the manuais value you should set it to an empty array in its definition (in the data) and update its value in the mounted lifecycle (check documentation here
Example :
export default {
data: () => {
return {
manuais: [],
}
},
mounted() {
var post: any = [];
var items: any;
const postRef = meudb.ref('********').once('value', (snapshot) => {
const documents = snapshot.val();
snapshot.forEach(d => {
post.push({
nome: d.val().nome,
descritivo: d.val().descritivo,
link: d.val().nivel
})
});
items = documents
// do something with documents
});
this.manuais = post; // <-- here updating the value
}
}
Working snippet
Here is a little snippet for you to better undestand how works v for.
In your case, if you apply this logic, your code might work
new Vue({
el: '#app',
data: () => ({
manuais: []
}),
mounted() {
// get data from dataBase here
this.manuais = [{
name: 'foo'
}, {
name: 'bar'
}]
}
})
<script src="https://unpkg.com/vue#2.x/dist/vue.js"></script>
<div id="app">
<div v-for="manual in manuals">{{ manual.name }}</div>
</div>

Related

Prop being passed but not updating the component

So I have a function called updateAll(data.items) which should take the array and randomly assign a new name, it appears the data.items is updating when I view in vue dev tools and is being passed to the component but not actually updating the browser
I've also added in my buildData.js this is being used for the same app but in react so was trying to keep it as general as I could
//HomeView.vue
<script setup>
import { reactive, watch } from "vue";
import Hero from '#/components/Hero.vue'
import FunctionButton from '#/components/FunctionButton.vue'
import ItemsContainer from '#/components/ItemContainer.vue';
import * as dataFunc from '#/data/buildData'
const data = reactive({
items: JSON.parse(localStorage.getItem('items')) || []
})
console.log(data.items)
watch(() => data.items,(newValue) => {
localStorage.setItem('items', JSON.stringify(newValue))
}, {deep: true});
</script>
<template>
<div class='w-2/3 mx-auto text-center'>
<Hero HeroText="The one stop shop to store recipes!" />
<main>
<h1 class="text-5xl font-extrabold dark:text-white">Recipe Manager</h1>
<div class='functions gap-4 grid grid-cols-2 md:grid-cols-3 mt-8 mx-auto w-fit'>
<FunctionButton name="Add new recipe" #click="() => data.items = [...data.items,...dataFunc.buildData(1)]"/>
<FunctionButton name="Add 100 recipes" #click="() => data.items = [...data.items,...dataFunc.buildData(100)]"/>
<FunctionButton name="Add 1000 recipes" #click="() => data.items = [...data.items,...dataFunc.buildData(1000)]"/>
<FunctionButton name="Delete all recipes" #click="() => data.items = dataFunc.deleteAll()"/>
<FunctionButton name="Edit all recipes" #click="() => data.items = dataFunc.updateAll(data.items)"/>
</div>
<ItemsContainer :items="data.items"/>
</main>
</div>
</template>
//ItemContainer.vue
<script setup>
import Item from '#/components/Item.vue';
const props = defineProps({
items: {type: Array, default: () => []},
})
</script>
<template>
<div
class='items-container w-full md:w-max bg-gray-800 dark:bg-gray-800 text-white dark:text-black mx-auto mt-12 p-4 space-y-4'>
<Item
v-for="item in items"
v-if="items.length > 0"
:key="item.id"
:meal="item"
/>
<p v-else class='text-white'>No items to display</p>
</div>
</template>
I tried to make this as generic as possible as trying to make a similar app on Vue and React so they could share this
import { v4 as uuidv4 } from 'uuid';
function create(){
const data = {
id: uuidv4(),
category: category[random(category.length)],
name: meals[random(meals.length)],
prepTime: randomTime(),
cookTime: randomTime(),
isFav: false
}
return data
}
function random(max){
return Math.round(Math.random() * 1000) % max;
}
const meals = [
"Roast Chicken", "Omelette", "Steak Pie", "Beef Stir Fry", "Fish And Chips", "Tomato And Prawn Risotto", "Pepperoni Pizza", "Cheesy Nachos", "Fajitas", "Baked Potato",
"Full English Breakfast", "Pancakes", "Chocolate Brownies", "Meatballs With Red Pepper Sauce", "Chicken Cesar Salad", "Beef Burger", "Chips","Macaroni Cheese", "Fillet Steak", "Chicken Wings",
"BBQ Ribs", "Tomato Soup", "Prawn Dim Sum", "Pork Gyozas", "Tomato Bruschetta", "Spring Rolls", "Beef Jerky", "Lasagne", "Spagetti Carbonara", "Salmon And Potatoes"
]
const category = [
"Breakfast", "Lunch", "Dinner"
]
function randomTime(){
return Math.round(Math.random() * 30)
}
function buildData(count){
const data = new Array(count);
for(let i = 0; i<count; i++){
data[i] = create();
}
return data;
}
function updateAll(currentArray){
return currentArray.map((item) => {
return{...item, name: meals[random(meals.length)], }
})
}
function updateOne(item){
return{...item, name: meals[random(meals.length)], }
}
function favouriteAll(currentArray = []){
return currentArray.map((item) => {
return {...item, isFav: true}
})
}
function deleteAll(){
const data = []
return data;
}
export {buildData, deleteAll, favouriteAll, updateAll, updateOne};
Check the console for errors.
If I take the functionality from HomeView.vue, your ItemContainer.vue and an Item.vue (which you didn't provided), then it works.
Check the working Vue SFC Playground
The code:
App.vue
<script setup>
import { ref, watch, reactive } from 'vue'
import ItemsContainer from './ItemContainer.vue';
const data = reactive({
items: []
})
console.log(data.items)
watch(() => data.items, (newValue) => {
alert(JSON.stringify(newValue))
}, {deep: true});
const addItem = () => {
data.items.push({ id: data.items.length, name: 'New Item'})
}
</script>
<template>
<ItemsContainer :items="data.items"/><br />
<button type="button" #click="addItem()">Add</button>
</template>
ItemContainer.vue is the same.
Item.vue
<script setup>
const props = defineProps({
meal: {
type: Object,
default: {undefined}
}
})
</script>
<template>
<div>
{{JSON.stringify(meal)}}<br />
</div>
</template>
UPDATE
I've updated the playground with your functions and it still does work well. The problem is somewhere else.

Vuex store Getter loadings faster than store State

I have a nuxtJS vue2 components as follows:
<template>
<div class="product-fullpage">
<div class="product-card">
<img :src="productById(this.$route.params.id).imageURL">
<div class="product-headings">
<div class="product-info" v-animate-on-scroll>
<h1>{{ productById(this.$route.params.id).name }}</h1>
<h2>£{{ productById(this.$route.params.id).price }}</h2>
</div>
<div class="product-cart" v-animate-on-scroll>
<div class="quantity-info">
<label for="quantity">Quantity:</label>
<input v-model.number="productInfo.quantity" name="quantity" type="number" min="0" max="99"/>
</div>
<button #click="addToCart" :disabled="noQuantity">Add To Cart ></button>
</div>
</div>
</div>
</div>
</template>
I'm using a getter that does the following:
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters(['productById'])
},
}
</script>
Here's the getter
export const getters = {
productById: (state) => (id) => {
return state.products.find(product => product.id === id)
},
}
my state is set to pull from firebase
export const actions = {
async setProducts({commit}) {
let colRef = collection(db, 'products')
const querySnapshot = await getDocs(colRef)
querySnapshot.forEach((doc) => {
const imageDownloadURL = getDownloadURL(ref(storage, `${doc.data().imageRef}`))
.then( url => {
// console.log(url)
let article = ({
id: doc.id,
name: doc.data().name,
price: doc.data().price,
description: doc.data().description,
imageURL: url
})
commit('setProducts', article)
})
})
},
}
the mutation to set the state:
export const mutations = {
setProducts(state, article) {
let matchProduct = state.products.find(product => product.id == article.id)
if(!matchProduct) {
state.products.push(article)
}
},
}
and this is my state:
export const state = () => ({
products: [],
})
i thought that if i load everything beforehand in default.vue under 'layouts' that i can then have the store.state.products set.
<template>
<div class="container">
<!-- <nuxt /> -->
<nuxt v-if="!loading"/>
<div class="overlay" v-else>
Loading...
</div>
</div>
</template>
<script>
export default {
created() {
this.loading = true
this.$store.dispatch('setCart')
this.$store.dispatch('setProducts')
.finally(() => (this.loading=false))
},
data() {
return {
loading: false,
}
},
}
</script>
sorry if this is turning out to be a long post - but basically on initial load, I get my imageURL, name and price. but then on reload it comes out empty. i believe the getter is occurring before the store is loaded. how do i set it so that i can state.products.find(product => product.id == article.id) for my getter after state is loaded?

Nuxt Failed to execute ‘appendChild’ on ‘Node’ when trying to get window size

The problem is that the project must be transferred to Nuxt and some of the code does not work. Namely, the size of the screen must perform actions with the text. Since Nuxt is an SSR, the code cannot be executed on the server side because it does not know the size of the window.
Can I somehow fulfill this idea so that everything works?
I have a project with nuxt and i18n
[nuxt] Error while initializing app DOMException: Failed to execute 'appendChild' on 'Node': This node type does not support this method.
at Object.Je [as appendChild]
this my component vue
This code is an example of what causes an error.
<template>
<section>
<div>
<h2 class="subtitle" v-html="filterHeadSlogan"></h2>
</div>
</section>
</template>
<script>
export default {
name: 'testapp',
data() {
return {
filterHeadSlogan: '',
windowWidth: 0
}
},
methods: {
getWindowWidth(event) {
this.windowWidth = document.documentElement.clientWidth
var str = "<i>HELLO WORLD</i>"
if (this.windowWidth >= 960) {
this.filterHeadSlogan = str
} else {
this.filterHeadSlogan = str.replace(/<\/?[^>]+(>|$)/g, '')
}
}
},
mounted() {
this.$nextTick(function () {
window.addEventListener('resize', this.getWindowWidth);
//Init
this.getWindowWidth()
})
}
}
</script>
An error occurred because there was no data in the variable. The village appeared, but there was no data and there was a conflict. I created asyncData
async asyncData(){
return {
headSlogan: ""
}
},
Full code
<template>
<div class="westartslogan">
<div class="head-slogan">
<h2 v-html="headSlogan"></h2>
</div>
<h3>{{$t('page.home.wellcom_block_subtitle_left')}}</h3>
<ul>
<li><i class="icon"></i>
<div v-html="$t('page.home.wellcom_block_option_1_left')"></div></li>
<li><i class="icon"></i>
<div v-html="$t('page.home.wellcom_block_option_2_left')"></div></li>
<li><i class="icon"></i>
<div v-html="$t('page.home.wellcom_block_option_3_left')"></div></li>
<li><i class="icon"></i>
<div v-html="$t('page.home.wellcom_block_option_4_left')"></div></li>
<li><i class="icon"></i>
<div v-html="$t('page.home.wellcom_block_option_5_left')"></div></li>
</ul>
<div class="startcalc-btn button-container">
<nuxt-link :to="getLocalizedRoute({ name: 'calculator' })" class="uk-button uk-button-default">{{
$t('page.home.wellcom_button_calculator') }}
</nuxt-link >
</div>
<div class="ourproject-btn uk-hidden#s">
<div class="button-container">
<nuxt-link :to="getLocalizedRoute({ name: 'portfolio' })" class="uk-button uk-button-default">
{{ $t('page.home.wellcom_button_portfolio') }}
</nuxt-link>
</div>
</div>
</div>
</template>
<script>
export default {
async asyncData(){
return {
headSlogan: ""
}
},
name: 'we_can',
data () {
return {
filterHeadSlogan: '',
headSlogan: this.$i18n.t('page.home.wellcom_block_title_left'),
windowWidth: 0
}
},
methods: {
getWindowWidth (event) {
this.windowWidth = document.documentElement.clientWidth
if (this.windowWidth >= 960) {
this.headSlogan = this.headSlogan
} else {
var str = this.headSlogan
this.headSlogan = str.replace(/<\/?[^>]+(>|$)/g, '')
}
}
},
mounted() {
this.$nextTick(function () {
window.addEventListener('resize', this.getWindowWidth);
//Init
this.getWindowWidth()
})
}
}
</script>
<style scoped>
</style>
I was dealing with the same problem.
Do these steps:
Run your project (yarn start).
Open http://localhost:3000/ in Chrome.
In Chrome devtools clear site data in application tab.
Hard reload the page.

export default issue with VueJS ES5

I am developing a website, partially in Vue using ES5.
I need to get data from a Vue Child component to be sent back up to the parent, the way explained to me was via using export default {} However I am constantly recieving syntax errors and I am not sure why because from what i see, I am following Mozillas recomendations.
My question component:
var question = Vue.component('question', {
props: {
scenarios: Array,
scenario: Object,
post: Boolean
},
data: function () {
return ({
choice: 0,
counter: 0,
finished: false
});
},
export default {
methods: {
onClickButton: function (event) {
this.$emit('clicked', 'someValue')
},
postResponse: function () {
var formData = new FormData();
formData.append(this.choice);
// POST /someUrl
this.$http.post('Study/PostScenarioChoice', formData).then(response => {
// success callback
}, response => {
// error callback
});
},
activatePost: function () {
if (this.counter < this.scenarios.length) {
this.counter++;
}
else {
this.finished = true;
}
}
}
},
template:
`<div>
<div v-if="this.finished === false" class="row justify-content-center">
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioLeftImg" />
</button>
</div>
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioRightImg" />
</button>
</div>
</div>
<finished v-else></finished>
</div >
});
I get the error in the browser console Expected ':' which is on the line export default {
Any assistance will be greatly appreciated.
You have written completely wrong syntax of JavaScript. What you are trying to do here is to put "export default" in the place where object key should go. I will provide correct code here, but I strongly suggest you go and learn the fundamentals of JavaScript including arrays and objects in order to be able to read and write correct and valid JavaScript. Here is some good learning material for beginners:
http://eloquentjavascript.net/
https://maximdenisov.gitbooks.io/you-don-t-know-js/content/
And here is the fixed Vue component:
export default Vue.component("question", {
props: {
scenarios: Array,
scenario: Object,
post: Boolean
},
data: function () {
return ({
choice: 0,
counter: 0,
finished: false
});
},
methods: {
onClickButton: function (event) {
this.$emit("clicked", "someValue");
},
postResponse: function () {
var formData = new FormData();
formData.append(this.choice);
// POST /someUrl
this.$http.post("Study/PostScenarioChoice", formData).then(response => {
// success callback
}, response => {
// error callback
});
},
activatePost: function () {
if (this.counter < this.scenarios.length) {
this.counter++;
}
else {
this.finished = true;
}
}
},
template:
`<div>
<div v-if="this.finished === false" class="row justify-content-center">
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioLeftImg" />
</button>
</div>
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioRightImg" />
</button>
</div>
</div>
<finished v-else></finished>
</div>`
});
The actual answer was a simple misunderstanding of the information presented here. The use of export default was irrelevant in my implementation however this was not easily visible until I started noticing the emit posting the parent element later on.
The actual implementation was as follows:
var question = Vue.component('question', {
props: {
scenarios: Array,
scenario: Object,
post: Boolean,
counter: Number
},
data: function () {
return ({
choice: 0,
finished: false
});
},
methods: {
onClickButton: function (event) {
this.$emit('clicked', 'someValue');
},
postResponse: function () {
var formData = new FormData();
formData.append(this.choice);
// POST /someUrl
this.$http.post('Study/PostScenarioChoice', formData).then(response => {
// success callback
}, response => {
// error callback
});
}
},
template:
`<div>
<div v-if="this.finished === false" class="row justify-content-center">
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioLeftImg" />
</button>
</div>
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioRightImg" />
</button>
</div>
</div>
<finished v-else></finished>
</div >`
});
The receiving method in the parent element is as follows:
onClickChild: function (value) {
console.log(value) // someValue
this.showTimer = true;
this.countdownTimer();
if (this.counter < this.scenarios.length) {
this.counter++;
}
else {
this.finished = true;
}
}

Returning promise from api and putting into global data

Sorry if this is obvious but new to vue and my js not so great.....
I'm trying to get some data from an api and then put that data into a global object so it can be passed down to some child components.
I can't seem to $set the data into the global projects object, it either returns undefined or returns a promise.
parent --
<template>
<div id="app">
<section class="intro">
<h1>WELCOME</h1>
</section>
<transition name="fade"> <router-view class="view" :computedProjects=computedProject ></router-view></transition>
</div>
</template>
<script>
import projectsList from './components/projectsList'
var client = contentful.createClient({
// This is the space ID. A space is like a project folder in Contentful terms
space: '',
// This is the access token for this space. Normally you get both ID and the token in the Contentful web app
accessToken: '',
})
var PRODUCT_CONTENT_TYPE_ID = 'project'
export default {
name: 'app',
components: {
projectsList
},
data: function() {
return {
users:'',
projects:'',
}
},
methods: {},
created : function () {
client.getEntries().then((response) => {
console.log(response)
this.projects = response
console.log(this.projects)
return response
});
},
computed: {
computedProject: function() {
if (!this.projects) return null;
return this.projects;
}
}
}
}
child ---
<template>
<section class="project-list">
<swiper :options="swiperOption">
<swiper-slide v-for="project in projects" v-bind:ref="'project' + project.name" :key="project.name" data-swiper-parallax="-100">
<div class="project-cover wrapper">
{{project.name}}
<router-link :to="{ name: 'project', params: { id: project.name }}">
<div class="cover-img" :style="{ 'background-image': 'url(../static/img/projects/'+project.name+'/'+'project-details-header.jpg)' }"> </div>
<div class="project-copy"></div>
<div href="#" class="tilter tilter--4">
<figure class="tilter__figure">
<img class="tilter__image" :src="'+project.fields.coverImage.fields.file.url+'" alt="" />
<div class="tilter__deco tilter__deco--shine"></div>
<div class="tilter__deco tilter__deco--overlay" v-bind:style="{ backgroundImage: project.gradient }"></div>
<figcaption class="tilter__caption">
<h1 class="projectName tilter__title title">{{project.name}}</h1>
<h2 class="tilter__description">{{project.tagline}}</h2>
</figcaption>
<svg class="tilter__deco tilter__deco--lines" viewBox="0 0 300 415">
<path d="M20.5,20.5h260v375h-260V20.5z" />
</svg>
</figure>
</div>
</router-link>
</div>
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</section>
</template>
<script>
export default {
data: function () {
return {
swiperOption: {
autoplay:false,
setWrapperSize :true,
observeParents:true,
keyboardControl : true,
centeredSlides :true,
freeMode:false,
freeModeSticky:true,
pagination: '.swiper-pagination',
paginationType: 'progress',
watchSlidesProgress : true,
watchSlidesVisibility : true,
speed:650,
grabCursor:true,
parallax:true
},
}
},
props :['computedProject'],
created : function () {
console.log(this.projects)
},
any help will be appreciated.
You can just assign the vue data variable in the callback of getEntries like following:
created : function () {
var self = this
client.getEntries().then((response) => {
console.log(response)
self.projects = response
})
console.log(this.projects)
},