I want to change the format of this price IDR 50,000.00 to be like this IDR 50.000 using JS and Vue.
I got this script from this link but I don't understand how it works. I don't know what is this
replace(/(\d)(?=(\d{3})+(?:\.\d+)?$)/g, "$1,")
so I can't change the format.
Vue.js
<template lang="html">
<div>
<input type="text" class="form-control" v-model="displayValue" #blur="isInputActive = false" #focus="isInputActive = true"/>
</div>
</template>
<script>
export default {
props: ["value"],
data: function() {
return {
isInputActive: false
}
},
computed: {
displayValue: {
get: function() {
if (this.isInputActive) {
// Cursor is inside the input field. unformat display value for user
return this.value.toString()
} else {
// User is not modifying now. Format display value for user interface
return "IDR " + this.value.toFixed(2).replace(/(\d)(?=(\d{3})+(?:\.\d+)?$)/g, "$1,")
}
},
set: function(modifiedValue) {
// Recalculate value after ignoring "$" and "," in user input
let newValue = parseFloat(modifiedValue.replace(/[^\d\.]/g, ""))
// Ensure that it is not NaN
if (isNaN(newValue)) {
newValue = 0
}
// Note: we cannot set this.value as it is a "prop". It needs to be passed to parent component
// $emit the event so that parent component gets it
this.$emit('input', newValue)
}
}
}
}
</script>
<style lang="css">
</style>
I changed this line
return "$ " + this.value.toFixed(2).replace(/(\d)(?=(\d{3})+(?:\.\d+)?$)/g, "$1,")
to this
return "IDR " + this.value.toString().replace(/(\d)(?=(\d{3})+(?:\.\d+)?$)/g, "$1\.")
Snippet:
Vue.component('my-currency-input', {
props: ["value"],
template: `
<div>
<input type="text" v-model="displayValue" #blur="isInputActive = false" #focus="isInputActive = true"/>
</div>`,
data: function() {
return {
isInputActive: false
}
},
computed: {
displayValue: {
get: function() {
if (this.isInputActive) {
// Cursor is inside the input field. unformat display value for user
return this.value.toString()
} else {
// User is not modifying now. Format display value for user interface
return "IDR " + this.value.toString().replace(/(\d)(?=(\d{3})+(?:\.\d+)?$)/g, "$1\.")
}
},
set: function(modifiedValue) {
// Recalculate value after ignoring "$" and "," in user input
let newValue = parseFloat(modifiedValue.replace(/[^\d\.]/g, ""))
// Ensure that it is not NaN
if (isNaN(newValue)) {
newValue = 0
}
// Note: we cannot set this.value as it is a "prop". It needs to be passed to parent component
// $emit the event so that parent component gets it
this.$emit('input', newValue)
}
}
}
});
new Vue({
el: '#app',
data: function() {
return {
price: 50000
}
}
});
body {
margin: 20px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
input {
border: 1px solid #888;
font-size: 1.2rem;
padding: 0.5rem;
}
<script src="https://unpkg.com/vue#2.1.5/dist/vue.js"></script>
<div id="app">
Price:
<my-currency-input v-model="price"></my-currency-input>
<p>
Price (in parent component): {{price}}
</p>
</div>
In case you are using jquery I would recommend using the jquery number format.
this allow you to automatic formatting of numbers in input elements as you type
Vue.component('idr', {
template: '<input type="text" class="form-control" v-model="txTotal"/>',
computed: {
txTotal: {
get() {
return this.value;
},
set(val) {
var rp = val.replace(/[^0-9]/g, '');
this.$emit('input', rp)
}
}
},
mounted() {
$(this.$el).number(true, 0, ',', '.')
}
})
new Vue({
el: '#app',
data: {
total: '',
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/df-number-format/2.1.6/jquery.number.min.js"></script>
<div id="app">
<idr #input="total = $event"></idr>
{{ total }}
</div>
UPDATE WITHOUT JQUERY
Vue.component('idr', {
template: '<input type="text" v-model="currentValue" #input="handleInput" />',
props: {
value: {
type: [String, Number],
default: ""
},
},
data: () => ({
currentValue: ''
}),
watch: {
value: {
handler(after) {
this.currentValue = this.format(after)
},
immediate: true
}
},
methods: {
format: value => (value + '').replace(/\D/g, "").replace(/\B(?=(\d{3})+(?!\d))/g, "."),
handleInput() {
this.currentValue = this.format(this.currentValue)
this.$emit('input', (this.currentValue + '').replace(/[^0-9]/g, ""))
}
}
})
new Vue({
el: '#app',
data: {
total: 5000,
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<idr v-model="total"></idr>
{{ total }}
</div>
Related
I am trying to change the data depending on the value of my props in a Nuxtjs app.
export default {
props: {
moved: {
default: false,
type: Boolean,
}
},
data: function () {
if (!this.moved){
dataName: {
fill: "value1"
}
else dataName: {
fill: "value2"
}
return dataName
}
}
}
But I have an error message telling me that dataName is not defined. I don't know what I am doing wrong here...
You can make computed property instead:
Vue.component('Child', {
template: `
<div>
{{ dataName }}
<svg height="210" width="500">
<polygon points="100,10 40,198 190,78 10,78 160,198" :fill="dataName.fill"/>
</svg>
</div>
`,
props: {
moved: {
default: false,
type: Boolean,
}
},
computed: {
dataName() {
let mov = {}
this.moved ? mov.fill = 'blue' : mov.fill = 'red'
return mov
}
}
})
new Vue({
el: '#demo',
data() {
return {
moved: false
}
},
methods: {
changeMov() {
this.moved = !this.moved
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<button #click="changeMov">Change</button>
<Child :moved="moved" />
</div>
I have an input in a child component and when the user starts typing in the input it updates the data in the parent component, or should. This is my code, I can post other parts if useful.
Child
<input
:keyup="updateFilters(filters[data.field.key])"
:placeholder="data.label"
/>
methods: {
updateFilters(value) {
this.$emit("input", value);
}
}
Parent
data() {
return {
filters: {
name: "",
age: "",
address: "",
},
};
},
You can change the parent from child component the same way you do emiting other events like onchange, onkeyup, onkeydown etc.
Vue.component('parent', {
data() {
return {
parentValue: ''
};
},
template: `
<div>
<label>Parent state value:</label>
{{parentValue}}
<br/><br/>
<label>input is the child component:</label>
<br/>
<child #fromChild="fromChild"></child>
</div>
`,
methods: {
fromChild(value) {
this.parentValue = value
console.log(value) // someValue
}
}
})
Vue.component('child', {
template: `
<input
v-on:keyup="updateParent($event.target.value)"
placeholder="type something"
/>
`,
methods: {
updateParent(value) {
console.log(value)
this.$emit("fromChild", value);
}
},
})
new Vue({
el: "#app",
data: {
label: 'in Vue'
},
methods: {
toggle: function(todo) {
todo.done = !todo.done
}
}
})
I've prepared a working example here.
I did a custom input component, it works correctly but there is a problem: when i try to update its value from a method the model is updated but the input value still there.
This is my component:
https://codepen.io/ken-ramirez/pen/JgyKad
const BasicInput = {
template: '<input v-model="content" #input="handleInput" />',
prop: ['value'],
data () {
return {
content: this.value
}
},
methods: {
handleInput (e) {
this.$emit('input', this.content)
}
}
}
new Vue({
el: '#app',
data: { name: '' },
components: { BasicInput },
methods: {
restart() {
this.name = ''
}
}
})
You can press on restart button to see what i mean.
You have a mistake in your code: props, not prop.
But this is not enough, also you need to update your content manually, it's not reactive with value prop. Everything declared inside data is not reactive with its initial values.
const BasicInput = {
template: '<input v-model="content" #input="handleInput" />',
props: ['value'],
data () {
return {
content: this.value
}
},
methods: {
handleInput (e) {
this.$emit('input', this.content)
}
},
watch: {
value(val) {
this.content = val;
}
}
}
Like #NikitaK mentioned in his answer you're making a typo , you should write props instead of prop, but i want to give a shorter solution without using watcher property only with this code #input="$emit('input',$event.target.value)"
Full example
const BasicInput = {
template: `<input :value="value" #input="$emit('input',$event.target.value)" />`,
props: ['value']
}
new Vue({
el: '#app',
data() {
return{
name: ''
}},
components: { BasicInput },
methods: {
restart() {
this.name = ''
}
}
})
body {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<basic-input v-model="name"></basic-input>
<p>
<strong>Name:</strong> {{ name }}
</p>
<button #click="restart">restart</button>
</div>
Vue component won't re-render array items after its value was set externally. State chenges but v-for element is not showing the changes.
I have a component that renders items from array. I also have buttons to change the array length and it works well: '+' adds one line and '-' removes the last line. The problem starts when I set the array data from a fetch method. Data is displayed but '+' and '-' buttons don't work.
Here's a link to codesanbox https://codesandbox.io/s/q9jv524kvw
/App.vue
<template>
<div id="app">
<button #click="downloadTemplate">Load data</button>
<HelloWorld :formData="formData" />
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
name: "App",
components: {
HelloWorld
},
data() {
return {
fakeData: {
unloadingContactPersons: [
{
id: this.idGen("unloadingContactPersons"),
value: "123"
},
{
id: this.idGen("unloadingContactPersons"),
value: "1234"
},
{
id: this.idGen("unloadingContactPersons"),
value: "12345"
}
]
},
lengthDependentLoadings: [
"loadingDates",
"loadingAddresses",
"loadingContactPersons"
],
lengthDependentUnloadings: [
"unloadingDates",
"unloadingAddresses",
"unloadingContactPersons"
],
formData: {
unloadingContactPersons: [
{
id: this.idGen("unloadingContactPersons"),
value: ""
}
]
}
};
},
methods: {
idGen(string = "") {
// Math.random should be unique because of its seeding algorithm.
// Convert it to base 36 (numbers + letters), and grab the first 9 characters
// after the decimal.
return (
string +
"_" +
Math.random()
.toString(36)
.substr(2, 9)
);
},
addLine(id) {
console.log("id", id);
const parentName = id.split("_")[0];
const dependentArray = this.lengthDependentLoadings.includes(parentName)
? this.lengthDependentLoadings
: this.lengthDependentUnloadings;
dependentArray.forEach(objName => {
this.formData[objName]
? this.formData[objName].push({
id: this.idGen(objName),
value: ""
})
: null;
});
console.log("--length", this.formData.unloadingContactPersons.length);
},
removeLine(id) {
const parentName = id.split("_")[0];
const dependentArray = this.lengthDependentLoadings.includes(parentName)
? this.lengthDependentLoadings
: this.lengthDependentUnloadings;
dependentArray.forEach(objName => {
this.formData[objName] ? this.formData[objName].pop() : null;
});
console.log("--length", this.formData.unloadingContactPersons.length);
},
downloadTemplate(link) {
// fake fetch request
const getFunctionDummy = data =>
new Promise(resolve => setTimeout(resolve.bind(null, data), 1500));
// data setter
getFunctionDummy(this.fakeData).then(result => {
// set our data according to the template data
const templateKeys = Object.keys(result);
const templateData = result;
this.formData = {};
templateKeys.forEach((key, index) => {
let value = templateData[key];
console.log(value);
if (Array.isArray(value)) {
console.log("array", value);
this.formData[key] = value.map((item, id) => {
console.log("---from-template", item);
return {
id: this.idGen(key),
value: item.value
};
});
} else {
this.formData[key] = {
id: this.idGen(key),
value
};
}
});
});
}
},
mounted() {
// takes id number of item to be added
this.$root.$on("addLine", ({ value }) => {
console.log("---from-mounted", value);
this.addLine(value);
});
// takes id number of item to be removed
this.$root.$on("removeLine", ({ value }) => {
this.removeLine(value);
});
},
beforeDestroy() {
this.$root.$off("addLine");
this.$root.$off("removeLine");
}
};
</script>
/HelloWorld.vue
<template>
<div class="hello">
<div class="form-item">
<div class="form-item__label">
<label :for="formData.unloadingContactPersons"
>Contact person on unload:</label
>
</div>
<div class="form-item__input multiline__wrapper">
<div
class="multiline__container"
#mouseover="handleMouseOver(unloadingContactPerson.id);"
v-for="unloadingContactPerson in formData.unloadingContactPersons"
:key="unloadingContactPerson.id"
>
<span
class="hover-button hover-button__remove"
#click="removeLine(unloadingContactPerson.id);"
><i class="fas fa-minus-circle fa-lg"></i>-</span
>
<input
class="multiline__input"
:id="unloadingContactPerson.id"
type="text"
v-model="unloadingContactPerson.value"
#input="emitFormData"
/>
<span
class="hover-button hover-button__add"
#click="addLine(unloadingContactPerson.id);"
><i class="fas fa-plus-circle fa-lg"></i>+</span
>
</div>
</div>
</div>
</div>
</template>
<script>
import Datepicker from "vuejs-datepicker";
import { uk } from "vuejs-datepicker/dist/locale";
export default {
name: "SubmitForm",
components: {
Datepicker
},
props: {
formData: Object
},
data: () => {
return {
uk,
hoveredItemId: null
};
},
methods: {
emitFormData() {
this.$root.$emit("submitFormData", { value: this.formData });
},
handleMouseOver(id) {
this.hoveredItemId = id;
},
addLine(id) {
// console.log("---add", id);
this.$root.$emit("addLine", {
value: id
});
},
removeLine(id) {
// console.log("---remove", id);
this.$root.$emit("removeLine", {
value: id
});
}
}
};
</script>
Just comment line no 111 of App.vue and it will work.
// this.formData = {}
The problem is that you directly mutating formData object which Vue.js cannot detect. Read more about Array Change detection [List Rendering - Vue.js]
How to binding parent's model to child in Vue.js?
These codes below is works fine. if i fill the input manually, then child's model return it's value to the parent's model.
But the issue is, if the data set from AJAX request in a parent, the input doesn't automatically filled.
Can anyone help me on this?
Form.vue
<template>
<form-input v-model="o.name" :fieldModel="o.name" #listenChanges="o.name = $event"/>
<form-input v-model="o.address" :fieldModel="o.address" #listenChanges="o.address = $event"/>
</template>
<script>
import FormInput from '../share/FormInput.vue'
export default {
data () {
return {
o: {
name: '',
address: ''
}
}
},
components: { 'form-input': FormInput },
created: function() {
axios.get('http://api.example.com')
.then(response => {
this.o.name = response.data.name
this.o.address = response.data.address
})
.catch(e => { console.log(e) })
}
}
</script>
FormInput.vue
<template>
<input type="text" v-model='fieldModelValue' #input="forceUpper($event, fieldModel)">
</template>
<script>
export default {
props: ['fieldModel'],
data() {
return {
fieldModelValue: ''
}
},
mounted: function() {
this.fieldModelValue = this.fieldModel;
},
methods: {
forceUpper(e, m) {
const start = e.target.selectionStart;
e.target.value = e.target.value.toUpperCase();
this.fieldModelValue = e.target.value.toUpperCase();
this.$emit('listenChanges', this.fieldModelValue)
}
}
}
</script>
Things are more straightforward if you take advantage of v-model in components.
If you put v-model on a component, the component should take a prop named value, and should emit input events to trigger it to update.
I like to make a computed to hide the event emitting, and allow me to just v-model the computed inside my component.
new Vue({
el: '#app',
data: {
o: {
name: '',
address: ''
}
},
components: {
'form-input': {
template: '#form-input',
props: ['value'],
computed: {
fieldModelValue: {
get() {
return this.value;
},
set(newValue) {
this.$emit('input', newValue.toUpperCase());
}
}
}
}
},
// Simulate axios call
created: function() {
setTimeout(() => {
this.o.name = 'the name';
this.o.address = 'and address';
}, 500);
}
});
<script src="//unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
Name ({{o.name}})
<form-input v-model="o.name"></form-input>
Address ({{o.address}})
<form-input v-model="o.address"></form-input>
</div>
<template id="form-input">
<input type="text" v-model='fieldModelValue'>
</template>
The mounted() hook is blocking subsequent updates from the parent.
Remove mounted and change v-model to 'fieldModel'
<template>
<input type="text" :value='fieldModel' #input="forceUpper($event, fieldModel)">
</template>
<script>
export default {
props: ['fieldModel'],
data() {
return {
fieldModelValue: ''
}
},
// mounted: function() {
// this.fieldModelValue = this.fieldModel;
// },
methods: {
forceUpper(e, m) {
const start = e.target.selectionStart;
e.target.value = e.target.value.toUpperCase();
this.fieldModelValue = e.target.value.toUpperCase();
this.$emit('listenChanges', this.fieldModelValue)
}
}
}
</script>
Demo CodeSandbox