Dynamic v-model problem on nuxt application - vuejs2

I am trying to create a dynamic v-model input. All seems well except for the following.
The on click event that triggers the checkAnswers method only works if you click out of the input then click back into the input and then press the button again. It should trigger when the button is pressed the first time.
Does anyone have any ideas? Thanks in advance.
<template>
<div class="addition container">
<article class="tile is-child box">
<div class="questions">
<ul v-for="n in 5">
<li>
<p>{{ randomNumberA[n] }} + {{ randomNumberB[n] }} = </p>
<input class="input" type="text" maxlength="8" v-model.number="userAnswer[n]">
<p>{{ outcome[n] }}</p>
</li>
</ul>
</div>
<div class="button-container">
<button #click="checkAnswers" class="button">Submit Answer</button>
</div>
</article>
</div>
</template>
<script>
export default {
data() {
return {
randomNumberA: [] = Array.from({length: 40}, () => Math.floor(Math.random() * 10)),
randomNumberB: [] = Array.from({length: 40}, () => Math.floor(Math.random() * 10)),
userAnswer: [],
outcome: [],
}
},
methods: {
checkAnswers() {
for (var i = 0; i < 6; i++) {
if (this.userAnswer[i] === (this.randomNumberA[i] + this.randomNumberB[i])) {
this.outcome[i] = 'Correct';
} else {
this.outcome[i] = 'Incorrect';
}
}
}
}
}
</script>

You have some basic issues with your use of the template syntax here. According to the vue docs:
One restriction is that each binding can only contain one single
expression, so the following will NOT work: {{ var a = 1 }}
If you want to populate your arrays with random numbers you would be better calling a function on page mount. Something like this:
mounted() {
this.fillArrays()
},
methods: {
fillArrays() {
for (let i = 0; i < 5; i++) {
this.randomNumberA.push(Math.floor(Math.random() * 10))
this.randomNumberB.push(Math.floor(Math.random() * 10))
this.answer.push(this.randomNumberA[i] + this.randomNumberB[i])
}
}
}
Then you can use template syntax to display your arrays.
It looks like you are setting up a challenge for the user to compare answers so I think you'd be better to have a function called on input: Something like:
<input type="whatever" v-model="givenAnswer[n-1]"> <button #click="processAnswer(givenAnswer[n-1])>Submit</button>
Then have a function to compare answers.
Edit
I have basically rewritten your whole page. Basically you should be using array.push() to insert elements into an array. If you look at this you'll see the randomNumber and answer arrays are populated on page mount, the userAnswer array as it is entered and then the outcome on button click.
<template>
<div>
<div >
<ul v-for="n in 5">
<li>
<p>{{ randomNumberA[n-1] }} + {{ randomNumberB[n-1] }} = </p>
<input class="input" type="text" maxlength="8" v-model.number="userAnswer[n-1]">
<p>{{ outcome[n-1] }}</p>
</li>
</ul>
</div>
<div class="button-container">
<button #click="checkAnswers" class="button">Submit Answers</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
randomNumberA: [],
randomNumberB: [],
answer: [],
userAnswer: [],
outcome: [],
}
},
mounted() {
this.fillArrays()
},
methods: {
checkAnswers() {
this.outcome.length = 0
for (var i = 0; i < 6; i++) {
if (this.userAnswer[i] === this.answer[i]) {
this.outcome.push('Correct');
} else {
this.outcome.push('Incorrect');
}
}
},
fillArrays() {
for (let i = 0; i < 5; i++) {
this.randomNumberA.push(Math.floor(Math.random() * 10))
this.randomNumberB.push(Math.floor(Math.random() * 10))
this.answer.push(this.randomNumberA[i] + this.randomNumberB[i])
}
}
}
}
</script>

Related

v-on does not working why not receive $emit in custom component

I'm create a an component which represents my money field.
My target is on add element in list, set zero on money field to add next element in list...
But, my problem is that not working when send using $emit event to clear input to improve usability.
$emit works as described on image bellow
My money field:
<template>
<div class="input-group" #clear="clearInputField()">
<span>{{ title }}</span>
<input ref="displayMoney" type="text" v-model="displayMoney" #focus="isActive = true" #blur="isActive = false" />
</div>
</template>
<script>
export default {
props: {
title: String,
},
data() {
return {
money: 0,
isActive: false,
};
},
methods: {
clearInputField() {
console.log("Its work event");
this.money = 0;
this.displayMoney = "";
},
},
computed: {
displayMoney: {
get: function () {
if (this.isActive) {
return this.money;
} else {
return this.money.toLocaleString("pt-br", { style: "currency", currency: "BRL" });
}
},
set: function (modifiedMoney) {
let newMoney = parseFloat(modifiedMoney.replace(/[^\d.]/g, "."));
if (isNaN(newMoney) || newMoney.length == 0) {
newMoney = 0;
}
this.$emit("input", newMoney);
return (this.money = parseFloat(newMoney));
},
},
},
};
</script>
My principal component
<template>
<div class="wish-list">
<div class="row">
<div class="input-group">
<span>Digite sua meta: </span>
<input ref="descriptionWish" type="text" v-model="descriptionWish" />
</div>
<MoneyField title="Valor (R$): " v-model="valueWish" #keyup.native.enter="addWish" />
<button id="btnCalculate" #click="addWish()">Adicionar a lista de desejos</button>
</div>
<div class="list-items">
<ul>
<li v-for="wish in wishes" :key="wish">{{ wish }}</li>
</ul>
</div>
</div>
</template>
<script>
import MoneyField from "./Fields/MoneyField";
export default {
components: {
MoneyField,
},
data() {
return {
wishes: [],
valueWish: 0,
descriptionWish: "",
};
},
methods: {
addWish() {
if (!isNaN(this.valueWish) && this.valueWish > 0 && this.descriptionWish.length > 0) {
this.wishes.push(
`${this.descriptionWish} => ${this.valueWish.toLocaleString("pt-BR", { currency: "BRl", style: "currency" })}`
);
this.descriptionWish = "";
console.log("addWish");
this.valueWish = 0;
this.$emit("clear");
this.$refs.descriptionWish.focus();
}
this.valueWish = 0;
},
},
};
</script>
I still don't understand much about vueJS, but I believe it's something related to parent and child elements, but I've done numerous and I can't get my answer.
sorry for my bad english .
The emit sends an event from the child to the parent component not as you've done, to run a method from the child component you could add a ref in the component inside the parent one like :
<MoneyField title="Valor (R$): "
ref="moneyField" v-model="valueWish" #keyup.native.enter="addWish" />
then run this.$refs.moneyField.clearInputField() instead this.$emit("clear")

Vue Component Select option from list item

I'm new to Vue. I have an autocomplete component that used AXIOS to get the data. When I type in at least 3 characters, it does display the results in a list item, however I can't seem to figure out how to actually select the option and populate the text field.
Here is the code
<template>
<div>
<input type="text" placeholder="Source client" v-model="query" v-on:keyup="autoComplete" #keydown.esc="clearText" class="form-control">
<div class="panel-footer" v-if="results.length">
<ul class="list-group">
<li class="list-group-item" v-for="result in results">
<span> {{ result.name + "-" + result.oid }} </span>
</li>
</ul>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default{
data(){
return {
selected: '',
query: '',
results: []
}
},
methods: {
clearText(){
this.query = ''
},
autoComplete(){
this.results = [];
if(this.query.length > 2){
axios.get('/getclientdata',{params: {query: this.query}}).then(response => {
this.results = response.data;
});
}
}
}
}
</script>
Any help would be appreciated!
Writing an autocomplete is a nice challenge.
You want this component to act like an input. That means that if you'd use your autocomplete component, you will probably want to use it in combination with v-model.
<my-autocomplete v-model="selectedModel"></my-autocomplete>
To let your component work in harmony with v-model you should do the following:
Your component should accept a prop named value, this may be a string, but this may also be an object.
In the example above the value of selectedModel will be available inside your autocomplete component under '{{value}}' (or this.value).
To update the value supplied you $emit an input event with the selected value as second parameter. (this.$emit('input', clickedItem))
Inside your autocomplete you have query, which holds the search term. This is a local variable and it should be. Don't link it to value directly because you only want to change the value when a user selects a valid result.
So to make the example work you could do the following:
<template component="my-autocomplete">
<div>
<input type="text" placeholder="Source client" v-model="query" v-on:keyup="autoComplete" #keydown.esc="clearText" class="form-control">
<div class="panel-footer" v-if="results.length">
<ul class="list-group">
<li class="list-group-item" v-for="result in results" #click="selectValue(result)">
<span> {{ result.name + "-" + result.oid }} </span>
</li>
</ul>
</div>
</div>
</template>
<script>
//import axios from 'axios'
export default{
props: ['value'],
data(){
return {
selected: '',
query: '',
results: []
}
},
methods: {
clearText(){
this.query = ''
},
autoComplete(){
this.results = [];
if(this.query.length > 2){
this.results = [{ name: 'item 1', oid: '1' }, {name: 'item 2', oid: '2'}];
//axios.get('/getclientdata',{params: {query: this.query}}).then(response => {
//this.results = response.data;
//});
}
},
selectValue(selectedValue) {
this.$emit('input', selectedValue);
}
}
}
</script>
I commented out the axios part for demonstration purposes.
To verify the workings:
<template>
<div>Autocomplete demo:
<my-autocomplete v-model="selectedRow">
</my-autocomplete>
Selected value: <pre>{{selectedRow}}</pre>
</div>
</template>
<script>
export default {
data() {
return {
selectedRow: null
}
}
}
</script>
Now the autocomplete does what it should, it allows you to search and pick a valid result from the list.
One problem remains though. How do you display the selected value. Some options are:
Copy the value to query, this works seemlessly if all your options are strings
Copy the value of result.name to query, this would make the solution work
Accept a scoped slot from the parent component which is responsible for displaying the selected item.
I will demonstrate option 2:
<template component="my-autocomplete">
<div>
<input type="text" placeholder="Source client" v-model="query" v-on:keyup="autoComplete" #keydown.esc="clearText" class="form-control">
<div class="panel-footer" v-if="results.length">
<ul class="list-group">
<li class="list-group-item" v-for="result in results" #click="selectValue(result)">
<span> {{ result.name + "-" + result.oid }} </span>
</li>
</ul>
</div>
</div>
</template>
<script>
//import axios from 'axios'
export default{
props: ['value'],
data(){
return {
selected: '',
query: '',
results: []
}
},
mounted() {
if (this.value && this.value.name) {
this.query = this.value.name;
}
},
// Because we respond to changes to value
// we do not have to do any more work in selectValue()
watch: {
value(value) {
if (this.value && this.value.name) {
this.query = this.value.name;
} else {
this.query = '';
}
}
},
methods: {
clearText(){
this.query = ''
},
autoComplete(){
this.results = [];
if(this.query.length > 2){
this.results = [{ name: 'item 1', oid: '1' }, {name: 'item 2', oid: '2'}];
//axios.get('/getclientdata',{params: {query: this.query}}).then(response => {
//this.results = response.data;
//});
}
},
selectValue(selectedValue) {
this.$emit('input', selectedValue);
}
}
}
</script>
A working example can be found here: https://jsfiddle.net/jangnoe/4xeuzvhs/1/

Vue.js Autocomplete

I am building a live search component with Vuejs. The search is working as expected. On keyup the method populates an unordered list. I am at a loss but need to:
Click on a selection from the search results and populate the input with that name.
Get the selected id.
Any suggestions?
The View
<label>Vendor:</label>
<input type="text" v-model="vendor" v-on:keyup="get_vendors">
<div class="panel-footer" v-if="vendors.length">
<ul class="list-group">
<li class="list-group-item for="vendor in vendors">
{{ vendor.vendor_name }}
</li>
</ul>
</div>
The Script
export default {
data: function() {
return {
vendor:'',
vendors: []
}
},
methods: {
get_vendors(){
this.vendors = [];
if(this.vendor.length > 0){
axios.get('search_vendors',{params: {vendor: this.vendor}}).then(response =>
{
this.vendors = response.data;
});
}
}
}
}
</script>
The Route
Route::get('search_vendors', 'vendorController#search_vendors');
The Controller
public function search_vendors(Request $request){
$vendors = vendor::where('vendor_name','LIKE','%'.$request->vendor.'%')->get();
return response()->json($vendors);
}
This is what I came up with. Works nicely.
The View
<label>Vendor:</label>
<input type="text" v-model="vendor" v-on:keyup="get_vendors" class="col-xl-6 form-control ">
<div class="panel-footer autocomplete-box col-xl-6">
<ul class="list-group">
<li v-for="(vendor,id) in vendors" #click="select_vendor(vendor)" class="list-group-item autocomplete-box-li">
{{ vendor.vendor_name }}
</li>
</ul>
</div>
The Script
export default {
data: function() {
return {
vendor:'',
vendor_id:'',
vendors: []
}
},
methods: {
select_vendor(vendor){
this.vendor = vendor.vendor_name
this.vendor_id = vendor.id
this.vendors = [];
},
get_vendors(){
if(this.vendor.length == 0){
this.vendors = [];
}
if(this.vendor.length > 0){
axios.get('search_vendors',{params: {vendor: this.vendor}}).then(response => {
this.vendors = response.data;
});
}
},
},
}
</script>
The Route
Route::get('search_vendors', 'vendorController#search_vendors');
The Controller
public function search_vendors(Request $request){
$vendors = vendor::where('vendor_name','LIKE','%'.$request->vendor.'%')->get();
return response()->json($vendors);
}
The CSS
.autocomplete-box-li:hover {
background-color: #f2f2f2;
}
.autocomplete-box{
position: absolute;
z-index: 1;
}
If you want to get the id of the vendor you can do it using vendor.vendor_id which should be present in the vendor object which is returned from the server and if you want to populate the vendors array with new options you can have a watch or a #change (binding) function to add the entered text field as the new vendor in the vendor array. Also, I would suggest that you download the vue extension as it will help you a lot in debugging
<label>Vendor:</label>
<input type="text" v-model="vendor" v-on:keyup="get_vendors">
<div class="panel-footer" v-if="vendors.length">
<ul class="list-group">
<li class="list-group-item for="(vendor,index) in vendors">
{{ index }}
{{ vendor.vendor_id }}
{{ vendor.vendor_name }}
</li>
</ul>
</div>

How to make pagination?

How to make pagination. I tried many times already, but I can’t do it. I have to paginate without Laravel. The problem is that I can not make a cycle that will display the number of pages, each page should have 10 posts, and there are 98 posts in total. I made the property to be calculated, thanks to which you can find out how many pages there will be. I made a page switch that works. But for some reason, the cycle with which I will display the number of pages does not work for me, I cannot understand what the problem is?
Screenshot
My Vue js:
import axios from 'axios';
export default {
name: 'app',
data () {
return{
counter: 1,
zero: 0,
posts: [],
createTitle: '',
createBody: '',
visiblePostID: '',
}
},
watch: {
counter: function(newValue, oldValue) {
this.getData()
}
},
created(){
this.getData()
},
computed: {
evenPosts: function(posts){
return Math.ceil(this.posts.length/10);
}
},
methods: {
getData() {
axios.get(`http://jsonplaceholder.typicode.com/posts?_start=${this.counter}+${this.zero}&_limit=10`).then(response => {
this.posts = response.data
})
},
// even: function(posts) {
// return Math.ceil(this.posts.length/10)
// },
deleteData(index, id) {
axios.delete('http://jsonplaceholder.typicode.com/posts/' + id)
.then(response => {
console.log('delete')
this.posts.splice(index, 1);
})
.catch(function(error) {
console.log(error)
})
},
addPost() {
axios.post('http://jsonplaceholder.typicode.com/posts/', {
title: this.createTitle,
body: this.createBody
}).then((response) => {
this.posts.unshift(response.data)
})
},
changePost(id, title, body) {
axios.put('http://jsonplaceholder.typicode.com/posts/' + id, {
title: title,
body: body
})
},
},
}
My html:
<div id="app">
<div class="smallfon">
<div class="blocktwitter"><img src="src/assets/twitter.png" class="twitter"/></div>
<div class="addTextPost">Add a post</div>
<input type="text" v-model="createTitle" class="created"/>
<input type="text" v-model="createBody" class="created"/>
<div><button #click="addPost()" class="addPost">AddPost</button></div>
<div class="post1">
<div class="yourPosts">Your Posts</div>
<ul>
<li v-for="(post, index) of posts" class="post">
<p><span class="boldText">Title:</span> {{ post.title }}</p>
<p><span class="boldText">Content:</span> {{ post.body }}</p>
<button #click="deleteData(index, post.id)" class="buttonDelete">Delete</button>
<button #click="visiblePostID = post.id" class="buttonChange">Change</button>
<div v-if="visiblePostID === post.id" class="modalWindow">
<div><input v-model="post.title" class="changePost"><input v-model="post.body" class="changePost"></div>
<button type="button" #click="changePost(post.id, post.title, post.body)" class="apply">To apply</button>
</div>
</li>
</ul>
<button type="button" #click="counter -=1" class="prev">Предыдущая</button>
<!-- <div class="counter">{{ counter }}</div> --> <span v-for="n in evenPosts" :key="n.id">{{ n }} </span>
<button type="button" #click="counter +=1" class="next">Следущая</button>
<!-- <span v-for="n in evenPosts" :key="n.id">{{ n }} </span> -->
</div>
</div>
</div>
If you bind a limit to your fetching request (axios.get(...&_limit=10)), you can't return a paginate count because your computed evenPost property will always return 1 i.e Math.ceil(10/10) == 1
To fix your pagination, remove the parameters query to get the data:
getData() {
axios.get('https://jsonplaceholder.typicode.com/posts').then(response => {
this.posts = response.data
})
}
Then change the default counter page to 0 and add a computed property to return 10 posts based on it:
data () {
return {
counter: 0,
//...
}
},
computed: {
paginatedPosts() {
const start = this.counter * 10;
const end = start + 10;
return this.posts.slice(start, end);
}
}
Now you can iterate on this property:
<ul>
<li v-for="(post, index) of paginatedPosts" class="post">
...
</li>
</ul>
Basic live example

updating specific component inside v-for loop - Vue js - laravel 5.6

I am struggling to find a solution for this problem: I am using Vue.js with Laravel 5.6 and fetching items then displaying them using Vue.
When clicking on the bid on this item button, i would like to update the data inside that < li > list item, such as the element with the ref property totalBids using the bidOnThis(id) method
Edited: updated the code to reflect ref property and the updated but still wrong bidOnThis(id) function
<template>
<div>
<ul class="row">
<li class="col-lg-4" v-for="auction in auctions" :key="auction.id">
<span>{{ auction.name }} {{ auction.id }}</span><br />
<span>{{ auction.bid_time }}</span><br />
<span ref="totalBids">{{ auction.total_bids }}</span><br />
<span ref="user">{{ auction.username }}</span><br />
<button ref="newBid" #click="bidOnThis(auction.id)">Bid on this item</button>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
auctions: [],
newBid: '',
totalBids: ''
};
},
created() {
axios.get('/auctions').then(result => {
this.auctions = result.data
})
},
methods: {
bidOnThis(id) {
axios.post('/auctions', { id: id });
this.auction.totalBids = '100';
}
}
};
this is where I am at, but doesn't work either
bidOnThis(id) {
axios.post('/auctions', { id: id });
this.auctions.$refs.totalBids.innerHTML = '100';
}
After a lot of searching around, i found this: Better method of using refs inside a v-for based on object
and have updated my code using key, index in my v-for loop, then referencing the key through the method
allowing me to use the key to reference the correct element
bidOnThis(auction.id, key)
and
this.$refs.totalBids[key].innerHTML = parseInt(this.$refs.totalBids[key].innerHTML) + 1 ;
See full code below:
<template>
<div>
<h1 ref="testing">0</h1>
<ul class="row">
<li class="col-lg-4" v-for="(auction, key, index) in auctions" :key="auction.id">
<span>{{ auction.name }} ({{ auction.id }})</span><br />
<span>{{ auction.bid_time }}</span><br />
<span ref="totalBids">{{ auction.total_bids }}</span><br />
<span ref="user">{{ auction.username }}</span><br />
<button ref="newBid" #click="bidOnThis(auction.id, key)">Bid on this item</button>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
auctions: [],
newBid: '',
totalBids: '',
testing: ''
};
},
created() {
axios.get('/auctions').then(result => {
this.auctions = result.data
})
},
methods: {
bidOnThis(id, key) {
axios.post('/auctions', { id: id });
this.$refs.totalBids[key].innerHTML = parseInt(this.$refs.totalBids[key].innerHTML) + 1 ;
}
}
};