VueJS - input file repeater - javascript

I want delete item from array but there are the same item and js delete last one !
let app = new Vue({
el: '#app',
data: {
items: []
},
methods: {
addItem() {
this.items.push('');
},
removeItem(index) {
this.items.splice(index, 1);
}
}
});
<script src="https://unpkg.com/vue#2.1.10/dist/vue.js"></script>
<div id="app">
<ul class="list-group">
<li class="list-group-item" v-for="(item , index) in items">
remove
<input name="form[]" type='file'>
</li>
</ul>
<button #click='addItem'>new item</button>
</div>
JSFiddle: https://jsfiddle.net/6hvbqju2/

Vue uses an "in-place patch strategy" when dealing with list of elements. This strategy is not suitable when relying on form input values.
When using v-for directive it is better to define a v-bind:key to give Vue a hint to track each node.
We'll store numbers in the items array and use them as a key. In your case you should use an item's property that can serve as a unique key.
let app = new Vue({
el: '#app',
data: {
counter: 0,
items: []
},
methods: {
addItem() {
this.items.push(this.counter++);
},
removeItem(index) {
this.items.splice(index, 1);
}
}
});
<script src="https://unpkg.com/vue#2.1.10/dist/vue.js"></script>
<div id="app">
<ul class="list-group">
<li class="list-group-item" v-for="(item , index) in items" :key="item">
remove
<input name="form[]" type='file'>
</li>
</ul>
<button #click='addItem'>new item</button>
</div>

Your codes working fine but,
This is because of file input auto complete behaviour
See this example
let app = new Vue({
el : '#app',
data : {
items: [],
},
methods : {
addItem() {
this.items.push({file: null});
console.log(this.items)
},
removeItem(index) {
this.items.splice(index,1);
},
handleChange(item, event){
item.file = event.target.files["0"];
}
}
});
.upload-btn-wrapper {
position: relative;
overflow: hidden;
display: inline-block;
vertical-align: middle;
}
.btn {
border: 1px solid gray;
color: gray;
background-color: white;
padding: 4px 10px;
border-radius: 4px;
font-size: 15px;
font-weight: bold;
}
.upload-btn-wrapper input[type=file] {
font-size: 100px;
position: absolute;
left: 0;
top: 0;
opacity: 0;
}
<script src="https://unpkg.com/vue#2.1.10/dist/vue.js"></script>
<div id="app">
<ul class="list-group">
<li class="list-group-item" v-for="(item , index) in items">
remove
<div type="button" class="upload-btn-wrapper">
<button class="btn">{{ item.file ? item.file.name : 'Choose File' }}</button>
<input name="form[]" type="file" #change="handleChange(item, $event)">
</div>
</li>
</ul>
<button #click='addItem'>new item</button>
</div>

Related

Replace values in an array dynamically

I have a scenario like this. Say: let a = [2,5,6] and let b = [1,2,3,4,5,6,7,8]. Array b is displayed in boxes and revealed when one clicks any of the boxes. What I am trying to do is, when one clicks on any box and the value is the same as any value in array a, I replace the value with other unique values and if they're not the same I display as it is.
e.g. If I click a box that has a value of 2 or 5 or 6 i replace the values with the other values.
A minimal example is:
new Vue({
el: "#app",
data: {
a: [2,5,6],
b: [1,2,3,4,5,6,7,8]
},
methods: {
replaceNumber() {
// function to replace the values
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
.numbers {
display: flex;
}
li {
list-style-type: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>Numbers:</h2>
<br/>
<ul class="numbers">
<li v-for="num in a">
{{num}}
</li>
</ul>
<br/>
<template>
<button #click="replaceNumber" v-for="number in b">
{{ number }}
</button>
</template>
</div>
Use indexOf() to locate the position of the element you want to replace.
Then use splice() together with the index you got to remove that element.
Then use splice() again to insert a new value to the same index.
Check the documentation of each method above to understand their syntax.
You can try with random numbers if found in first array i.e a
var a = [2,5,6]
var b = [1,2,3,4,5,6,7,8]
a.forEach(function(e){
$("#aDiv").append(`<h2>${e}</h2>`);
})
b.forEach(function(e){
$("#bDiv").append(`<h2 class="seconddiv">${e}</h2>`);
});
$(".seconddiv").on('click',function(){
let val= $(this).html();
if(a.includes(parseInt(val))){
var uniqueNo = 0;
do {
uniqueNo=getRandomInt(0,10);
}
while (a.includes(parseInt(uniqueNo)));
$(this).html(uniqueNo);
}
})
let getRandomInt= (x,y)=>x+(y-x+1)*crypto.getRandomValues(new Uint32Array(1))[0]/2**32|0
#aDiv,#bDiv{
color:yellow;
background-color:black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="maindiv">
<div id="aDiv">
</div>
<div id="bDiv" style="margin-top:50px;">
</div>
</div>

Vuejs populate input fields value on a element select

I am not sure if I am terming this correctly. I have a very simple vuejs application (I just started learning vue a couple of days ago, so my knowledge of vue is really limited).
I have a input field which acts as a search box. When we feed some text input, it triggers v-on:blur event to call a function. It then sets the suggestions which are displayed just below the searchbox.
What I am trying to achieve is, when any of those anchor tags are clicked (from the search suggestions), two new input boxes should be automatically populated with the values from the search suggestions.
{name: 'Some Name', state: 'Some State'}
A very simple and stripped version of the code is as https://jsfiddle.net/dfhpj08g/
new Vue({
el: "#app",
data: {
suggestions: [],
showSuggestions: false,
},
methods: {
suggest() {
// this is dynamically generated via ajax
this.suggestions = [{
name: 'A',
state: 'OH'
},
{
name: 'B',
state: 'OR'
},
];
this.showSuggestions = true;
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-on:blur="suggest" placeholder="search">
<div v-show="showSuggestions">
<span>Did you mean</span>
<li v-for="s in suggestions">
<a href="#">
{{s.name}} - ({{s.state}})
</a>
</li>
</div>
<input type="text" name="name" placeholder="name">
<input type="text" name="state" placeholder="state">
</div>
If you want to insert the values into your name and state fields, I would suggest using v-model on them and declaring the corresponding data in your component. In that way, you can simply set them using this.name and this.state:
data: {
suggestions: [],
showSuggestions: false,
name: '',
state: ''
},
Use v-model to bind name and state data to your input elements:
<input type="text" name="name" placeholder="name" v-model="name">
<input type="text" name="state" placeholder="state" v-model="state">
You can bind a click handler to each of the <a> elements, so that you will can pass the index of the clicked suggestion. This will allow you to do this.suggestion[i] to retrieve the data:
<li v-for="(s, i) in suggestions" v-bind:key="i">
<a href="#" v-on:click.prevent="suggestionSelected(i)">
{{s.name}} - ({{s.state}})
</a>
</li>
Then, in your methods, you can create a new function suggestionSelected, which accepts the index of the suggestion as i. In that way, you can use the bracket syntax to access the selected suggestion:
suggestionSelected(i) {
this.name = this.suggestions[i].name;
this.state = this.suggestions[i].state;
}
See proof-of-concept example below:
new Vue({
el: "#app",
data: {
suggestions: [],
showSuggestions: false,
name: '',
state: ''
},
methods: {
suggest() {
// this is dynamically generated via ajax
this.suggestions = [{
name: 'A',
state: 'OH'
},
{
name: 'B',
state: 'OR'
},
];
this.showSuggestions = true;
},
suggestionSelected(i) {
this.name = this.suggestions[i].name;
this.state = this.suggestions[i].state;
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-on:blur="suggest" placeholder="search">
<div v-show="showSuggestions">
<span>Did you mean</span>
<li v-for="(s, i) in suggestions" v-bind:key="i">
<a href="#" v-on:click.prevent="suggestionSelected(i)">
{{s.name}} - ({{s.state}})
</a>
</li>
</div>
<input type="text" name="name" placeholder="name" v-model="name">
<input type="text" name="state" placeholder="state" v-model="state">
</div>

searching for local storage items

I have a vue app which can add and edit todos in the local storage, what i am trying to do now is create a search for the item topic which is saved inside of the key value todos in the local storage by using computed in the code in order to filter for the data.
<div id="app">
<br>
<br>
<input type="text" v-model="search" style=" margin: 20px; padding: 10px;">
<br>
<ul>
<li v-for="(todo, index) in todos" style="border:2px solid blue; margin-bottom: 10px; width: 15%; position: relative; padding: 10px;">
<span v-if="todo"> topic:{{todo.topic}}<br> price:{{todo.price}}<br> loc:{{todo.place}}<br> time:{{todo.time_length}} </span>
<br>
<br>
</li>
</ul>
</div>
<script>
Vue.use(VueLocalStorage);
new Vue({
el: '#app',
data() {
return {
search: '',
todo: {
topic: null,
},
todos: null || [],
}
},
watch: {
todos: function (val) {
this.$ls.set('todos', val)
}
},
mounted() {
this.todos = this.$ls.get('todos', this.todos);
var _this = this;
this.$ls.on('todos', function (val) {
_this.todos = val;
});
},
computed: {
filteredList() {
return this.todo.filter(todos => {
return todos.topic.toLowerCase().includes(this.search.toLowerCase())
})
}
}
});
</script>

Vue: How do I generate a new instance of a div with a buttonclick?

I need to create a function that generates a new instance of a specific div (.container in the template) when a button (#addDiv) is clicked. The button should be the only thing visible on the webpage before that. How do I do?
I know that it may be possible to do this with document.appendChild. Is there a better way?
I am using toggle-function that works great, I have included it in the code to give you the whole picture.
Vue
Vue.component('dynamicdDvs', {
template: `
<div>
<div class="headerDiv">
<button class="addDiv" type="button" v-on:click="createDiv">Create</button>
</div>
<div class="container">
<div class="createdDiv">
<h2>I am dynamic!</h2>
<button class="minimize" v-on:click="expand">Make me smal</button>
</div>
<div class="createdDivMinimized" v-if="!displayDiv">
<p>I am a smal version of the created div!</p>
<button class="maximize" v-on:click="expand">Expand me</button>
</div>
</div>
</div>
`,
data:function () {
return {
displayDiv: false
}
},
methods: {
expand: function () {
this.displayDiv = !this.displayDiv;
},
createDiv: function() {
//The function that creates a new div, with the same code as
//.createdDiv and .createDivMinimized may be placed here
}
}
});
CSS
.headerDiv {
height: 100px;
width: 400px;
background-color: blue;
color: white
}
.createdDiv {
height: 300px;
width: 400px;
background-color: black;
color: white
}
.createdDivMinimized {
height: 300px;
width: 400px;
background-color: red;
color: white
}
HTML
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="parent">
<dynamicDivs></dynamicDivs>
</div>
So what you can do is set a number in data for the number of <div> elements and use v-for with a range. For example
data () {
return {
displayDiv: [false],
divs: 1
}
}
And in your template
<template v-for="n in divs">
<div class="createdDiv">
<!-- snip -->
<button class="minimize" v-on:click="displayDiv[n-1] = !displayDiv[n-1]">Make me small</button>
</div>
<div class="createdDivMinimized" v-if="!displayDiv[n-1]">
<!-- snip -->
</div>
</template>
and in your methods...
createDiv () {
this.divs++
this.displayDiv.push(false)
}

Vue.js: Collapse/expand all elements from parent

I need to add "expand/collapse all" functionality for my Vue component(some collapsible panel).
If user clicks collapse button then clicks on some panel and expand it then clicking on collapse button will do nothing because watched parameter will not change.
So how to implement this functionality properly (buttons must collapse and expand components always)?
I prepared simple example(sorry for bad formatting, it looks nice in editor :( ):
var collapsible = {
template: "#collapsible",
props: ["collapseAll"],
data: function () {
return {
collapsed: true
}
},
watch: {
collapseAll: function(value) {
this.collapsed = value
}
}
}
var app = new Vue({
template: "#app",
el: "#foo",
data: {
collapseAll: true
},
components: {
collapsible: collapsible
}
});
.wrapper {
width: 100%;
}
.wrapper + .wrapper {
margin-top: 10px;
}
.header {
height: 20px;
width: 100%;
background: #ccc;
}
.collapsible {
height: 100px;
width: 100%;
background: #aaa;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="foo"></div>
<script type="text/x-template" id="collapsible">
<div class="wrapper">
<div class="header" v-on:click="collapsed = !collapsed"></div>
<div class="collapsible" v-show="!collapsed"></div>
</div>
</script>
<script type="text/x-template" id="app">
<div>
<button v-on:click="collapseAll = true">Collapse All</button>
<button v-on:click="collapseAll = false">Expand All</button>
<collapsible v-for="a in 10" v-bind:collapseAll="collapseAll" v-bind:key="a"></collapsible>
</div>
</script>
Thanks!
This is a case where I might use a ref.
<button v-on:click="collapseAll">Collapse All</button>
<button v-on:click="expandAll">Expand All</button>
<collapsible ref="collapsible" v-for="a in 10" v-bind:key="a"></collapsible>
And add methods to your Vue.
var app = new Vue({
template: "#app",
el: "#foo",
methods:{
collapseAll(){
this.$refs.collapsible.map(c => c.collapsed = true)
},
expandAll(){
this.$refs.collapsible.map(c => c.collapsed = false)
}
},
components: {
collapsible: collapsible
}
});
Example.

Categories