Vue Copy Paste feature, Vue list rendering - javascript

When I type some text into the input field and then hit enter it adding into the list with a button with the title "Copy"
Now I want when I click on the next to input it copies that input.
But its only copy the element with an index of 1
you can run this code
Please check this and let me know What is the issue
Thank you
new Vue({
el: "#app",
data: {
myInput: '',
items: []
},
methods: {
AddNew() {
this.items.push(this.myInput)
this.myInput= ""
},
copyText(index) {
console.log('1 =',this.items[index])
var textToCopy = document.querySelector(`.obj${this.items.indexOf(index)}`)
console.log( '2 =', textToCopy);
textToCopy.select()
document.execCommand("copy");
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-on:keyup.enter="AddNew" v-model="myInput">
<div v-for="(item, index) in items" :key="index">
<input :class="['obj-' + index]" :value="item"> {{index}}
<button v-on:click=" copyText(index)">copy</button>
</div>
</div>

You modify an array so it's not reliable to use an index as a key and to find an item to copy.
At least pass the item itself to copyText method:
<div v-for="(item, index) in items" :key="index">
<input :class="['obj-' + index]" :value="item"> {{index}}
<button v-on:click="copyText(item)">copy</button>
</div>
...
copyText(item) {
console.log('1 =',item)
var textToCopy = document.querySelector(`.obj${item}`)
console.log( '2 =', textToCopy);
textToCopy.select()
document.execCommand("copy");
}

You have a couple of errors where you select the element:
var textToCopy = document.querySelector(`.obj${this.items.indexOf(index)}`)
Should be this:
var textToCopy = document.querySelector(`.obj-${index}`)
You're missing a hyphen after obj and, as you're already passing the index, there's no need to use indexOf.
new Vue({
el: "#app",
data: {
myInput: '',
items: []
},
methods: {
AddNew() {
this.items.push(this.myInput);
this.myInput = "";
},
copyText(index) {
console.log('1 =',this.items[index])
var textToCopy = document.querySelector(`.obj-${index}`)
console.log( '2 =', textToCopy);
textToCopy.select()
document.execCommand("copy");
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-on:keyup.enter="AddNew" v-model="myInput">
<div v-for="(item, index) in items" :key="index">
<input :class="['obj-' + index]" :value="item"> {{index}}
<button v-on:click="copyText(index)">copy</button>
</div>
</div>

Related

How to Computed Two Results For Two Target Element in Vue.js

Can someone please let me know if there is a way to have two result() in Vue computed based on element id?
For example I want return into #and a result which is replaced comma with AND
and an other result for #dash which in this result is replaced with -
new Vue({
el: '#demo',
data() {
return {
target: '',
}
},
computed: {
result() {
//if it is going to #and
// return '`' + this.target.replaceAll(",", "` AND `") + '`' // If {{ result }} is #and
// if it is going to #dash
return '`' + this.target.replaceAll(",", "` - `") + '`' // If {{ result }} is #dash
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<p id="and">You Entered {{ result }}</p>
<p id="dash">You Entered {{ result }}</p>
<input v-model="target" placeholder="Enter Your Target" />
</div>
You can create a function instead:
new Vue({
el: '#demo',
data() { return { target: '' } },
methods: {
getSeperatedInputVal(sep) {
return this.target &&
this.target.split(',').map(str => '`'+ str + '`').join(sep);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<p id="and">You Entered {{ getSeperatedInputVal(" AND ") }}</p>
<p id="dash">You Entered {{ getSeperatedInputVal(" - ") }}</p>
<input v-model="target" placeholder="Enter Your Target" />
</div>

VueJS: How to use Google places API with multiple refs

I'm using the Google places API for my work and everything works fine. However, I have a unique scenario where I need to have more than one delivery address, hence I'll need to make Google place API works that way.
Here is my current code:
<template>
<div>
<div v-for="(search, index) in searchInput" :key="index">
<input type="text" ref="search" v-model="search.city">
</div>
<button type="button" #click="addMore">Add more</button>
</div>
</template>
<script>
export default {
name: "App",
data: () => ({
location: null,
searchInput: [
{
city: ""
}
]
}),
mounted() {
window.checkAndAttachMapScript(this.initLocationSearch);
},
methods: {
initLocationSearch() {
let autocomplete = new window.google.maps.places.Autocomplete(
this.$refs.search
);
autocomplete.addListener("place_changed", function() {
let place = autocomplete.getPlace();
if (place && place.address_components) {
console.log(place.address_components);
}
});
},
addMore() {
this.searchInput.push({ city: "" });
}
}
};
</script>
I get this error from the API: InvalidValueError: not an instance of HTMLInputElement and b.ownerDocument is undefined
How do I make the refs unique for the individual item that I add and then pass it to the initLocationSearch method? Please, any help would be appreciated.
Her is a Codesandbox demo
Given the details you provided for the complete scenario, here is how I would do it.
I would use a simple counter to keep track of the number of inputs and set its initial value to 1 and also use it to display the inputs.
<div v-for="(n, index) in this.counter" :key="index">
<input type="text" ref="search">
</div>
Then bind the Autocomplete widget to the last added input, based on the counter.
autocomplete = new window.google.maps.places.Autocomplete(
this.$refs.search[this.counter - 1]
);
And use your addMore() method to increment the counter.
addMore() {
this.counter++;
}
Use updated() for whenever a new input is added to the DOM to trigger the initLocationSearch() method.
updated() {
this.initLocationSearch();
},
On place_changed event, update the list of selected cities based on the various input values.
this.selectedCities = this.$refs["search"].map(item => {
return { city: item.value };
});
This way you always have an up to date list of cities, whatever input is changed. Of course you would need to handle emptying an input and probably other edge cases.
Complete code:
<template>
<div>
<div v-for="(n, index) in this.counter" :key="index">
<input type="text" ref="search">
</div>
<button type="button" #click="addMore">Add more</button>
<hr>Selected cities:
<ul>
<li v-for="(item, index) in this.selectedCities" :key="index">{{ item.city }}</li>
</ul>
</div>
</template>
<script>
export default {
name: "App",
data: () => ({
selectedCities: [],
counter: 1
}),
mounted() {
window.checkAndAttachMapScript(this.initLocationSearch);
},
updated() {
this.initLocationSearch();
},
methods: {
setSelectedCities() {
this.selectedCities = this.$refs["search"].map(item => {
return { city: item.value };
});
},
initLocationSearch() {
let self = this,
autocomplete = new window.google.maps.places.Autocomplete(
this.$refs.search[this.counter - 1],
{
fields: ["address_components"] // Reduce API costs by specifying fields
}
);
autocomplete.addListener("place_changed", function() {
let place = autocomplete.getPlace();
if (place && place.address_components) {
console.log(place);
self.setSelectedCities();
}
});
},
addMore() {
this.counter++;
}
}
};
</script>
CodeSandbox demo
You can dynamically bind to ref. Something like :ref="'search' + index"
<template>
<div>
<div v-for="(search, index) in searchInput" :key="index">
<input type="text" :ref="'search' + index" v-model="search.city">
</div>
<button type="button" #click="addMore">Add more</button>
</div>
</template>
Then in addMore, before creating a pushing on a new object to searchInput, grab the last index in searchInput. You can then get the newly created ref and pass that to initLocationSearch.
initLocationSearch(inputRef) {
let autocomplete = new window.google.maps.places.Autocomplete(
inputRef
);
autocomplete.addListener("place_changed", function() {
let place = autocomplete.getPlace();
if (place && place.address_components) {
console.log(place.address_components);
}
});
},
addMore() {
const lastInputRef = this.$refs['search' + this.searchInput.length - 1];
this.initLocationSearch(lastInputRef);
this.searchInput.push({ city: "" });
}

Vue.js and input's value that is never displayed

I'm making a simple shopping list app in Vue.js and I was curious if there's a standard way of doing what I need to do. I have a list of items with add and delete buttons:
const app = new Vue({
el: '#app',
data: {
items: [
'Chocolate',
'Pizza',
'Coca-Cola',
],
newItem: ''
},
template: `
<div>
<div>{{ items.length }} item{{ items.length !== 1 ? 's' : '' }}</div>
<ul>
<li v-for="(item, index) of items">
{{ item }}
<button #click="deleteItem(index)">X</button>
</li>
<li>
<input type="text" v-model="newItem" placeholder="Item name">
<button #click="addItem">+</button>
</li>
</ul>
</div>
`,
methods: {
addItem() {
const item = this.newItem.trim();
if (item === '') return;
this.items.push(item);
this.newItem = '';
},
deleteItem(index) {
this.items.splice(index, 1);
}
}
});
It works just as it should, but I'm not sure about using data entry that is never displayed anywhere. There's also another approach with $refs:
const app = new Vue({
el: '#app',
data: {
items: [
'Chocolate',
'Pizza',
'Coca-Cola',
],
},
template: `
<div>
<div>{{ items.length }} item{{ items.length !== 1 ? 's' : '' }}</div>
<ul>
<li v-for="(item, index) of items">
{{ item }}
<button #click="deleteItem(index)">X</button>
</li>
<li>
<input type="text" placeholder="Item name" ref="newItem">
<button #click="addItem($refs.newItem.value)">+</button>
</li>
</ul>
</div>
`,
methods: {
addItem(item) {
item = item.trim();
if (item === '') return;
this.items.push(item);
this.$refs.newItem.value = '';
},
deleteItem(index) {
this.items.splice(index, 1);
}
}
});
Instead of using separate data entry and v-model, I'm using $refs directly. Is any of these approaches more widely accepted in Vue.js community or guidelines? Or perhaps there's even more popular way?
I just wanted to share my views here. Personally I like to use v-model as it provides few added benefits like:
We can use .trim modifier with v-model which automatically trims whitespace from user input like:
<input v-model.trim="msg">
This way you don't need to write additional code to trim text like item = item.trim();. Few lines of code saved here.
Using this.newItem = '' we can easily clear out the previously entered text after button click using v-model reactivity feature. So, again less line of code instead of doing this.$refs.newItem.value = '';
Another advantage of using v-model is that, instead of doing
<button #click="addItem($refs.newItem.value)">
You can simply call the function like:
<button #click="addItem">
So, you can see these are the few benefits of using a simple v-model, which is mostly related to the developer experience (DX) point of view.
Working Demo:
const app = new Vue({
el: '#app',
data: {
items: [
'Chocolate',
'Pizza',
'Coca-Cola',
],
newItem: ''
},
template: `
<div>
<div>{{ items.length }} item{{ items.length !== 1 ? 's' : '' }}</div>
<ul>
<li v-for="(item, index) of items">
{{ item }}
<button #click="deleteItem(index)">X</button>
</li>
<li>
<input type="text" placeholder="Item name" v-model.trim="newItem">
<button #click="addItem">+</button>
</li>
</ul>
</div>
`,
methods: {
addItem() {
if (this.newItem === '') return;
this.items.push(this.newItem);
this.newItem = '';
},
deleteItem(index) {
this.items.splice(index, 1);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
<div id="app">
</div>

How to clean input value after submitting it in Vuejs?

I am doing a to-do list in Vuejs. When I enter a new value in the input field, it seems that I can't make the input value to reset. How can I accomplish this?
I have tried to grab the input value & reset it to an empty string, but I haven't had any luck.
HTML Code:
<div id="app">
<h1>{{ message }}</h1>
<form v-on:submit="addNewTodo">
<input class="input-value" v-model="todo.task" type="text">
<button type="submit">Add todo</button>
</form>
<ul>
<li v-for="todo in todos" :class="{ completed: todo.isActive }" #click="$set(todo, 'isActive', !todo.isActive)">
{{ todo.task }} <span v-on:click="deleteTodo">{{ todo.delete }}</span>
</li>
</ul>
</div>
JS Code:
var app = new Vue({
el: '#app',
data: {
message: 'List of things to do today',
todos: [
{ task: 'Have breakfast', delete:'(x)'},
{ task: 'Go to the gym', delete:'(x)'},
{ task: 'Study Vuejs', delete:'(x)'}
],
todo: {task: '', delete: '(x)'}
},
methods: {
addNewTodo: function(e){
e.preventDefault();
this.todos.push( this.todo );
var inputValue = document.querySelectorAll('.input-value').value;
inputValue = ''
},
deleteTodo: function(){
this.todos.shift( this.todo )
}
}
});
Your input value is two-way binded with todo.task so you could do the following after adding a new todo task.
this.todo.task = ''
Try to flush it like :
addNewTodo: function(e){
e.preventDefault();
this.todos.push( this.todo );
this.todo.task=""
}

Binding property model value to array with the value from checkbox and inputbox

I am implementing the v-for as follows:
new Vue({
el: '#app',
data: {
PaymentType:[
{Id: 1, Text:"Cash"},
{Id: 2, Text:"Check"},
{Id: 3, Text:"Paypal"}
],
SelectedType:[]
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p> SelectedType {{ SelectedType }}</p>
<div v-for="(pay, index) in PaymentType">
<input type="checkbox" v-model="SelectedType" :value="{PayTypeId: pay.Id}" />
{{pay.Text}}
<input type="input" v-model="SelectedType" />
<!-- What goes here on :value attributes? -->
</div>
</div>
<div id="app">
<p> SelectedType {{ SelectedType }}</p>
<div v-for="(pay, index) in PaymentType">
<input type="checkbox" v-model="SelectedType" :value="{PayTypeId: pay.Id}"/> />
{{pay.Text}}
<input type="input" v-model="SelectedType" :value=""/>
<!-- What goes here on :value attributes? -->
</div>
</div>
Initially SelectedType is empty array object.
Is there any way in Vue to push the Id's to the PayTypeId and the value from the input to Remarks, on same array objects?
new Vue({
el: '#app',
data: {
PaymentType:[
{Id: 1, Text:"Cash"},
{Id: 2, Text:"Check"},
{Id: 3, Text:"Paypal"}
],
SelectedType:[]
}
})
SelectedType is an array of object, it signature looks like SelectedType:[{PayTypeId:0, Remarks:''}].
Is it possible to map the value from checkbox and input box to a Array of Object?
What you need is a bit complicated. The cleanest solution I see is to:
track the checked checkboxes and typed remarks separately, and
turning SelectedType into a computed property that merges those values.
In the example below the checked checkboxes are in selectePayTypeIds and the typed remarks are in typedRemarks.
Notice SelectedType is no longer in data but now in computed.
new Vue({
el: '#app',
data: {
PaymentType:[
{Id: 1, Text:"Cash"},
{Id: 2, Text:"Check"},
{Id: 3, Text:"Paypal"}
],
selectePayTypeIds: {},
typedRemarks: {}
},
computed: {
SelectedType: function () {
var vm = this;
return Object.keys(vm.selectePayTypeIds).filter(function (selectedPayTypeId) {
return vm.selectePayTypeIds[selectedPayTypeId];
}).map(function (selectedPayTypeId) {
return {PayTypeId: selectedPayTypeId, Remarks: vm.typedRemarks[selectedPayTypeId] || ''}
});
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p> SelectedType {{ SelectedType }}</p>
<div v-for="(pay, index) in PaymentType">
<input type="checkbox" v-model="selectePayTypeIds[pay.Id]" />
{{pay.Text}}
<input type="input" v-model="typedRemarks[pay.Id]">
<!-- What goes here on :value attributes? -->
</div>
</div>
Making the SelectedType computed property editable/assignable again
Since you actually want to also be able to assign values to the SelectedType property, we need to define a set function in the SelectedType computed.
To show an example of value being assigned to the SelectedType, have a look at the created function below.
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
PaymentType:[
{Id: 1, Text:"Cash"},
{Id: 2, Text:"Check"},
{Id: 3, Text:"Paypal"}
],
selectePayTypeIds: {},
typedRemarks: {}
},
created: function () {
this.SelectedType = [{PayTypeId: 2, Remarks: 'remarks about two'}];
},
computed: {
SelectedType: {
get: function () {
var vm = this;
return Object.keys(vm.selectePayTypeIds).filter(function (selectedPayTypeId) {
return vm.selectePayTypeIds[selectedPayTypeId];
}).map(function (selectedPayTypeId) {
return {PayTypeId: selectedPayTypeId, Remarks: vm.typedRemarks[selectedPayTypeId] || ''}
});
},
set: function (newSelectedType) {
var vm = this;
newSelectedType.forEach(function(sType) {
Vue.set(vm.selectePayTypeIds, sType.PayTypeId, true);
Vue.set(vm.typedRemarks, sType.PayTypeId, sType.Remarks);
});
}
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p> SelectedType {{ SelectedType }}</p>
<div v-for="(pay, index) in PaymentType">
<input type="checkbox" v-model="selectePayTypeIds[pay.Id]" />
{{pay.Text}}
<input type="input" v-model="typedRemarks[pay.Id]">
<!-- What goes here on :value attributes? -->
</div>
</div>

Categories

Resources