Recently I have signed up for VueMstery and I am lost. In order to understand code I have a habit of visualizing code and can't move if I cannot make sense of code. What I do not understand in the following code is
how function named updateProduct is updating the DOM with the passed in image and
how it know which image to display. (this point confuses me a lot)
Following is the code
<body>
<div class="nav-bar"></div>
<div id="app">
<div class="product">
<div class="product-image">
<img :src="image" alt="">
</div>
<div class="product-info">
<h1>{{products}}</h1>
<p v-if='inStock'>In Stock</p>
<p v-else>Out of stock</p>
<p v-if='inventory <= 10'>Low on stock</p>
<ul>
<li v-for='detail in details'>{{detail}}</li>
</ul>
<div v-for='variant in variants'
class='color-box'
:style="{backgroundColor: variant.variantColor}"
#mouseover='updateProduct(variant.variantImage)'>
</div>
cart {{cart}}
<button #click='addToCart'>
Add to cart
</button>
</div>
</div>
</div>
<!-- development version, includes helpful console warnings -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="main.js"></script>
and main.js
var app = new Vue({
el: '#app',
data: {
products: 'socks',
image: './assets/vmSocks-green.jpg',
inStock: true,
inventory: 10,
details: ["80% cotton", "20% polyester", "Gender-Neutral"],
variants: [
{
variantId: 2234,
variantColor: "green",
variantImage: "./assets/vmSocks-green.jpg"
},
{
variantId: 2235,
variantColor: "blue",
variantImage: "./assets/vmSocks-blue.jpg"
},
],
cart: 0
},
methods: {
addToCart: function(){
this.cart +=1
},
updateProduct: function(image){
this.image = image;
}
}
})
Look at the event handler which you've registered in the template:
#mouseover='updateProduct(variant.variantImage)'
Vue will run the function automatically when events fire. The function updateProduct just updates the value image in your data object. Vue detects any changes for that data and updates DOM when nessesary.
Also because your img tag depends on image property of data object:
<img :src="image" alt="">
you will see that image changes.
Related
Every time I use Nuxt.js in my projects I encounter the same mistake with styles. Some of them are enabled after the page is mounted. I tried to import styles from a file from a static folder, tried to use styles in the file (tag style), with the scoped attribute, without it, but each time some styles are included after the page has already been mounted. There are no problems in pure Vue js.
Look here, this is the state I was talking about. It lasts about half a second
And after that the page becomes normal
Sometimes this state persists until the user scrolls the page, sometimes it does not disappear at all
To clarify, I prefer the scoped attribute in my projects, without importing files with styles.
Edit
Ok. I don't know will it helps, but there is the code snippets
This is a test page
<template>
<div>
<app-promo
:data="{
titles: ['адреса пиццерий', 'roni napoletana'],
bgImg: 'page-addresses/promo/promo_bg.png'
}"
>
<app-search></app-search>
</app-promo>
</div>
</template>
This is a search component
<template>
<div v-click-outside="closeResults" class="search">
<div class="search__bar">
<input placeholder="Начните вводить название улицы, чтобы найти ближайшую пиццерию" v-model="searchRequest" #keypress.enter="search" type="text" class="search__input">
<button #click="search" class="search__submit"><img src="#/static/img/page-addresses/promo/zoom.svg" alt="Search"></button>
</div>
<div :class="['search__results', {'search__results_active': showResults}]">
<nuxt-link
:to="item.link"
class="search__result"
v-for="item in searchedItems"
:key="item.title"
>
{{ item.title }}
</nuxt-link>
</div>
</div>
</template>
<script>
export default {
data() {
return {
searchedItems: [
{
title: 'Истикбол улица',
link: '/'
},
{
title: 'Шевченко улица',
link: '/'
},
{
title: 'Тараккиёт улица',
link: '/'
}
],
searchRequest: '',
showResults: false
}
},
methods: {
search() {
this.showResults = true
},
closeResults() {
this.showResults = false
}
},
}
</script>
This is a promo component
<template>
<section class="promo">
<div class="promo__bg"><img src="#/static/img/page-main/promo/promo_bg.png" alt=""></div>
<div class="promo__inner">
<h1 class="promo__title">
<template v-for="title in data.titles">
{{ title }}
</template>
</h1>
<h2 class="promo__subtitle" v-if="data.subtitle">{{ data.subtitle }}</h2>
<slot></slot>
</div>
<div class="promo__label" v-if="data.labelImg">
<img src="#/static/img/page-main/promo/label_img.svg" alt="Img">
</div>
</section>
</template>
<script>
export default {
props: {
data: {
default: {
titles: '',
subtitle: '',
labelImg: '',
bgImg: ''
}
}
}
}
</script>
My nuxtconfig is standard. The styles are too.
I am learning to deal with VueJS and made a simple todo app. It works well, but I want to store data locally and make it persistent even if there is a page reload.
This is the code produced following instruction of a few useful tutorials (leaving CSS outside to ease readability):
<template>
<div class="main-container">
<div class="header md-elevation-4">
<h1 class="md-title">{{ header }}</h1>
</div>
<div class="todo-list">
<div class="row"></div>
<div class="row">
<!-- add new todo with enter key -->
<md-field class="todo-input">
<md-input
v-model="currentTodo"
#keydown.enter="addTodo()"
placeholder="Add a todo! It's easy!"
/>
</md-field>
<!-- for all todos, set class of edited todos -->
<ul class="todos">
<div class="list-div">
<li v-for="todo in todos" :key="todo.id">
<!-- binds checkbox to todo model after each instance; -->
<input
class="toggle-todo"
type="checkbox"
v-model="todo.completed"
/>
<!-- starts editing process on double click -->
<span
class="todo-item-label"
:class="{ completed: todo.completed }"
#dblclick="editTodo(todo)"
v-if="!todo.edit"
>{{ todo.label }}</span
>
<!-- concludes editing with enter click -->
<input
v-else
class="todo-item-edit"
type="text"
v-model="todo.label"
#keyup.enter="completedEdit(todo)"
/>
<!-- deletes todos using removeTodo method -->
<i
class="material-icons"
alt="remove toDo"
#click="removeTodo(todo)"
>delete</i
>
</li>
</div>
</ul>
</div>
</div>
</div>
</template>
<script>
export default {
name: "RegularToolbar",
data() {
return {
header: "A VueJS ToDo App",
todos: [],
currentTodo: "",
completed: false, // add a completed property
editedToDo: null // add a edited property
};
},
methods: {
addTodo() {
this.todos.push({
id: this.todos.length,
label: this.currentTodo,
completed: false, // initializes property
edit: false // initializes property
});
this.currentTodo = "";
},
mounted() {
// console.log('App mounted!');
if (localStorage.getItem("todos"))
this.todos = JSON.parse(localStorage.getItem("todos"));
},
watch: {
todos: {
handler() {
// console.log('Todos changed!');
localStorage.setItem("todos", JSON.stringify(this.todos));
},
deep: true
}
},
removeTodo(todo) {
// allows users to remove todos
var index = this.todos.indexOf(todo);
this.todos.splice(index, 1);
},
editTodo(todo) {
todo.edit = true;
},
completedEdit(todo) {
todo.edit = false;
}
}
};
</script>
<style lang="scss" scoped></style>
As it is, all the JS part referring to mount and watch does not work. I am able to add new todos and delete them as I wish, but it does not retain the data if the user reloads the page.
Could some of the colleagues spot what I am missing here?
It's a problem of code organization:
Your mounted and watch sections are currently inside the methods section, which will prevent them from firing as expected.
Move those out into their own sections (both should be at the same level as methods) and you should be all set.
With those edits and nothing else, I've got your code working in a fiddle here: https://jsfiddle.net/ebbishop/dc82unyb/
I'm trying to fetch product data from a JSON file, but can't get it to work.
I've tried several things and searched the internet for a solution but none of the examples on the internet equals my situation.
I'm new to both vue and axios, so please excuse my ignorance.
This is what I have so far:
Vue.component('products',{
data: {
results: []
},
mounted() {
axios.get("js/prods.json")
.then(response => {this.results = response.data.results})
},
template:`
<div id="products">
<div class="productsItemContainer" v-for="product in products">
<div class="productsItem">
<div class="">
<div class="mkcenter" style="position:relative">
<a class="item">
<img class="productImg" width="120px" height="120px" v-bind:src="'assets/products/' + product.image">
<div class="floating ui red label" v-if="product.new">NEW</div>
</a>
</div>
</div>
<div class="productItemName" >
<a>{{ product.name }}</a>
</div>
<div class="mkdivider mkcenter"></div>
<div class="productItemPrice" >
<a>€ {{ product.unit_price }}</a>
</div>
<div v-on:click="addToCart" class="mkcenter">
<div class="ui vertical animated basic button" tabindex="0">
<div class="hidden content">Koop</div>
<div class="visible content">
<i class="shop icon"></i>
</div>
</div>
</div>
</div>
</div>
</div>
`
})
new Vue({
el:"#app",
});
The json file is as follows
{
"products":[
{
"name": "Danser Skydancer",
"inventory": 5,
"unit_price": 45.99,
"image":"a.jpg",
"new":true
},
{
"name": "Avocado Zwem Ring",
"inventory": 10,
"unit_price": 123.75,
"image":"b.jpg",
"new":false
}
]
}
The problem is only with the fetching of the data from a JSON file, because the following worked:
Vue.component('products',{
data:function(){
return{
reactive:true,
products: [
{
name: "Danser Skydancer",
inventory: 5,
unit_price: 45.99,
image:"a.jpg",
new:true
},
{
name: "Avocado Zwem Ring",
inventory: 10,
unit_price: 123.75,
image:"b.jpg",
new:false
}
],
cart:0
}
},
template: etc.........
As the warnings suggest, please do the following:
Rename the data array from results to products since you are referencing it by the latter one as a name during render.
Make your data option a function returning an object since data option must be a function, so that each instance can maintain an independent copy of the returned data object. Have a look at the docs on this.
Vue.component('products', {
data() {
return {
products: []
}
},
mounted() {
axios
.get("js/prods.json")
.then(response => {
this.products = response.data.products;
});
},
template: `
//...
`
}
<div id="products">
<div class="productsItemContainer" v-for="product in products">
<div class="productsItem">
...
Also, since you're not using CDN (I think), I would suggest making the template a component with a separate Vue file rather than doing it inside template literals, something like that:
Products.vue
<template>
<div id="products">
<div class="productsItemContainer" v-for="product in products">
<div class="productsItem">
<!-- The rest of the elements -->
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Products',
data() {
return {
products: []
}
},
mounted() {
axios
.get("js/prods.json")
.then(response => {
this.products = response.data.products;
});
}
}
</script>
And then in your main JS file or anywhere else requiring this component:
import Products from './components/Products.vue';
new Vue({
el: '#app',
data() {
return {
//...
}
},
components: {
Products
}
})
<div id="app">
<Products />
</div>
Whenever I execute below code above error is shown. please help. I am a beginner in Vue.js so I do not have enough practice of Vue. I am trying to show different card on different button click.
<div v-for="item in cards">
<cards v-bind:if="cat==item.id" title="item.title" text="item.text"
src="item.src"></cards>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
cards:[
{'id':'0','title': 'BASED ON YOUR READING HISTORY Learn these neat
JavaScript tricks in less than 5 minutes', 'text':'Some quick
example text to build on the card title and make up the bulk of
the card', 'src':'', 'age_group':'pregnancy'},
],
cat:'0'
}
},
})
Vue.component('cards', {
props: ['title','source','text'],
template: '<div class="container-fluid" ><div class="card"
style="width:40%"><img style="float:right;" src={{source}}
alt="Card image cap"><div class="card-body"><h3 style="padding-
right:30%;">{{title}}</h5><p class="card-text" style="padding-
right:30%;">{{text}}</p><a href="#" class="btn btn-primary"
style="float:right;">Go somewhere</a></div></div></div>'
})
</script>
I am learning to use vue js with wprest api by following watch-learn tutorials on the same topic. the problem is that the vue js version used in the tutorial seems to be v 1.x and i started using vue js 2.x. I was able to figure out the initial stages on how to display all the post using vue js 2.x.. I have an input field using which I search for a specific title it should filter and show the post. The issue is unable to workout exactly how it needs to be computed using vuejs 2.x.. I have included a codepen link containing the json data as well as my working code.
the following is the input field to be used to filter the posts by title
<div class="row">
<h4>Filter by name:</h4>
<input type="text" name="" v-model="nameFilter">
</div>
<div class="row">
<div class="col-md-4" v-for="post in posts">
<div class="card post">
<img class="card-img-top" v-bind:src="post.fi_300x180" >
<div class="card-body">
<h2 class="card-text">{{ post.title.rendered }}</h2>
<small class="tags" v-for="category in post.cats">{{ category.name }}</small>
</div>
</div> <!-- .post -->
</div> <!-- .col-md-4 -->
</div> <!-- .row -->
https://codepen.io/dhivyasolomon/pen/LdZKJY
I would appreciate any help in figuring out the next step. thanks.
You don't need directives, achieve this using the power of Computed properties
So you will have to itarate over the computed property which filter the posts by the input value and return a new array of posts.
Little example:
new Vue({
el: '#example',
computed: {
filteredPosts () {
return this.posts.filter(p => p.title.toLowerCase().includes(this.filterText.toLowerCase()))
}
},
data () {
return {
filterText: '',
posts: [
{
title: 'My first post title',
body: 'foo'
},
{
title: 'Another post title',
body: 'foo'
},
{
title: 'This will work fine',
body: 'foo'
},
{
title: 'Omg it\'s working!',
body: 'foo'
}
]
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.15/vue.js"></script>
<div id="example">
<input type="text" v-model="filterText" />
<ul>
<li v-for="post in filteredPosts">{{ post.title }}</li>
</ul>
</div>