Vue | Call method function inside of data - vue.js

Stack.
The problem | Codepen
I have this code in Vue:
<input v-model="name" type="text"/>
<span>{{ slug }}</span>
And this data:
data: {
name: ''
}
This is methods:
computed: {
slug: function() {
return this.slugify(this.name);
}
slugify: function(name) {
var slug = name.toLowerCase();
slug = slug.replace(/\s*$/g, '');
slug = slug.replace(/\s+/g, '-');
return slug;
}
How can I use {{ slug }} in input value?
v-bind:value is not working together with v-model as Vue Docs said.
The question
I need to generate slug inside another input?
It will be look like:
<input v-model="name" type="text"/>
<input v-model="slug" type="text"/> <--- Here goes generated input with value = slug

My solution for this would be like this:
new Vue({
el: '#app',
data: {
title: '',
slug: ''
},
methods: {
slugify: function () {
let slugInput = this.title.toLowerCase()
slugInput = slugInput.replace(/\s*$/g, '');
slugInput = slugInput.replace(/\s+/g, '-');
this.slug = slugInput
}
}
});
HTML:
<div id="app">
<input
v-model="title"
type="text"
#input="slugify"/>
<span>{{ title }}</span>
<br>
<input
v-model="slug"
type="text"/>
<span>{{ slug }}</span>
</div>

One way to do this would be to stick with v-model, then modify name in situ with #keypress and #blur, like this...
code
new Vue({
el: '#app',
data: {
title: '',
name:''
},
computed: {
slug: function() {
return this.slugify(this.name);
}
},
methods: {
onkeypress(){
console.log("change");
var slug = this.name.toLowerCase();
slug = slug.replace(/\s+/g, '-');
this.name = slug
},
onblur(){
this.name = slugify(this.name)
},
slugify: function(name) {
var slug = name.toLowerCase();
slug = slug.replace(/\s*$/g, '');
slug = slug.replace(/\s+/g, '-');
return slug;
}
}
});
markup
<div id="app">
<input v-model="name"#keypress="onkeypress" #blur="onblur" type="text"/>
<span>{{ slug }}</span>
</div>

Related

Search Turkish Character Problem on VueJs Bootstrap Vue Table

I have a problem. I used bootstrap vue table. And I have a search box. I have a yield as "Istanbul". It doesn't see it when I press i in lower case. It accepts a capital letter I. I tried toLocaleLowerCase() but didn't run.
I type "istanbul" in the search box, but it does not find it in the table. It finds it when you write it as "İstanbul".
This is my template and dataset:
<template>
<div>
<b-table striped hover :fields="fields" :items="cities"></b-table>
</div>
</template>
<script>
export default {
data() {
return {
cities : [
{key:1,city:'İstanbul'},
{key:2,city:'İzmir'},
{key:3,city:'Adana'},
],
cityCopyArray : [
{key:1,city:'İstanbul'},
{key:2,city:'İzmir'},
{key:3,city:'Adana'},
],
fields:["city"]
}
}
</script>
This is my input:
<input
:placeholder="'City Name"
:id="'cityNamr'"
v-model="citySearchSearch"></input>
This is my watch:
citySearchSearch: {
handler(val) {
this.cities = this.cityCopyArray.filter((city) => {
return this.converter(city.name).includes(this.converter(val))
})t
},
},
And I used this code as converter :
converter(text){
var trMap = {
'çÇ':'c',
'ğĞ':'g',
'şŞ':'s',
'üÜ':'u',
'ıİ':'i',
'öÖ':'o',
};
for(var key in trMap) {
text = text.replace(new RegExp('['+key+']','g'), trMap[key]);
}
return text.replace(/[^-a-zA-Z0-9\s]+/ig, '')
.replace(/\s/gi, "-")
.replace(/[-]+/gi, "-")
.toLowerCase();
},
You can compare Turkish characters using toLocaleUpperCase('tr-TR') like:
const firstWord = 'istanbul';
const secondWord = 'İstanbul';
// If firstWord contains secondWord, firstWordContainsSecondWord will be true otherwise false.
const firstWordContainsSecondWord = firstWord.toLocaleUpperCase('tr-TR').indexOf(secondWord.toLocaleUpperCase('tr-TR')) !== -1;
Simple example:
new Vue({
el: '#app',
data: {
firstWord: 'istanbul',
secondWord: 'İstanbul',
result: null,
},
watch: {
firstWord() {
this.contains();
},
secondWord() {
this.contains();
}
},
mounted() {
this.contains();
},
methods: {
contains() {
// If firstWord contains secondWord, result will be true otherwise false.
this.result = this.firstWord.toLocaleUpperCase('tr-TR').indexOf(this.secondWord.toLocaleUpperCase('tr-TR')) !== -1;
}
}
});
<script src="https://cdn.jsdelivr.net/vue/latest/vue.js"></script>
<div id="app">
<input placeholder="firstWord" v-model="firstWord">
<input placeholder="secondWord" v-model="secondWord">
<br/><br/>
<div>
Result =>
<br/> {{ firstWord }} contains {{ secondWord }} : {{ result }}
</div>
</div>

How do i get the v-model values of a v-for components if i iterate with only numbers?

I have v-for form-group components i iterated over a select's value(integer). I want to get the values of the iterated v-models, but i just can't seem to get it right
TEMPLATE
<b-form-group
id="input-group-1"
label="Jumlah Lowongan:"
label-for="selectJumlahLow"
description="Silahkan pilih satu."
v-if="show"
>
<b-form-select id="selectJumlahLow" v-model="form.jumlahlow" :options="selow" required></b-form-select>
</b-form-group>
<b-form-group label="Nama Lowongan:" v-for="n in parseInt(form.jumlahlow)" :key="n">
<b-form-input required placeholder="Masukkan nama lowongan" v-model="low"></b-form-input>
</b-form-group>
SCRIPT DATA
data() {
return {
form: {
jumlahlow: 1,
checked: [],
low: []
}
}
I've tried changing the model to low[n] or declaring low in data as object {} but either of these seems to be undefined according to TypeErrors i've encoutered.
How am i suppose to get the low[n]'s values?
EDIT:
Here is the full code:
<template>
<div>
<b-form #submit="onSubmit" #reset="onReset">
<b-form-group
id="input-group-1"
label="Jumlah Lowongan:"
label-for="selectJumlahLow"
description="Silahkan pilih satu."
v-if="show"
>
<b-form-select id="selectJumlahLow" v-model="form.jumlahlow" :options="selow" required></b-form-select>
</b-form-group>
<b-form-group label="Nama Lowongan:" v-for="n in parseInt(form.jumlahlow)" :key="n">
<b-form-input required placeholder="Masukkan nama lowongan" v-model="low"></b-form-input>
</b-form-group>
<b-button type="submit" variant="primary">
{{ buttonText }}
<i class="material-icons">arrow_forward_ios</i>
</b-button>
<b-button type="reset" variant="danger">Reset</b-button>
</b-form>
<b-card class="mt-3" header="Form Data Result">
<pre class="m-0">{{ form }}</pre>
</b-card>
</div>
</template>
<script>
export default {
name: "lowonganForm",
data() {
return {
form: {
jumlahlow: 1,
checked: [],
low: []
},
selow: [
{ text: "Pilih Satu", value: null, disabled: true },
1,
2,
3,
4,
5,
6
],
show: true,
target: false,
buttonText: "Next"
};
},
methods: {
onSubmit(evt) {
evt.preventDefault();
alert(JSON.stringify(this.form));
// if (this.jumlahlow !== null || !this.jumlahlow < 1) {
// this.show = false;
// }
},
onReset(evt) {
evt.preventDefault();
// Reset our form values
this.form.jumlahlow = null;
this.form.checked = [];
// Trick to reset/clear native browser form validation state
this.show = false;
this.$nextTick(() => {
this.show = true;
});
}
},
computed: {}
};
</script>
You should try to model your data for how you want the view to be rendered. If you want to have a list of input boxes, then the data for those inputs should be defined in an array that is prepopulated with those items, or when you need to adjust the number of items you should add those data items to the array. You'll avoid reactivity problems this way too.
Here's an example of what I mean:
new Vue({
el: '#app',
data: {
maxCount: 5,
count: 3,
items: [],
data: '',
},
computed: {
visibleItems() {
return this.items.slice(0, this.count)
}
},
created() {
// Define the data upfront so it will be reactive
for (let i = 0; i < this.maxCount; i++) {
this.items.push({
firstName: '',
lastName: '',
})
}
},
methods: {
submit() {
// Transform the data into some JSON that is
// compatible with your API
const data = this.visibleItems.map(item => ({
first_name: item.firstName,
last_name: item.lastName,
role: 'user',
}))
this.data = JSON.stringify(data, null, ' ')
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>
Number of people:
<select v-model="count">
<option v-for="i of maxCount" :value="i">{{ i }}</option>
</select>
</div>
<div v-for="item of visibleItems">
<input placeholder="First name" v-model="item.firstName">
<input placeholder="Last name" v-model="item.lastName">
</div>
<button #click="submit">Submit</button>
<pre>{{ data }}</pre>
</div>
Try this example.
<div id="app">
<div>
<select v-model="jumlahlow">
<option v-for="i in selects" :key="i">{{ i }}</option>
</select>
</div>
<div v-for="num, index in parseInt(jumlahlow)">
<input v-model="lows[index].value" />
</div>
</div>
And JS
new Vue({
el: '#app',
data: {
lows: [
{
value: ''
}
],
jumlahlow: 1,
selects: [
1,
2,
3,
4,
5,
6
]
},
watch: {
jumlahlow: function (val) {
this.lowsTmp = this.lows;
this.lows = [];
for (let i = 0; i < val; i++) {
const currentVal = typeof this.lowsTmp[i] !== 'undefined' ? this.lowsTmp[i].value : '';
this.addLow(currentVal);
}
}
},
methods: {
addLow: function(val) {
this.lows.push({ value: val });
}
}
})
Directly check here: https://jsfiddle.net/abinhho/m3c8r4tj/2/
you are iterating v-for="n in parseInt(form.jumlahlow)" but that's an Object and v-for works on array not on objects.
Here you can use array of objects to iterate, for example:-
form: [{
jumlahlow: 1,
checked: [],
low: []
}]
and after that you will have to write v-for="n in form" then try accessing low by form.low

VueJs Watch per key of a model

I have an object (model), as an input field,for a search method, and I want that the watcher to detect changes per key, and not for all.
Right now ,if an input is changed, the watcher is called 10 times (the number of inputs that I have).
<b-form-input
v-model="search[field.column]"
type="search"
id="filterInput"
placeholder="search.."
></b-form-input>
watch:{
search: {
handler(){
// do somenthing
},
deep: true
}
}
You can watch a computed value that return the specific field that you want to watch.
Vue.config.devtools = false;
Vue.config.productionTip = false;
var app = new Vue({
el: '#app',
data: {
form: {
name : '',
lastname: ''
}
},
computed: {
formName() {
return this.form.name;
}
},
watch: {
formName() {
console.log("Name has changed")
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
Name :
<input v-model="form.name" />
Lastname :
<input v-model="form.lastname" />
</div>
You can explicitly watch the required key only.
new Vue({
el: '#app',
data: {
search: {
name: { placeholder: 'search name', value: '' },
age: { placeholder: 'search age', value: '' },
country: { placeholder: 'search country', value: '' }
}
},
watch:{
'search.name.value': { // Explicitly watch the required key.
handler(){
console.log('name changed...')
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<form>
<input
v-for="(value, key, i) in search"
:key="i"
v-model="search[key].value"
type="search"
id="filterInput"
:placeholder="value.placeholder"
/>
</form>
</div>
So, here watch's handler is called only when the name is searched & does not get called when age or country is searched.

VueJS set first radiobutton as checked if v-model is empty

How can i have checked radio button in v-for, if my v-model is empty?
The list of users may be different, so I can't set selectedUserId: 665. I tried to use :checked="index == 0" but it does not work with v-model.
var radioVue = new Vue({
el: "#test",
data: {
selectedUserId: null,
users: [{id: 665, name: 'John'}, {id: 1135, name: 'Edward'}, {id: 7546, name: 'David'}]
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="test">
<div v-for="(user, index) in users">
<input type="radio" :value="user.id" v-model="selectedUserId"> {{ user.name }}
</div>
<span>{{ selectedUserId}}</span>
</div>
You can use watch for that:
watch: {
selectedUserId: {
handler(newVal) {
if (newVal === null || newVal === undefined) {
this.selectedUserId = this.users[0].id
}
},
immediate: true
}
}
for this you can use get/set on computed variable with your v-model
the setter does a standard set while the getter return the wanted users id or if there is not the id of the first user (as in first entry of the array)
var radioVue = new Vue({
el: "#test",
data: {
selectedUserId: 7546,
users: [{id: 665, name: 'John'}, {id: 1135, name: 'Edward'}, {id: 7546, name: 'David'}]
},
computed: {
checkedUserId: {
get() {
// need to use == instead of === because v-model seems to set as string :/
return this.users.some(user => user.id == this.selectedUserId) ? this.users.filter(user => user.id == this.selectedUserId)[0].id : this.users[0].id
},
set(val) {
// doing a parseInt() here would allow you to use === in get
this.selectedUserId = val
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="test">
<div v-for="user in users">
<input type="radio" :value="user.id" name="name" v-model="checkedUserId"> {{ user.name }} - {{ user.id }}
</div>
<span>{{ selectedUserId}}</span>
<input type="text" v-model="selectedUserId">
</div>

Vue2 Search in List received via Axios

since filtering is way more complex then in Vue 1, I have a question.
This is my Component, where a list of Sheeps is shown with the option to search/filter on Name or Family.
But I can't figure out how to achieve this.
<input type="search" v-model="search" placeholder="Search for Name OR Family" />
<ul>
<li v-for="sheep in sheeps"> <!-- Tried also: 'sheep in filteredSheeps' -->
{{ sheep.name }} ({{ sheep.type }}/{{ sheep.family }} )
</li>
</ul>
<script>
import axios from 'axios';
export default {
data() {
return {
sheeps: [],
search: '',
};
},
mounted() {
this.getSheeps();
}
methods: {
getSheeps() {
var self = this;
const url = '/api/sheeps';
axios.get(url).then(function(response) {
self.sheeps = response.data;
});
},
},
computed: {
filteredSheeps: function() {
var self = this;
return this.sheeps.filter(function(item) {
return item.family.toLowerCase().indexOf(self.search.toLowerCase()) > -1
})
}
}
}
}
</script>
I thought that it needed the computed method filteredSheeps in the v-for, but that's not it either. Getting an error directly then:
TypeError: null is not an object (evaluating 'item.family.toLowerCase')"
Here is a computed that will protect you from cases where the family and/or name is not populated.
filteredSheeps: function() {
let searchTerm = (this.search || "").toLowerCase()
return this.sheeps.filter(function(item) {
let family = (item.family || "").toLowerCase()
let name = (item.name || "").toLowerCase()
return family.indexOf(searchTerm) > -1 || name.indexOf(searchTerm) > -1
})
}
And a working example.
console.clear()
const sheeps = [
{name: "Lily", type: "Dorper", family: "Family 1"},
{name: "Joe", type: "Merino", family: "Family 2"},
{name: "Bob", type: "Dorper", family: null},
]
new Vue({
el: "#app",
data:{
sheeps:[],
search: null
},
computed: {
filteredSheeps: function() {
let searchTerm = (this.search || "").toLowerCase()
return this.sheeps.filter(function(item) {
let family = (item.family || "").toLowerCase()
let name = (item.name || "").toLowerCase()
return family.indexOf(searchTerm) > -1 || name.indexOf(searchTerm) > -1
})
}
},
created(){
setTimeout(() => this.sheeps = sheeps, 500)
}
})
<script src="https://unpkg.com/vue#2.4.2"></script>
<div id="app">
<input type="search" v-model="search" placeholder="Search for Name OR Family" />
<ul>
<li v-for="sheep in filteredSheeps">
{{ sheep.name }} ({{ sheep.type }}/{{ sheep.family }} )
</li>
</ul>
</div>