v-currency how to remove symbol from model value? - vue.js

I am using v-currency for vuetify
<v-text-field
v-model.trim="$v.service.manual_cost_per_slot.$model"
v-currency="{
currency: service.currency,
locale: locale,
allowNegative: false,
masked: false
}"
:label="$t('service.manual_cost_per_slot')"
class="purple-input"
/>
The value is saved into the model along with the currency symbol and thousands separator.
How can I prevent from this? what I want to achieve is to still see the symbol and separator on the form but the value in model should be a number with no symbol or separator. I am sure I am missing something obvious here...

Well, as the documentation of the module says:
... the v-currency
directive always emits the formatted string instead of the number
value when used with v-model. To get the number value you can use the
parseCurrency method.
There's an example there on the page. If you want the parsed value without symbols, use that numberValue computed property:
import { CurrencyDirective, parseCurrency } from 'vue-currency-input'
export default {
directives: {
currency: CurrencyDirective
},
data: () => ({
value: '$1,234.50',
locale: 'en',
currency: 'USD'
}),
computed: {
options () {
return {
locale: this.locale,
currency: this.currency
}
},
numberValue () {
return parseCurrency(this.value, this.options)
// OR using the instance method:
// return this.$parseCurrency(this.value, this.options)
}
}
}

Related

How to create getters and setters for all sub-properties of a Vuex state property efficiently?

I couldn't find the answer anywhere.
Let's say we have Vuex store with the following data:
Vuex store
state: {
dialogs: {
dialogName1: {
value: false,
data: {
fileName: '',
isValid: false,
error: '',
... 10 more properties
}
},
dialogName2: {
value: false,
data: {
type: '',
isValid: false,
error: '',
... 10 more properties
}
}
}
}
Dialogs.vue
<div v-if="dialogName1Value">
<input
v-model="dialogName1DataFileName"
:error="dialogName1DataIsValid"
:error-text="dialogName1DataError"
>
<v-btn #click="dialogName1Value = false">
close dialog
</v-btn>
</div>
<!-- the other dialogs here -->
Question
Let's say we need to modify some of these properties in Dialogs.vue.
What's the best practices for creating a getter and setter for every dialog property efficiently, without having to do it all manually like this:
computed: {
dialogName1Value: {
get () {
return this.$store.state.dialogs.dialogName1.value
},
set (value) {
this.$store.commit('SET', { key: 'dialogs.dialogName1.value', value: value })
}
},
dialogName1DataFileName: {
get () {
return this.$store.state.dialogs.dialogName1.data.fileName
},
set (value) {
this.$store.commit('SET', { key: 'dialogs.dialogName1.data.fileName', value: value })
}
},
dialogName1DataIsValid: {
get () {
return this.$store.state.dialogs.dialogName1.data.isValid
},
set (value) {
this.$store.commit('SET', { key: 'dialogs.dialogName1.data.isValid', value: value })
}
},
dialogName1DataIsError: {
get () {
return this.$store.state.dialogs.dialogName1.data.error
},
set (value) {
this.$store.commit('SET', { key: 'dialogs.dialogName1.data.error', value: value })
}
},
... 10 more properties
And this is only 4 properties...
I suppose I could generate those computed properties programmatically in created(), but is that really the proper way to do it?
Are there obvious, commonly known solutions for this issue that I'm not aware of?
getters can be made to take a parameter as an argument - this can be the 'part' of the underlying state you want to return. This is known as Method-style access. For example:
getFilename: (state) => (dialogName) => {
return state.dialogs[dialogName].data.fileName
}
You can then call this getter as:
store.getters.getFilename('dialogName1')
Note that method style access doesn't provide the 'computed property' style caching that you get with property-style access.
For setting those things in only one central function you can use something like this:
<input
:value="dialogName1DataFileName"
#input="update_inputs($event, 'fileName')">
// ...
methods:{
update_inputs($event, whichProperty){
this.$store.commit("SET_PROPERTIES", {newVal: $event.target.value, which:"whichProperty"})
}
}
mutation handler:
// ..
mutations:{
SET_PROPERTIES(state, payload){
state.dialogName1.data[payload.which] = payload.newVal
}
}
Let me explain more what we done above. First we change to v-model type to :value and #input base. Basically you can think, :value is getter and #input is setter for that property. Then we didn't commit in first place, we calling update_inputs function to commit because we should determine which inner property we will commit, so then we did send this data as a method parameter (for example above code is 'fileName') then, we commit this changes with new value of data and info for which property will change. You can make this logic into your whole code blocks and it will solved your problem.
And one more, if you want to learn more about this article will help you more:
https://pekcan.dev/v-model-using-vuex/

How to create custom start/end date Vue custom validator

I am trying to create a custom Vue validator. I have reviewed their docs https://vuelidate.netlify.com/#custom-validators, as well as a very useful tutorial https://vuejsdevelopers.com/2018/08/27/vue-js-form-handling-vuelidate/.
However, I still don't see a clear example of how to do the following:
I have two datepicker input fields, a start and end date. I want to be able to create a validator which can
Check both dates in tandum to make sure that the end date is not before the start date
Have a single validation message based on this (aka: we don't want one field with 'Start date can't be before end date' and the other with 'End date can't be before start date')
This type of functionality (or using other fields values inside a different one) is basically what the core sameAs validator (see below) has:
import { ref, withParams } from './common'
export default (equalTo) =>
withParams({ type: 'sameAs', eq: equalTo }, function(value, parentVm) {
return value === ref(equalTo, this, parentVm)
})
I have tried to mimic this, but its not working...
import { ref, withParams } from 'vuelidate/lib/validators/common.js'
export default (endDate) =>
withParams({ type: 'dateRange', eq: endDate }, function(value, parentVm) {
console.log('parentVm', parentVm);
return value < ref(endDate, this, parentVm)
})
Its not even logging my console.log. Here is the code calling it
<date-picker id="financial-start-date" v-model="$v.start_date.$model" :config="datepickerConfig"></date-picker>
<date-picker id="financial-end-date" v-model="$v.end_date.$model" :config="datepickerConfig"></date-picker>
Validations:
validations: {
transaction_id: {
},
start_date: {
},
end_date: {
dateRange: dateRange('startDate')
}
},
Can be solved using the following code:
first create custom validator:
const isAfterDate = (value, vm) => {
return new Date(value).getTime() > new Date(vm.startDate).getTime();
};
Second, call the validator within validations:
endDate: {
required,
isAfterDate
}

Why it is hard to use vue-i18n in vue data() (why it is not reactive)

I am using vue-i18n in a vue project. And I found it really confusing when using some data in vue data with i18n. Then if I change locale, that data is not reactive. I tried to return that data from another computed data but anyways it is not reactive because i18n is written in data. *My situation - * I want to show table with dropdown(list of columns with checkbox) above it. When user checks a column it will be showed in table if unchecks it won't. It is working fine until I change locale. After changing locale table columns is not translated but dropdown items is reactively translated and my code won't work anymore. Here is some code to explain better: In my myTable.vue component I use bootstrap-vue table -
template in myTable.vue
<vs-dropdown vs-custom-content vs-trigger-click>
<b-link href.prevent class="card-header-action btn-setting" style="font-size: 1.4em">
<i class="fa fa-th"></i>
</b-link>
<vs-dropdown-menu class="columns-dropdown">
<visible-columns :default-fields="columns" #result="columnListener"></visible-columns>
</vs-dropdown-menu>
</vs-dropdown>
<b-table class="generalTableClass table-responsive" :fields="computedFieldsForTable">custom content goes here</b-table>
script in myTable.vue
data(){
return {
fieldsForTable: [];
}
},
computed: {
computedFieldsForTable () {
return this.fieldsForTable;
},
columns() {
return [
{
key: 'id',
label: this.$t('id'),,
visible: true,
changeable: true
},
{
key: 'fullName',
label: this.$t('full-name'),,
visible: true,
changeable: true
},
{
key: 'email',
label: this.$t('email'),,
visible: true,
changeable: true
}
]
}
},
mounted () {
this.fieldsForTable = this.filterColumns(this.columns);
},
methods: {
filterColumns(columns = []) {
return columns.filter(column => {
if (column.visible) {
return column
}
})
},
columnListener ($event) {
this.fieldsForTable = this.filterColumns($event)
}
}
Can someone give me some advice for this situation ?
*EDIT AFTER SOME DEBUGGING: I think when filtering columns(in computed) and returning it for fieldsForTable inside filterColumns(columns) method, it actually returning array(of objects) with label='Label Name' not label=this.$t('labelName'). So after filtering the new array has nothing to do with vue-i18n. My last chance is reloading the page when locale changes.
Trying modify computedFieldsForTable as follows. You need to reference this.columns in computedFieldsForTable, so that Vue can detect the change of labels in this.columns.
computedFieldsForTable () {
return this.filterColumns(this.columns);
},
EDITED: put your this.columns in data. Then
columnListener ($event) {
this.columns = $event;
}
I hope i didn't misunderstand what you mean.
EDITED (again):
Maybe this is the last chance that I think it can work. Put columns in computed() still and remove computedFieldsForTable. Finally, just leave fieldsForTable and bind it on fields of <b-table>.
watch: {
columns(val) {
this.fieldsForTable = this.filterColumns(val)
}
},
method: {
columnListener ($event) {
this.fieldsForTable = this.filterColumns($event)
}
}
However, I think it is better and easier to reload page whenever local change. Especially when your columns have a more complex data structure.

How to set Vue component that is imported globally to have preset attributes?

For example, I'm using vue-numeric.
Every time I need it, I always use at least this classes and attributes.
<vue-numeric
class="form-control border-input text-right"
:separator="."
:minus=false
/>
Is it possible to declare some of the attributes when registering the component, so that I don't need to type it each time I need vue-numeric?
And also that maybe one day I decided to change the class, I just need to change it in 1 file, e.g in main.js. I don't want to change it from node_modules.
I would like to do something like this.
Vue.component('vue-numeric', VueNumeric, {
class:"form-control border-input text-right",
separator:".",
minus: false
}
so then in template, I just need to add some specific attributes.
<vue-numeric v-model="price" :min=0 />
Create a functional wrapper component which applies the default prop values:
Vue.component('vue-numeric-default', {
functional: true,
render(h, ctx) {
return h('vue-numeric', Object.assign({}, ctx.data, {
// Apply default class only if no class was defined
staticClass: ctx.data.class || ctx.data.staticClass
? ctx.data.staticClass
: 'form-control border-input text-right',
// Apply default props
props: Object.assign({
separator: '.',
minus: false,
}, ctx.props),
}), ctx.children);
},
});
You could also use
{ separator: '.', ...ctx.props }
syntax instead of
Object.assign({ separator: '.' }, ctx.props)
if it is supported.
You can specify the classes in component's root element. And apply default props in mounted hook.
You can define the default value to the props methods in a component and if you
want to override it must be called it else it has the default value,
you should add below code to the "vue-numeric" component file
props: {
class: {
type: String,
default: 'form-control border-input text-right'
},
separator: {
type: String,
default: '.'
}
minus: {
type: Boolean,
default: false
}
}

Vue.js - v-model with a predefined text

I have an input attribute that I want to have text from a source and is two-way binded
// messages.html
<input type="textarea" v-model="newMessage">
// messages.js
data () {
newMessage: ''
},
props: {
message: {
type: Object,
required: true,
default () {
return {};
}
}
// the message object has keys of id, text, and hashtag
I would like the initial value of input to be message.text. Would it be appropriate to do something like newMessage: this.message.text?
EDIT
I tried adding :value="message.text" in input but that didn't really show anything
Yes, you can reference the props in the data function.
data(){
return {
newMessage: this.message.text
}
}