Skip to content
Open
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ Primary wrapper component.
},
age: {
required: true,
label: 'Age'
label: 'Age',
type: Number
}
}"
>
Expand All @@ -101,16 +102,27 @@ Primary wrapper component.

| Prop | Default | Description |
| ------ | ------- | ----------- |
| fields | null | (required) The field names used to map the CSV. |
| fields | null | (required) The fields used to map the CSV - see below. |
| text | see below | (optional) Override the default text used in the component. |
| modelValue | N/A | (optional) Binds to the mapped CSV object. |

#### Default text
##### Fields Prop

The fields prop may be a simple array (e.g. `['name', 'age']) or an object with the following properties:

| Prop | Default | Description |
| ------ | ------- | ----------- |
| required | true | (required) The field names used to map the CSV. |
| label | N/A | (required) Override the default text used in the component. |
| type | String | (optional) A primitive object used to cast the field value. |

##### Default Text Prop

```json
{
errors: {
fileRequired: 'A file is required',
invalidFieldType: "Invalid data type in row ${row} column ${col}. ",
invalidMimeType: "Invalid file type"
},
toggleHeaders: 'File has headers',
Expand Down
36 changes: 34 additions & 2 deletions src/components/VueCsvImport.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
const defaultLanguage = {
errors: {
fileRequired: 'A file is required',
invalidFieldType: 'Invalid data type in row ${row} column ${col}. ',
invalidMimeType: "Invalid file type"
},
toggleHeaders: 'File has headers',
Expand All @@ -46,6 +47,7 @@
key: key,
label: get(val, 'label', val),
required: get(val, 'required', true),
type: get(val, 'type', String),
};
});
}
Expand Down Expand Up @@ -84,10 +86,12 @@
const buildMappedCsv = function () {
let newCsv = VueCsvImportData.fileHasHeaders ? VueCsvImportData.rawCsv : drop(VueCsvImportData.rawCsv);

VueCsvImportData.value = map(newCsv, (row) => {
VueCsvImportData.errors = [];

VueCsvImportData.value = map(newCsv, (row, index) => {
let newRow = {};
forEach(VueCsvImportData.map, (column, field) => {
set(newRow, field, get(row, column));
set(newRow, field, typeCast(row, column, field, index));
});

return newRow;
Expand All @@ -96,6 +100,34 @@
emit('update:modelValue', VueCsvImportData.value);
};

const typeMap = VueCsvImportData.fields.reduce((a, f) => set(a, f.key, f.type ?? String), {});

const typeCast = function(row, column, field, index) {
let fieldVal = get(row, column);
let castVal = typeMap[field](fieldVal);

// Handle Booleans
if (typeMap[field] === Boolean) {
switch (fieldVal.toLowerCase().trim()) {
case 'false': case 'no': case '0': case 'null': case '': return false;
case 'true': case 'yes': case '1': return true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fieldVal.toLowerCase().trim() === 'false' ? false : !!string Could be an option too but I do like the idea of being able to pass these other values. "on/off" also comes to mind.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have time to push a documentation update tomorrow and I'll add on/off to the boolean switch statement.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, just pushed a few more commits to handle on/off booleans, improve error handling, and better document the new functionality. Wow. This really turned into a big thing. 😅

default: return fieldVal; // Return uncast value
}
}

// Catch non-numeric Numbers
if (Object.is(NaN, castVal)) {
VueCsvImportData.errors.push(
defaultLanguage.errors.invalidFieldType
.replace(/\${row}/g, index + (VueCsvImportData.fileHasHeaders ? 1 : 2))
.replace(/\${col}/g, column + 1)
);
return fieldVal; // Return uncast value
}

return castVal;
};

provide('VueCsvImportData', VueCsvImportData);
provide('buildMappedCsv', buildMappedCsv);

Expand Down