Vuelidate empty object on select validation issue - vue.js

I would like to know how to validate empty object using vuelidate. I tried to give a demonstration on jsfiddle as links follows
Vue.use(window.vuelidate.default)
const { required, minLength } = window.validators
new Vue(
{
el: "#app",
data: {
companies: [
{
id: 1,
name: 'facebook'
},
{
id: 2,
name: 'apple'
}
],
text: {
id: null,
name: null
}
},
validations: {
text: {
required
}
}
}
)
jsfiddle

$v.text is valid because it is a non-empty object. That means it doesn't have 'falsy' value so it meets the requirement. One way to make it work:
validations: {
text: {
id: {required},
name: {required},
},
},
JSFiddle
If you don't want to repeat items object structure, you can write a custom validator.

There is missing information about how to use withParams in the documentation of vuelidate page.
So i have searched on its github page and found this link .
According to link i came up with that solution
import { withParams } from 'vuelidate'
export const checkIfNull = withParams(
{ type: 'required' },
value => (value.id === null ? false : true)
)

There is nothing special about validating an object, you just need to define the structure and add any validation rules you require.
Please see the example I created and take another look at the Collections docs.

Related

Getting documents with ID from firstore collection

While using Firestore, vuefire, vue-tables-2, I stuck getting document's id.
My data structure is as below.
Here is my code.
<v-client-table :columns="columns" :data="devices" :options="options" :theme="theme" id="dataTable">
import { ClientTable, Event } from 'vue-tables-2'
import { firebase, db } from '../../firebase-configured'
export default {
name: 'Devices',
components: {
ClientTable,
Event
},
data: function() {
return {
devices: [],
columns: ['model', 'id', 'scanTime', 'isStolen'],
options: {
headings: {
model: 'Model',
id: 'Serial No',
scanTime: 'Scan Time',
isStolen: 'Stolen YN'
},
templates: {
id: function(h, row, index) {
return index + ':' + row.id // <<- row.id is undefined
},
isStolen: (h, row, index) => {
return row.isStolen ? 'Y': ''
}
},
pagination: {
chunk: 5,
edge: false,
nav: 'scroll'
}
},
useVuex: false,
theme: 'bootstrap4',
template: 'default'
}
},
firestore: {
devices: db.collection('devices')
},
};
My expectation is devices should id property as vuefire docs.
But array this.devices didn't have id field even if I check it exist it console.
Basically, every document already has id attribute, but it's non-enumerable
Any document bound by Vuexfire will retain it's id in the database as
a non-enumerable, read-only property. This makes it easier to write
changes and allows you to only copy the data using the spread operator
or Object.assign.
You can access id directly using device.id. But when passing to vue-tables-2、devices is copied and lost id non-enumerable attribute.
I think you can workaround using computed property
computed: {
devicesWithId() {
if (!this.devices) {
return []
}
return this.devices.map(device => {
...device,
id: device.id
})
}
}
Then, please try using devicesWithId in vue-tables-2 instead.

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.

Datatable dynamic data not available from apollo graphql query (static works)

import gql from 'graphql-tag'
export default {
name: 'patient-list',
data () {
return {
patients: [],
patientsColumns: [
{ name: 'id', label: 'number', field: 'patient_number' },
{ name: 'last', label: 'last name', field: row => row.user.last },
{ name: 'first', label: 'first name', field: row => row.user.first }
]
}
},
apollo: {
patients: {
query: gql`
{
patients {
patient_number
user {
first
last
}
}
}
`
}
}
}
<div>
{{patients[0]}}
<q-table
:data="patients"
:columns="patientsColumns"
row-key="name"
></q-table>
</div>
I have problem with printing datas in table by apollo... and i can't figure out what is wrong with this :)
the main problem with my console is
( TypeError: Cannot add property __index, object is not extensible )
I created sample static data and was ok, but when I what use datas passed from apollo by graphql query datatable show "no data available"
If someone give me small advice I will be grateful :)
Error in render: "TypeError: Cannot add property __index, object is
not extensible"
Datatable and sample record passed from Graphql by apollo
this is my code
Printed first object from apollo
ERRORS FROM CONSOLE

Validation of fetched data from API Redux React

So, I will go straight to the point. I am getting such data from api:
[
{
id: 123,
email: asd#asd.com
},
{
id: 456,
email: asdasd.com
},
{
id: 789,
email: asd#asd
},
...
]
and I should validate email and show this all info in a list, something like this:
asd#asd.com - valid
asdasd.com - invalid
asd#asd - invalid
...
My question is what is the best way to store validation data in a store? Is it better to have something like "isValid" property by each email? I mean like this:
store = {
emailsById: [
123: {
value: asd#asd.com,
isValid: true
},
456: {
value: asdasd.com,
isValid: false
},
789: {
value: asd#asd,
isValid: false
}
...
]
}
or something like this:
store = {
emailsById: [
123: {
value: asd#asd.com
},
456: {
value: asdasd.com
},
789: {
value: asd#asd
}
...
],
inValidIds: ['456', '789']
}
which one is better? Or maybe there is some another better way to have such data in store? Have in mind that there can be thousands emails in a list :)
Thanks in advance for the answers ;)
I recommend reading the article "Avoiding Accidental Complexity When Structuring Your App State" by Tal Kol which answers exactly your problem: https://hackernoon.com/avoiding-accidental-complexity-when-structuring-your-app-state-6e6d22ad5e2a
Your example is quite simplistic and everything really depends on your needs but personally I would go with something like this (based on linked article):
var store = {
emailsById: {
123: {
value: '123#example.com',
},
456: {
value: '456#example.com',
},
789: {
value: '789#example.com',
},
// ...
},
validEmailsMap: {
456: true, // true when valid
789: false, // false when invalid
},
};
So your best option would be to create a separate file that will contain all your validations methods. Import that into the component you're using and then when you want to use the logic for valid/invalid.
If its something that you feel you want to put in the store from the beginning and the data will never be in a transient state you could parse your DTO through an array map in your reducer when you get the response from your API.
export default function (state = initialState, action) {
const {type, response} = action
switch (type) {
case DATA_RECIEVED_SUCCESS:
const items = []
for (var i = 0; i < response.emailsById.length; i++) {
var email = response.emailsById[i];
email.isValid = checkEmailValid(email)
items.push(email)
}
return {
...state,
items
}
}
}
However my preference would be to always check at the last moment you need to. It makes it a safer design in case you find you need to change you design in the future. Also separating the validation logic out will make it more testable
First of all, the way you defined an array in javascript is wrong.
What you need is an array of objects like,
emails : [
{
id: '1',
email: 'abc#abc.com',
isValid: true
},
{
id: '2',
email: 'abc.com',
isValid: false;
}
];
if you need do access email based on an id, you can add an id property along with email and isValid. uuid is a good way to go about it.
In conclusion, it depends upon your use case.
I believe, the above example is a good way to keep data in store because it's simple.
What you described in your second example is like maintaining two different states. I would not recommend that.

Find object by match property in nested array

I'm not seeing a way to find objects when my condition would involve a nested array.
var modules = [{
name: 'Module1',
submodules: [{
name: 'Submodule1',
id: 1
}, {
name: 'Submodule2',
id: 2
}
]
}, {
name: 'Module2',
submodules: [{
name: 'Submodule1',
id: 3
}, {
name: 'Submodule2',
id: 4
}
]
}
];
This won't work because submodules is an array, not an object. Is there any shorthand that would make this work? I'm trying to avoid iterating the array manually.
_.where(modules, {submodules:{id:3}});
Lodash allows you to filter in nested data (including arrays) like this:
_.filter(modules, { submodules: [ { id: 2 } ]});
Here's what I came up with:
_.find(modules, _.flow(
_.property('submodules'),
_.partialRight(_.some, { id: 2 })
));
// → { name: 'Module1', ... }
Using flow(), you can construct a callback function that does what you need. When call, the data flows through each function. The first thing you want is the submodules property, and you can get that using the property() function.
The the submodules array is then fed into some(), which returns true if it contains the submodule you're after, in this case, ID 2.
Replace find() with filter() if you're looking for multiple modules, and not just the first one found.
I think your best chance is using a function, for obtaining the module.
_.select(modules, function (module) {
return _.any(module.submodules, function (submodule) {
return _.where(submodule, {id:3});
});
});
try this for getting the submodule
.where(.pluck(modules, "submodules"), {submodules:{id:3}});
I looked into this and I think the best option is to use Deepdash. It's a collection of methods to do deeply filter, find etc.
Sure it would be possible with lodash alone but with Deepdash it's easier.
I tried to convert the previous answer to the latest Lodash version but that was not working. Every method was deprecated in v4 of Lodash. Possible replacements: select = map or filter, any = some, where = filter)
findDeep returns an object with some information to the found item (just some values, see the docs for more details):
value is the object found
key that's the index in the nested array
parent the parent of the value
So the code for the findDeep looks like:
const modules = [{
name: 'Module1',
submodules: [{
name: 'Submodule1',
id: 1
}, {
name: 'Submodule2',
id: 2
}]
}, {
name: 'Module2',
submodules: [{
name: 'Submodule1',
id: 3
}, {
name: 'Submodule2',
id: 4
}]
}];
const getModule = (modules, id) =>
_.findDeep(modules, module => module.id === id, {
childrenPath: "submodules"
});
const resultEl = document.getElementById("result");
const foundModule = getModule(modules, 3).value;
resultEl.innerText = JSON.stringify(foundModule, null, 2);
<script src="https://cdn.jsdelivr.net/npm/deepdash/browser/deepdash.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash/lodash.min.js"></script>
<script>
deepdash(_);
</script>
<h2>Example to get module with id = 3</h2>
<pre id="result"></pre>