searching for local storage items - javascript

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>

Related

In Vue.js, How this fix, after splicing an array, following its class

First of all, I'm sorry to write in English not well.
I'm looking foward to find the answer to fix this problems.
I'm making a todolist, it had a problem that the class ('centerLine') keeps following next element
after deleting an array to use splice.
Please someone know, let me know how to fix it.
Thank you
https://github.com/seongjin2427/Public
* checked the check box
*after pushing x-box to get rid of checked todo
You can send id to method
#click="deleteTask(todo.id)"
and then filter array
deleteTask(id) {
this.todos = this.todos.filter(t => t.id !== id)
}
let app = new Vue({
el: '#app',
data: {
todos: [{
id: 1,
text: '밥 먹기',
checked: false
},
{
id: 2,
text: '잘 자기',
checked: false
},
{
id: 3,
text: '유튜브 보기',
checked: false
}
],
input_text: ""
},
methods: {
addTodo() {
// 배열 길이 변수 저장
let arrayLength = this.todos[this.todos.length-1].id;
// Add 버튼 눌렀을 때, input_text값 그대로 배열에 push 하기
if (this.input_text != "") {
this.todos.push({
id: arrayLength + 1,
text: this.input_text
});
}
// push후 input 값 초기화
this.input_text = "";
},
change1(e) {
// 할 일 클릭 후 input 창으로 변경
let index = e.target.id.substr(3, 3);
document.querySelector('#vsb' + index).classList.toggle('none');
document.querySelector('#invsb' + index).classList.toggle('none');
},
change2(e) {
// input 창에서 마우스가 out되면 실행할 것
let index = e.target.id.substr(5, 3);
document.querySelector('#vsb' + index).classList.toggle('none');
document.querySelector('#invsb' + index).classList.toggle('none');
},
deleteTask(id) {
this.todos = this.todos.filter(t => t.id !== id)
}
}
});
#app li {
list-style: none;
padding:0;
margin: 0;
}
span.centerLine {
text-decoration: line-through;
color: gray;
}
.x-box {
border-radius: 30%;
opacity: 0%;
}
.x-box:hover {
opacity: 100%;
transition: all 1s;
}
.none {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<body>
<div id="app">
<h1>To Do List</h1>
<hr>
<input type="text" v-model="input_text" #keyup.enter="addTodo">
<input type="button" value="Add" #click="addTodo">
<br>
<div class="todo-box">
<ul>
<li v-for="(todo, idx) in todos" :key="idx">
<input :id="'chk'+(idx+1)" type="checkbox" v-model="todo.checked">
<span :id="'vsb'+(idx+1)" #click="change1" :class="{'centerLine': todo.checked}">{{ todo.text }}</span>
<input :id="'invsb'+(idx+1)" #mouseout="change2" class="none" type="text" v-model="todo.text">
<input :id="'xbox'+(idx+1)" class="x-box" #click="deleteTask(todo.id)" type="button" value="x">
</li>
</ul>
</div>
</div>
</body>
you can send the task in the method
#click="deleteTask(task)"
then splice it from array
deleteTask(task) {
this.todos.splice(this.todos.indexOf(task),1)
}

Set up a v-on:click directive inside v-for

I have displayed a list of images with some information. I want those images to be clickable. And when clicked it should show a div with saying "HI!!". I have been trying to add a variable as show:true in Vue data and tried to build some logic that show becomes false when clicked. But I have not been able to achieve it.
Below is the sample code:
template>
<div>
<h1>SpaceX</h1>
<div v-for="launch in launches" :key="launch.id" class="list" #click="iclickthis(launch)">
<div ><img :src="launch.links.patch.small" alt="No Image" title={{launch.name}} /></div>
<div>ROCKET NAME: {{launch.name}} </div>
<div>DATE: {{ launch.date_utc}} </div>
<div>SUCCESS: {{ launch.success}} </div>
<div>COMPLETENESS: {{ launch.landing_success}} </div>
</div>
<!-- <v-model :start="openModel" #close="closeModel" /> -->
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'SpaceXTimeline',
components: {
},
data: () => ({
launches : [],
openModel : false,
show : true,
}),
methods: {
iclickthis(launch) {
// this should load a model search v-model / bootstrap on vue npm install v-model
console.log(launch.name + " is launched");
console.log("DETAILS: "+ launch.details);
console.log("ROCKET INFO: "+ launch.links.wikipedia);
console.log("CREW DETAILS: "+ launch.crew);
console.log("Launchpad Name: "+ launch.launchpad);
this.openModel = true;
},
closeModel () {
this.openModel = false;
}
},
async created() {
const {data} = await axios.get('https://api.spacexdata.com/v4/launches');
data.forEach(launch => {
this.launches.push(launch);
});
}
};
</script>
<style scoped>
.list {
border: 1px solid black;
}
</style>
Thanks, and appreciate a lot.
v-model is a binding and not an element, unless you've named a component that? Is it a misspelling of "modal"?
Either way, sounds like you want a v-if:
<v-model v-if="openModel" #close="closeModel" />
Example:
new Vue({
el: '#app',
components: {},
data: () => ({
launches: [],
openModel: false,
show: true,
}),
methods: {
iclickthis(launch) {
// this should load a model search v-model / bootstrap on vue npm install v-model
console.log(launch.name + ' is launched');
console.log('DETAILS: ' + launch.details);
console.log('ROCKET INFO: ' + launch.links.wikipedia);
console.log('CREW DETAILS: ' + launch.crew);
console.log('Launchpad Name: ' + launch.launchpad);
this.openModel = true;
},
closeModel() {
this.openModel = false;
},
},
async created() {
const {
data
} = await axios.get('https://api.spacexdata.com/v4/launches');
data.forEach(launch => {
this.launches.push(launch);
});
},
})
Vue.config.productionTip = false;
Vue.config.devtools = false;
.modal {
cursor: pointer;
display: flex;
justify-content: center;
position: fixed;
top: 0;
width: 100%;
height: 100vh;
padding: 20px 0;
background: rgba(255, 255, 255, 0.5);
}
img {
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<div id="app">
<h1>SpaceX</h1>
<div v-for="launch in launches" :key="launch.id" class="list" #click="iclickthis(launch)">
<div>
<img :src="launch.links.patch.small" alt="No Image" :title="launch.name" />
</div>
<div>ROCKET NAME: {{ launch.name }}</div>
<div>DATE: {{ launch.date_utc }}</div>
<div>SUCCESS: {{ launch.success }}</div>
<div>COMPLETENESS: {{ launch.landing_success }}</div>
</div>
<div v-if="openModel" #click="closeModel" class="modal">
MODAL
</div>
</div>

Form is listening to enter key Vue

I have made a form component (CreateDocument) in Nuxt. Inside this component i made also an autocomplete (AutoCompleteFilters).
When I hit enter inside the autocomplete component, also the CreateDocument is listening to the enter key. But I only want that a specific input field is listing to the enter key event.
This is the CreateDocument component:
<template>
<div>
<Notification :message="notification" v-if="notification"/>
<form method="post" #submit.prevent="createDocument">
<div class="create__document-new-document">
<div class="create__document-new-document-title">
<label>Titel</label>
<input
type="text"
class="input"
name="title"
v-model="title"
required
>
</div>
<div class="create__document-new-document-textarea">
<editor
apiKey="nothing"
v-model="text"
initialValue=""
:init="{
height: 750,
width: 1400
}"
>
</editor>
</div>
<div class="create__document-new-document-extra-info">
<div class="create__document-new-document-tags">
<label>Tags</label>
<AutoCompleteFilters/>
</div>
<div class="create__document-new-document-clients">
<label>Klant</label>
<input
type="text"
class="input"
name="client"
v-model="client"
required
>
</div>
</div>
<Button buttonText="save" />
</div>
</form>
</div>
</template>
<script>
import Notification from '~/components/Notification'
import Editor from '#tinymce/tinymce-vue'
import Button from "../Button";
import { mapGetters, mapActions } from 'vuex'
import AutoCompleteFilters from "./filters/AutoCompleteFilters";
export default {
computed: {
...mapGetters({
loggedInUser: 'loggedInUser',
})
},
middleware: 'auth',
components: {
Notification,
Button,
editor: Editor,
AutoCompleteFilters
},
data() {
return {
title: '',
text: '',
tags: '',
client: '',
notification: null,
}
},
methods: {
...mapActions({
create: 'document/create'
}),
createDocument () {
const documentData = {
title: this.title,
text: this.text,
tags: this.tags,
client: this.client,
userId: this.loggedInUser.userId
};
this.create(documentData).then((response) => {
this.notification = response;
this.title = '';
this.text = '';
this.tags = '';
this.client= '';
})
}
}
}
</script>
And this is the AutoCompleteFilters component:
<template>
<div class="autocomplete">
<input
type="text"
id="my-input"
#input="onChange"
v-model="search"
#keydown.down="onArrowDown"
#keydown.up="onArrowUp"
#keydown.enter="onEnter"
/>
<ul
v-show="isOpen"
class="autocomplete-results"
>
<li
v-for="result in results"
:key="results.id"
class="autocomplete-result"
#click="setResult(result.name)"
:class="{ 'is-active': results.indexOf(result) === arrowCounter }"
>
{{ result.name }}
</li>
</ul>
</div>
</template>
<script>
import {mapActions} from 'vuex'
export default {
data() {
return {
isOpen: false,
results: false,
search: '',
arrowCounter: 0,
filter: null,
position: 0
};
},
methods: {
...mapActions({
getFilterByCharacter: 'tags/getTagsFromDb'
}),
onChange(e) {
this.isOpen = true;
this.position = e.target.selectionStart;
},
setResult(result) {
this.search = result;
this.isOpen = false;
},
getResults(){
this.getTagsByValue(this.search).then((response) => {
this.results = response;
});
},
async getTagsByValue(value){
const filters = {autocompleteCharacter : value};
return await this.getFilterByCharacter(filters);
},
onArrowDown() {
if (this.arrowCounter < this.results.length) {
this.arrowCounter = this.arrowCounter + 1;
}
},
onArrowUp() {
if (this.arrowCounter > 0) {
this.arrowCounter = this.arrowCounter - 1;
}
},
onEnter(evt) {
this.search = this.results[this.arrowCounter].name;
this.isOpen = false;
this.arrowCounter = -1;
}
},
watch: {
search: function() {
this.getResults();
}
},
};
</script>
<style>
.autocomplete {
position: relative;
}
.autocomplete-results {
padding: 0;
margin: 0;
border: 1px solid #eeeeee;
height: 120px;
overflow: auto;
width: 100%;
}
.autocomplete-result {
list-style: none;
text-align: left;
padding: 4px 2px;
cursor: pointer;
}
.autocomplete-result.is-active,
.autocomplete-result:hover {
background-color: #4AAE9B;
color: white;
}
</style>
Just as you did in your form to avoid "natural" form submit and replace it with a custom action:
#submit.prevent="createDocument"
... you have to preventDefault the "natural" event that submits the form when you press Enter while focusing the form.
To do so, just add .prevent to your events in the template:
#keydown.down.prevent="onArrowDown"
#keydown.up.prevent="onArrowUp"
#keydown.enter.prevent="onEnter"

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>

VueJS - input file repeater

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>

Categories