Validate dynamic fields using v-validate field binding - vue.js

I'm validating date in datetime picker using the following code:
<tr v-for="(input,k) in inputs" :key="k">
<datetime v-validate="validators.ArrivalDate" format="YYYY-MM-DD H:i:s" name="arrivalDateTime" v-model="input.arrivalDateTime" ></datetime>
<tr>
script:
data(){
return {
validators: {
ArrivalDate: {
required: true,
date_format: 'yyyy-MM-dd HH:mm:ss',
after: this.inputs.departureDateTime,
}
},
inputs: [
{
name:'',
arrivalDateTime: '',
departureDateTime:'',
}]
..
i want arrival date time should be greater than this.inputs.departureDateTime Departure field date time. Only issue is i want to pass index of fields also to after:this.inputs.departureDateTime.
Any help is highly appreciated.

You could use a v-validate string, containing a dynamic value of after for each v-for iterator:
<tr v-for="(input,k) in inputs" :key="k">
<datetime
name="departureDateTime"
v-validate="'date_format:yyyy-MM-dd HH:mm:ss|required'"
v-model="input.departureDateTime"
>
</datetime>
<datetime
name="arrivalDateTime" 👇
v-validate="`after:${input.departureDateTime}|date_format:yyyy-MM-dd HH:mm:ss|required`"
v-model="input.arrivalDateTime"
>
</datetime>
<tr>
demo

Related

vue2 list return mixed string or component

In loop like this, I mostly just iterate over item values as strings, but sometimes need to return rendered component, for example build link element, or dropdown menu, for that table cell - need to find a way to return other component output instead of raw html string
<tr class="listing-item listing-item-category">
<td v-for="td in headeritems">{{val(td.k)}}</td>
</tr>
Is that even possible? I've found no mention of this, how should the method code go to return other component output? I know I would have to use v-html, but how to get it?
Assume we have a list like this:
headerItems: [
{
type: 'text',
value: 'Some text'
},
{
type: 'img',
props: {
src: 'http://some-where....'
}
},
{
type: 'my-component',
value: 'v-model value',
props: {
prop1: 10,
prop2: 'Blah bla',
},
events: {
myEvt: () => console.log('myEvt has fired')
}
},
],
So, We can render it:
<tr>
<td
v-for="(item, i) in headerItems" :key="i"
>
<div v-if="item.type === 'text'"> {{ item.value }}</div>
<component
v-else
:is="item.type"
v-model="item.value"
v-bind="item.props"
v-on="item.events"
/>
</td>
</tr>

How to return date format from Bootstrap-vue's b-form-timepicker component from HH:mm:ss to HH:mm

Bootstrap-vue b-form-timepicker returns the value as with the format HH:mm:ss. I need its return value as HH:m', but I cannot find any way to change it.
Is there any way to change the return value format into HH:mm? If there is, please help.
You can remove the seconds by removing the property show-seconds as described in the component table properties. Then we'll format the date using vanilla JavaScript inside a Vue's watcher like so:
<template>
<div>
<label for="example-input">Choose a time</label>
<b-input-group class="mb-3">
<b-form-input
id="example-input"
v-model="value"
type="text"
placeholder="HH:mm"
></b-form-input>
<b-input-group-append>
<b-form-timepicker
v-model="value"
button-only
right
locale="en"
aria-controls="example-input"
></b-form-timepicker>
</b-input-group-append>
</b-input-group>
<p>Value: '{{ value }}'</p>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
value: "",
};
},
watch: {
value() {
this.value = this.value.split(':').slice(0,2).join(':');
},
},
};
</script>
You can check this working code playground example.

Binding input parameters to URL params in VueJS

Is there an easy way to bind the input parameters, to allow people to bookmark the Javascript calculation?
var app = new Vue({
el: '#app',
data: {
// dummy data
disksize: 100,
cost: 0.05,
items: [{
freq: "daily",
qty: 3,
ratio: 5
},
{
freq: "weekly",
qty: 0,
ratio: 10
},
{
freq: "yearly",
qty: 0,
ratio: 20
}
],
},
computed: {
initialCost() {
return Number(this.disksize * this.cost)
},
subtotalSize() {
return this.items.map((item) => {
return Number(item.qty * item.ratio / 100 * this.disksize)
});
},
subtotalCost() {
return this.items.map((item) => {
return Number(item.qty * item.ratio / 100 * this.disksize * this.cost)
});
},
subTotals() {
return this.items.reduce((subTotals, item) => {
return Number(subTotals + item.qty * item.ratio / 100 * this.disksize * this.cost)
}, 0);
},
total() {
return Number(this.initialCost + this.subTotals)
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<p><label>Size of disk: <input type=number v-model.number="disksize">GB</label></p>
<p><label>EBS cost: <input type=number step="0.001" v-model.number="cost">per GB-month of data stored</label></p>
<p><label>Cost of initial EBS disk: <input readonly :value="initialCost.toFixed(2)">USD per month</label></p>
<h3>
EBS snapshots
</h3>
<p>
EBS snapshots are incremental, so if you created a snapshot hourly each snapshot would only be backing up the changes that had been written to the volume in the last hour.
</p>
<table title="Retention">
<thead align="left">
<th>Frequency</th>
<th>Quantity</th>
<th>Ratio</th>
<th>Size</th>
<th>Cost per month</th>
</thead>
<tbody>
<tr v-for="(item, index) in items">
<td><input readonly :value="item.freq.charAt(0).toUpperCase() + item.freq.slice(1)" size="10"></td>
<td><input type="number" min="0" max="100" v-model.number="item.qty" size="10"></td>
<td><input type="number" min="0" max="100" v-model.number="item.ratio" size="3">%</td>
<td><input type="number" step="0.01" :value="subtotalSize[index].toFixed(2)" readonly size="10">GB</td>
<td><input type="number" step="0.01" :value="subtotalCost[index].toFixed(2)" readonly size="10">USD</td>
</tr>
</tbody>
</table>
<p>
<label>Total
{{initialCost.toFixed(2)}} initial cost + {{subTotals.toFixed(2)}} snapshots = <strong>{{total.toFixed(2)}} USD per month</strong>
</label>
</p>
</div>
I don’t want to use npm et al. Just prepackaged URLs like https://unpkg.com/vue-router/dist/vue-router.js ... if that's the solution. I'm not sure.
https://example.com/?disk=100&quantity=3&ratio=5
Quantity/Ratio can actually repeat, not sure what the at looks like in URL params. Any hints?
If I understand your question correctly, you want to do that allow an url like:
https://example.com/?disk=100&quantity=3&ratio=5
will passdisk,quantity and ratio props to your component.
This is achievable using vue router. One possible way is to use it in function mode:
You can create a function that returns props. This allows you to cast
parameters into other types, combine static values with route-based
values, etc.
const router = new VueRouter({
routes: [
{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }
]
})
If you simply want to get the values of a URL parameter and pass them along to a component, you can make use of Javascript's URLSearchParams() method (this won't work in IE 11, but a polyfill might be available).
function getURLParam (param) {
const queryString = window.location.search // query string from URL
const params = new URLSearchParams(queryString) // parse query string into object
return params.get(param) // get the value of the param name passed into function
}
Experiment with the code above. I can't be sure of the exact implementation you need, but this should suffice as a good starting point.

Creating a button dynamically in vuejs table

I am trying to create a table in vuejs for a personal project (I dont want to use a existing table) and I am facing probably a newbie problem.
I am trying to insert on my last column some buttons, but I dont know why, the grid is rendering my element tag instead of the element himself.
May someone explain to me why this does not work ? And, how can I create this feature ?
Fiddle: https://jsfiddle.net/y7830cvd/1/
<div id="app">
<div>
<vue-grid :rows="gridData" :title="nTitle"></vue-grid>
</div>
</div>
Vue.component('vue-grid', {
props: ['rows', 'title'],
template: `<div>
<h2>{{title}}</h2>
<div class="table-wrapper">
<table class="fl-table">
<thead>
<tr>
<th v-for="col in columns" :key="col.id" v-on:click="sortTable(col)">{{col}}</th>
</tr>
</thead>
<tbody v-if="rows.length > 0">
<tr v-for="row in rows" :key="row.id">
<td v-for="col in columns" :key="col.id">{{row[col]}}</td>
</tr>
</tbody>
</table>
</div>
</div>`,
computed: {
columns: function columns() {
if (this.rows.length == 0) {
return []
}
return Object.keys(this.rows[0])
}
},
sortTable(col) {
this.rows.sort(function(a, b) {
if (a[col] > b[col]) {
return 1
} else if (a[col] < b[col]) {
return -1
}
return 0
})
},
methods: {
formatter(row, column) {
return row.address
},
filterTag(value, row) {
return row.tag === value
},
filterHandler(value, row, column) {
const property = column['property']
return row[property] === value
}
}
});
var app = new Vue({
el: '#app',
data(){
return {
gridData: [
{"id" : 1, "name": "firstValue", "something": "wha the fox say?","options" : "<button>Add</button>" },
{"id" : 1, "name": "firstValue", "something": "uauu uauu uauu?"},
{"id" : 1, "name": "firstValue", "something": "The cow goes mu?"}
],
nTitle: "Hello There!"
}},
})
Try v-html:
<td v-for="col in columns" :key="col.id">
<span v-if="col == 'options'" v-html="row[col]"></span>
<span v-else>{{row[col]}}</span>
</td>
Something you should consider (source documentation - link above):
Updates the element’s innerHTML. Note that the contents are inserted
as plain HTML - they will not be compiled as Vue templates. If you
find yourself trying to compose templates using v-html, try to rethink
the solution by using components instead.

Vue,js, Computing a property with v-model?

I have a vue page that loads a bunch of actions from a database. It then creates a table with some data got from the action, using v-for.
Here is my problem: In one of the table rows I need a checkbox that is v-modeled to an attribute, action.weekly. Ideally, it will be a boolean. But there are a bunch of action entries in my database that don't have a weekly attribute. In those cases, I need the checkbox checked, as if it were true. Normally, I would use a computed property here, but you can't pass arguments to computed properties, so I don't know how to tell vue which action to look at at (I can't pass $event, ndx in to a computed property like I am doing below with handleEnableChanged() ).
Here is my code for the table:
<tbody>
<tr v-for="(action, ndx) in actions" >
<td class="pointer" #click='openModalCard(action, ndx)'>
{{action.name}}
</td>
<input type="checkbox" v-model="action.weekly??" #change="handleEnableChanged($event, ndx)"/>
</td>
<input type="checkbox" v-model="action.enabled" #change="handleEnableChanged($event, ndx)">
</td>
</tr>
</tbody>
In the cases where action does not have a weekly attribute, I want the checkbox checked as if it were true. How can I accomplish this?
If there is a better way to approach this, please let me know. I'm still a novice with vue.js.
I think it would be easiest to use v-if and v-else for this.. With that being said, I have also provided an example of how to handle this without v-if and v-else..
Using v-if and v-else:
new Vue({
el: "#root",
data: {
actions: [
{
name: "first",
weekly: true,
enabled: false
},
{
name: "second",
enabled: false
},
{
name: "third",
weekly: true,
enabled: true
},
{
name: "fourth",
enabled: true
}
]
},
template: `
<tbody>
<tr v-for="(action, ndx) in actions" >
<td class="pointer" #click='console.log(action, ndx)'>{{action.name}}</td>
<input v-if="'weekly' in action" type="checkbox" v-model="action.weekly" #change="handleEnableChanged($event, ndx)"/>
<input v-else type="checkbox" checked="true" #change="handleEnableChanged($event, ndx)"/>
</td>
<input type="checkbox" v-model="action.enabled" #change="handleEnableChanged($event, ndx)">
</td>
</tr>
</tbody>
`,
methods: {
handleEnableChanged(evt, ndx) {
console.log(evt, ndx);
},
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="root"></div>
Without v-if and v-else:
new Vue({
el: "#root",
data: {
actions: ""
},
template: `
<tbody>
<tr v-for="(action, ndx) in actions" >
<td class="pointer" #click='console.log(action, ndx)'>{{action.name}}</td>
<input type="checkbox" v-model="action.weekly" #change="handleEnableChanged($event, ndx)"/>
</td>
<input type="checkbox" v-model="action.enabled" #change="handleEnableChanged($event, ndx)">
</td>
</tr>
</tbody>
`,
methods: {
handleEnableChanged(evt, ndx) {
console.log(evt, ndx);
},
getActions() {
let originalActions = [
{
name: "first",
weekly: true,
enabled: false
},
{
name: "second",
enabled: false
},
{
name: "third",
weekly: true,
enabled: true
},
{
name: "fourth",
enabled: true
}
];
this.actions = originalActions.map(a => {
return {
name: a.name,
weekly: 'weekly' in a ? a.weekly : true,
enabled: a.enabled
}
})
}
},
created() {
this.getActions();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="root"></div>
You can just do this:
<input type="checkbox" :checked="!!action.weekly">
The !! operator will return true if the value is not undefined or empty
If you want all items in the array to have an action.weekly property, then update your data when it is initially retrieved. You can then just use v-model="action.weekly" for all items in the array.
this.newArray = this.oldArray.map(item => {
if(!item.hasOwnProperty('weekly')){
item.weekly = true;
}
return item
})