Can an Array be passed in an element-ui el-autocomplete callback? - javascript

I tried to use the el-autocomplete tag with the simplest version: an Array returned by the callback function (JSFiddle version).
Vue.component('button-counter', {
data: function() {
return {
selectdusers: [],
user: ''
}
},
template: '<el-container> <el-container style="margin-left: 15px; width: 150px"> <el-autocomplete class="inline-input" v-model="user" :fetch-suggestions="filterUsers" placeholder="add user" #select="handleSelect" ></el-autocomplete> <el-button type="info" icon="el-icon-plus" click="addUser(user)">Add</el-button> </el-container> </el-container>',
methods: {
addUser(user) {
this.selectedUsers.push(user)
this.user = ''
},
filterUsers(user, cb) {
console.log(user)
cb(['qqq', 'zzz'])
//return this.allusers.filter(x => _.includes(x.f+x.t, user))
},
handleSelect() {}
},
})
new Vue({
el: '#components-demo'
})
#import url("//unpkg.com/element-ui#2.4.8/lib/theme-chalk/index.css");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.4.7/index.js"></script>
<div id="components-demo">
<button-counter></button-counter>
</div>
The suggestion box is visually empty, depite DevTools showing the expected elements in <ElAutocompleteSuggestions> → data → suggestions

For using Array instead of Object, you can use slot in autocomplete block
<el-autocomplete ...>
<template slot-scope="{ item }">
<div class="item">{{ item }}</div>
</template>
</el-autocomplete>

What needs to be passed via the callback is an Object which must have a property called value. This property holds what is displayed as the suggestion.
For the example above:
filterUsers(user, cb) {
console.log(user)
cb([{'value': 'qqq', 'whatever': 'djhjfh'}, {'value': 'zzz', 'whatever': 'uiuiiu'}])
},

Related

How to dynamically add part of an object with an arg in a vue method

Super simple question but i've never been able to solve it.
Say we have some data:
section: {
option1: true,
option2: true
}
and on a button we have:
<button #click="toggle(option1)">
How do I dynamically paste 'option1' arg into something like this:
toggle(opp){
console.log(this.section.opp)
}
Because currently it's literally looking for this.section.opp, and opp doesn't exist in the data.
Use this.section[opp] instead of this.section.opp as opp contains dynamic value and can not access directly with dot(.) notation as it is containing a different value.
Working Demo :
new Vue({
el: '#app',
data: {
section: {
option1: true,
option2: true
},
result: null
},
methods: {
toggle(opp) {
this.result = this.section[opp];
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="toggle('option1')">Click Me</button>
<p>Result: {{ result }}</p>
</div>

How to Store Data Property Value from a Specific Item in Rendered List in Vue

I'm trying create a follow button on list items in Vue. My strategy is to grab the value of a particular list item property and store it in the data object. Then use this value in a method to add it to an array in my database.
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock">+follow</button>
</div>
I'm not sure how to get the value of result.symbol "into" the button element to set the value symbol in the data object below.
<script>
export default {
data() {
return {
results: [ // this is populated by an api call
{
currency: "USD"
exchangeShortName: "NYSE"
name: "International Game Technology PLC"
stockExchange: "NYSE"
symbol: "IGT"
},
{...},
...
],
symbol: "",
};
},
followStock() {
// add this.symbol to database array
},
},
};
</script>
I'm guessing there might be an easier strategy I'm overlooking as I'm still new to Vue, so any other solution that essentially allows me to fire off the value of result.symbol from any rendered result to my database would be awesome.
You can just pass the result as a parameter to your method.
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock(result)">+follow</button>
</div>
And in your method:
methods: {
followStock(result) {
// do something with result
console.log({result});
let symbol = result.symbol;
},
}
P.S I didn't see you put your followStock() inside a methods object, but I did so in the example. https://v2.vuejs.org/v2/api/#methods
Write directly as a function call.
The vue compiler will turn followStock(result.symbol) into function(event) {followStock(result.symbol)}.
new Vue({
el: '#app',
data() {
return {
results: [
{
name: "International Game Technology PLC",
symbol: "IGT"
},
{
name: "A name",
symbol: "A symbol"
}
]
};
},
methods: {
followStock(symbol) {
console.log(symbol)
},
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock(result.symbol)">+follow</button>
</div>
</div>
As Nazaire mentioned you can access the results anywhere inside the child elements when using v-for.
(it works like a normal for-loop)
It's not only limited to the corresponding element (the element in which you do v-for)
<div v-for="result in results" :key="result.symbol">
{{ result.name }}
<button #click="followStock(result.symbol)">+follow</button>
</div>
followStock(symbol){
// you can now add symbol to db
}

Deleting specific component in v-for array

I have below array, that contains a number of columns. Below example contains three columns, but columns can be added/removed dynamically:
[['position', '30'], ['position', '60'], ['position', '90']]
I am facing issues when deleting the correct column (index in array) with Vue.
Consider below snippet:
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
columns: [['position', '30'], ['position', '60'], ['position', '90']]
},
methods: {
deleteColumn: function(index) {
this.columns.splice(index, 1);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(item, index) in columns" :key="index">
Column #: {{index}} - <a #click="deleteColumn(index)">Delete me</a>
</div>
</div>
If you run the above code snippet end try to delete the #1 column, it will actually remove the #2 column (last item of the array). Same goes for #0.
I thought that by providing the index to my deleteColumn function, I could remove the "right" index from the array.
Any help is appreciated.
Just give them a property name and you are done. Notice what I changed here. Columns is no more a 2D array, but objects. Use this.$delete(this.columns, index); to delete the objects.
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
columns: {
'1': {
position: 30
},
'2': {
position: 60
},
'3': {
position: 90
}
}
},
methods: {
deleteColumn: function(index) {
this.$delete(this.columns, index);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(item, index) in columns" :key="index">
Column #: {{index}} - <a #click="deleteColumn(index)">Delete me</a>
</div>
</div>
{
'1': {
position: 30
},
'2': {
position: 60
},
'3': {
position: 90
}
}
Here, '1' is a property name and it's value is another object. It's like giving ids to your data.
The format for value of object is this
{ property_name : value }
Here, value is another object, and in that object, there is another property, named "position" with your corresponding values.
When you clicked any item you are removing it in the right way, your index is your key, that's the problem, but is visually, in the logic it's right. Display your position in your template just for you can see it. ANd for me your data it's not in the right way.
<div id="app">
<div v-for="(item, index) in columns" :key="index">
Column #: {{index}}-{{item.position}} -
<a #click="deleteColumn(index)">Delete me</a>
</div>
</div>
and your script for you can see the change
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
columns: [{position: 30}, {position: 60}, {position: 90}]
},
methods: {
deleteColumn: function(index) {
this.columns.splice(index, 1);
}
}
})
The splice method reindexes the array, moving all elements after the splice point up or down so that any new inserted values will fit and so that the array indices remain contiguous. You can see it more clearly if you also display the values of the items in your list:
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
columns: ['foo', 'bar', 'baz']
},
methods: {
deleteColumn: function(index) {
this.columns.splice(index, 1);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(item, index) in columns" :key="index">
Column #{{index}} = {{item}} - <a #click="deleteColumn(index)" style="cursor:pointer">Delete me</a>
</div>
</div>
Initially, the snippet above will render like this:
Column #0 = foo - Delete me
Column #1 = bar - Delete me
Column #2 = baz - Delete me
If you now click the "Delete me" link on column #0 ("foo"), it will change to:
Column #0 = bar - Delete me
Column #1 = baz - Delete me
You can see that the value "foo" indeed got spliced out of the array — and the values "bar" and "baz" were shifted down by one position to become the new elements #0 and #1.
Anyway, the fix for this problem is simply "don't do that":
If you're using v-for with a simple array whose elements have no natural key value, you can just omit :key entirely and let Vue decide how to best handle changes to the underlying array. As long as the contents within the v-for loop doesn't contain any form inputs or stateful components or other fancy stuff that doesn't react well to the array being reindexed, it should work just fine.
Conversely, if you do have a natural unique key available for each array element, use it. If you don't, but can create one, consider doing that.
You should not use index as the key with CRUD operations since this will confuse Vue when it comes to deleting. The key should be a unique identifier that relates to the data.
You can create a new formatted array of objects on mount with a key generated from the data within the array (note: I haven't tested the code in a browser if there are any mistakes).
<template>
<div>
<div v-for="col in formattedColumns" :key="col.key">
{{ col.value }}
</div>
</div>
</template>
<script>
export default {
data() {
return {
columns: [['position', '30'], ['position', '60'], ['position', '90']],
formattedColumns: null,
};
},
mounted() {
let columns = [];
for (let i = 0; i < this.columns.length; i++) {
columns.push({
value: this.columns[i],
key: this.columns[i][0] + this.columns[i][1],
});
}
this.formattedColumns = columns;
},
};
</script>
Try this this.$delete(this.columns, index) which is the same as Vue.delete(this.columns, index)
https://v2.vuejs.org/v2/api/index.html#Vue-delete

How to link words in an HTML string and assign them #click method in VueJS 2?

I have a array of strings in which I want to linkify certain words like "User object", "Promise", etc like this:
var strings = ['This returns a promise containing a User Object that has the id', 'next string']
This needs to be rendered like this
<div class="wrapper">
<div class="item" v-for="str in strings" v-html="str"></div>
</div>
The problem is I want to replace words like "User object", "Promise" and bind them to a #click event that my app can handle.
So if it were rendered like I want it to be, it would be something like this (the same v-for loop above rendered manually)
<div class="wrapper">
<div class="item">This returns a promise containing a User object that has the id</div>
<div class="item">next string</div>
</div>
I tried doing this but it doesn't bind the #click event
methods: {
linkify(str) {
return str.replace(/user object/, 'User object');
}
}
Any ideas?
Here's an example of a component that takes in a string for the full message and a string for the text to replace with a link and renders a span with that message with the link text wrapped in a <a> tag:
Vue.component('linkify', {
template: '#linkify-template',
props: {
value: { type: String },
linkText: { type: String }
},
computed: {
before() {
return this.value.split(this.linkText)[0];
},
after() {
return this.value.split(this.linkText)[1];
}
}
});
new Vue({
el: '#app',
data() {
return {
message: 'This returns a promise containing a User Object that has the id',
}
},
methods: {
foo() {
console.log('clicked')
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<script type="text/x-template" id="linkify-template">
<span>
{{ before }}
<a href="#" #click.prevent="$emit('click')">
<code>{{ linkText }}</code>
</a>
{{ after }}
</span>
</script>
<div id="app">
<linkify link-text="User Object" :value="message" #click="foo"></linkify>
</div>
Okay figured it out. If somebody has a better way to do it please answer too!
Vue.component('linkify', {
props: ['value', 'words'],
template: `<span :is="html"></span>`,
data() {
return {
html: Vue.compile('<span>' + this.value.replace(new RegExp('(' + this.words.join('|') + ')', 'g'), `<code>$1</code>`) + '</span>'),
}
}
});
Now all I need to do in the main app is this:
<div class="wrapper">
<div class="item" v-for="str in strings">
<linkify :value="str" :words="['user object', 'promise']" #click="help"></linkify>
</div>
</div>
Unfortunately this only works with full version of Vue (which has the compile function)

Trying to bind the query to the json string for every request in to the api in Vue.js

I'm trying to get results from an api based on the user search box. When the user enters a value 'en' or 'de'. They should get the result from that search. I need to bind the user input into my query string. This works when I manually code the country into the template, but not when I bind the value into the string after the user inputs a value for the second time. The 'get' request that uses the user input value 'query' works fine. But not when I bind this a second time
I want to be fit to access
results[i].query.name
But '.query' is not working when I query the data unless I enter the value manually '.en'
I have a json file that looks like the following
[
{
"en": {
"name": "testone",
"id": 5363289,
"location": "messages_en.properties1"
},
"de": {
"name": "testonede",
"id": 5363289,
"location": "messages_en.properties2"
}
},
{
"en": {
"name": "test2",
"id": 5363289,
"location": "messages_en.properties3"
},
"de": {
"name": "test2de",
"id": 5363289,
"location": "messages_en.properties4"
}
}
]
Below is my index.html vue.js template
<div id=#app>
<input type="text" v-model="query" placeholder="Choose Language" />
<div class="medium-6 columns">
<a #click="getResult(query)" class="button expanded">Retrieve</a>
</div>
<template v-for="(result, i) in results">
<div class="card" style="width: 20rem; display:inline-block;">
<div class="card-block"></div>
<p> {{results[i].query}} </p>
<!-- works when I manually code in the 'en' query but when ran with 'query' it returns an error 'Cannot read property 'name' of undefined"' second time it returns that the value is -->
<!-- <p> {{results[i].en.name}} </p> -->
<!-- <p> {{results[i].query.name}} </p> -->
</div>
</template>
</div>
Vue.js
el: '#app',
data () {
return {
search: '',
query: 'en',
results: '',
title: '',
items: '',
section: ''
}
},
methods: {
getResult(query) {
axios.get('http://localhost:3000/api/country?country=' + query + '&blank=true').then(response => {
this.results = response.data;
console.log(this.results);
});
},
You need to use bracket notation to access a property using a param, so:
results[i][query].name
The second issue is that results[i][query] will be undefined until the async call has completed, so you will need to check that the property is not undefined or use a boolean flag. So, to check that it is not undefined you could do something like:
<p v-if="!!results[i][query]">{{results[i][query].name}}</p>
<p v-else>Loading...</p>
Here's a simplified JSFiddle for that: https://jsfiddle.net/4w3dxm22/
Or you could just use a dataLoaded flag:
new Vue({
el: '#app',
methods:{
getResult(query) {
this.dataLoaded = false; // set dataLoaded to false
axios.get('http://localhost:3000/api/country?country=' + query + '&blank=true').then(response => {
this.results = response.data;
this.dataLoaded = true; // Data has loaded so set dataLoaded to true
});
},
data: {
dataLoaded: false
}
})
Then you can do:
<span v-if="dataLoaded">{{results[i][query].name}}</span>
<span v-else>Loading Data...</span>
Here's the simplified JSFiddle for that: https://jsfiddle.net/99ydx82u/

Categories

Resources