Laravel uploading CSV with field mapping Undefined index: problem - javascript

I am trying to upload excel on my db with field matching, i have a working example but now need to develop a little more and am a bit stuck. What I'm confused about is the best way to handle the 2nd import mapping process. After i upload excel file this is my 2nd step https://prnt.sc/d3v1Jk9Ta_-A and the problem if on last field on excel if phone and on the db i need to match with phone_number i get this error
ErrorException
Undefined index: phone_number
But if i match the excel header phone with phone on db the data will be imported and will work fine. How can i fix this problem ? Any idea?
This is my code on import_blade
<table class="min-w-full divide-y divide-gray-200 border">
#if (isset($headings))
<thead>
<tr>
#foreach ($headings[0][0] as $csv_header_field)
{{-- #dd($headings)--}}
<th class="px-6 py-3 bg-gray-50">
<span class="text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">{{ $csv_header_field }}</span>
</th>
#endforeach
</tr>
</thead>
#endif
<tbody class="bg-white divide-y divide-gray-200 divide-solid">
#foreach($csv_data as $row)
<tr class="bg-white">
#foreach ($row as $key => $value)
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
{{ $value }}
</td>
#endforeach
</tr>
#endforeach
<tr>
#foreach ($csv_data[0] as $key => $value)
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
<select name="fields[{{ $key }}]">
#foreach ($db_field as $field)
<option value="{{ (\Request::has('header')) ? $field : $loop->index }}"
#if ($key === $field) selected #endif>{{ $field }}</option>
#endforeach
</select>
</td>
#endforeach
</tr>
</tbody>
</table>
And the controller functions
class ImportController extends Controller
{
public function parseImport(CsvImportRequest $request)
{
if ($request->has('header')) {
$headings = (new HeadingRowImport)->toArray($request->file('csv_file'));
$data = Excel::toArray(new ContactsImport, $request->file('csv_file'))[0];
} else {
$data = array_map('str_getcsv', file($request->file('csv_file')->getRealPath()));
}
if (count($data) > 0) {
$csv_data = array_slice($data, 0, 2);
$csv_data_file = CsvData::create([
'csv_filename' => $request->file('csv_file')->getClientOriginalName(),
'csv_header' => $request->has('header'),
'csv_data' => json_encode($data)
]);
} else {
return redirect()->back();
}
$contact = new Contact;
$table = $contact->getTable();
$db_field = \Schema::getColumnListing($table);
// dd($db_field);
return view('import_fields', [
'headings' => $headings ?? null,
'csv_data' => $csv_data,
'csv_data_file' => $csv_data_file,
'db_field' => $db_field
]);
}
public function processImport(Request $request)
public function processImport(Request $request)
{
// dd($request->fields);
$data = CsvData::find($request->csv_data_file_id);
$csv_data = json_decode($data->csv_data, true);
foreach ($csv_data as $key => $row) {
$contact = new Contact();
$table = $contact->getTable();
$db_field = \Schema::getColumnListing($table);
foreach ($db_field as $index => $field) {
if ($data->csv_header) {
if (array_key_exists($field, $request->fields)) {
$csv_field = $request->fields[$field];
$contact->$field = array_values(array($row[$csv_field]));
}
} else {
if (array_key_exists($index, $request->fields)) {
$csv_field = $request->fields[$index];
$contact->$field = $row[$csv_field];
}
}
}
$contact->save();
}
return redirect()->route('contacts.index')->with('success', 'Import finished.');
}
dd($csv_field); print me this
array:5 [▼
"id" => "id"
"first_name" => "first_name"
"last_name" => "last_name"
"email" => "email"
"phone" => "phone_number"
]
On the first step parseImport i get CSV header and data from import also i get db.table header name (field), and display this data on second step where i need to match the field. But when i match manually a field like screenshot example phone -> phone_number is not working and i get Undefined index: phone_number error

Related

vuetify v-radio doesn't select one radio in table nuxt js 3

I try to make a multiple grid radio with conditions: if one radio button is selected, save it as json with key: question and value: column where the radio button is selected.
<template>
<v-radio-group class="mt-0"
v-model="answer"
:rules="answerRule"
>
<thead>
<tr>
<th>Pilihan</th>
<th v-for="(option, keys) in columns" :key="keys + 'A'">
{{ option.value }}
</th>
</tr>
<tr v-for="(option, keys) in rows" :key="keys + 'A'">
<th>
{{ option.value }}
</th>
<td v-for="(option, key) in columns" :key="key + 'B'">
<v-radio-group
v-model="answer"
#change="update"
:rules="answerRule"
>
<v-radio
solo
:key="key"
:value="option.value"
>
</v-radio>
</v-radio-group>
</td>
</tr>
</thead>
<tbody></tbody>
</v-radio-group>
</template>
Here is my script on how to load the data and try to save the json:
<script>
export default {
props: ['question'],
data() {
return {
rows: this.question.options,
answer: [],
columns: this.question.optionsColumns,
answerRule: [],
}
},
methods: {
async update() {
try {
let payload = {
questionId: this.question.id,
value: this.answer,
questionName: this.question.question,
}
//update question options
await this.$store.commit('answers/update', payload)
} catch (err) {
this.$store.commit('alerts/show', {
type: 'error',
message: err.response
? this.$t(err.response.data.message)
: this.$t('SERVER_ERROR'),
})
}
},
},
beforeMount() {
if (this.question.required) {
this.answerRule.push(
(v) => v.length > 0 || this.$t('QUESTION_REQUIRED')
)
}
},
}
</script>

How to show data with the async command in the template?

I have an Angular 8 application. And I am using the asynccommand for passing data from child to parent component. But the data is returning by the server but is not been displayed by the HTML template.
This is my typescript:
correspondenceEntries$: Observable<DossierEntry[]>;
attachmentEntries$: Observable<DossierEntry[]>;
#Input() allCorrespondence: Array<DossierEntry>;
#Input() correspondenceEntries: Array<DossierEntry>;
#Input() attachmentEntries: Array<DossierEntry>;
message = '';
emptyMessageCorrespondentie = 'Geen correspondentie.';
errorMessageConnection = 'Er ging iets mis met de connectie. Probeer over enkele minuten nogmaals.';
correspondenceLoaded = false;
showingSingle = false;
single: DossierEntry;
constructor(private documentCorrespondeceService: DocumentCorrespondenceService, private authService: AuthService) {}
ngOnInit() {
this.authService.loginStatus().subscribe(user => {
const UUID = user.profile.participant;
this.documentCorrespondeceService.getDossierEntry(UUID, 'correspondence').subscribe(result => {
this.handleCorrespondenceLoad(result), (this.correspondenceLoaded = true);
}, msg => (this.message = this.emptyMessageCorrespondentie));
});
}
handleCorrespondenceLoad(result: any) {
if (result.length === 0) {
this.message = this.emptyMessageCorrespondentie;
return;
}
this.allCorrespondence = result;
this.attachmentEntries = [];
this.correspondenceEntries = [];
const groups = _.groupBy(result, 'type');
console.log(this.correspondenceEntries = groups.correspondence);
console.log(this.attachmentEntries = groups.attachments);
this.correspondenceEntries = groups.correspondence;
this.attachmentEntries = groups.attachments;
}
And here is the HTML template:
<h2 class="dossier-page-header">Correspondentie</h2>
<p class="data-entry" *ngIf="!allCorrespondence">{{ message }}</p>
<ng-container *ngIf="(correspondenceEntries$ | async) as correspondenceEntries">
<app-dossier-correspondence-list [correspondenceEntries]="correspondenceEntries" ></app-dossier-correspondence-list>
</ng-container>
<ng-container *ngIf="(attachmentEntries$ | async) as attachmentEntries">
<app-dossier-correspondence-attachments [attachmentEntries] = "attachmentEntries"></app-dossier-correspondence-attachments>
</ng-container>
<app-dossier-correspondence-item
[item]="single"
(goBack)="goBack($event)"
*ngIf="showingSingle">
</app-dossier-correspondence-item>
So when I do this:
console.log(this.correspondenceEntries = groups.correspondence);
console.log(this.attachmentEntries = groups.attachments);
I see the data:
Array(13)
:4200/dossier-dossier-module-ngfactory.js:17533 Array(3)
But it's not displayed in the view.
So how to improve this, that the data will also be showing in the view.
Thank you.
I understand.
But How to do this then:
this.attachmentEntries$ = this.documentCorrespondeceService.getDossierEntry(UUID, 'correspondence').subscribe(result => {
this.handleCorrespondenceLoad(result), (this.correspondenceLoaded = true);
}, msg => (this.message = this.emptyMessageCorrespondentie));
});
Of course this doenst work.So how to use the Observables:
correspondenceEntries$: Observable<DossierEntry[]>;
attachmentEntries$: Observable<DossierEntry[]>;
?
Because there are two arrays.
So you mean like this:
<app-dossier-correspondence-list *ngFor="let item of correspondenceEntries" ></app-dossier-correspondence-list>
and this:
ngOnInit() {
this.authService.loginStatus().subscribe(user => {
const UUID = user.profile.participant;
this.documentCorrespondeceService.getDossierEntry(UUID, 'correspondence').subscribe(result => {
this.handleCorrespondenceLoad(result), (this.correspondenceLoaded = true);
}, msg => (this.message = this.emptyMessageCorrespondentie));
});
}
But the
child component looks already like this:
<div *ngIf="!showingSingle && correspondenceEntries && correspondenceEntries.length > 0;">
<div class="main-row main-row-dossier">
<section class="data-entry">
<h3 class="dossier-header">Algemeen</h3>
<table class="dossier-table" *ngIf="correspondenceEntries else loadingCorrespondenceEntires ">
<thead class="dossier-tableheader">
<tr>
<th class="dossier-tablehead fixed-one-fifth">Datum</th>
<th class="dossier-tablehead fixed-four-fifths">Onderwerp</th>
</tr>
</thead>
<tbody class="dossier-tablebody">
<tr class="dossier-correspondencerow" *ngFor="let entry of correspondenceEntries; let i = index" (click)="gotoItem(i, entry.type)">
<td>{{ entry.date | date:"dd-MM-y" }}</td>
<td>{{ entry.name }}</td>
</tr>
</tbody>
</table>
</section>
</div>
</div>
<ng-template #loadingCorrespondenceEntires>
<div>..Loading </div>
</ng-template>
You are mixing 2 ways to subscribe to an Observable. Choose one, don't do both.
Solution 1:
// component
this.myService.getDataFromServer().subscribe(
result => this.data = result
);
<!-- HTML -->
<div *ngFor="let item of data">
<span>{{item.label}}</span>
</div>
Solution 2:
// component
this.data$ = this.myService.getDataFromServer()
<!-- HTML -->
<div *ngIf="(data$ | async) as data">
<span>{{data.label}}</span>
</div>

VueJS not updating view after using $set

I'm trying to update a list of products with an editable quantity, which updates and changes a row-wise total of product prices. Please see my code below -
<template>
<div>
<product-search-bar :product_search_route="product_search_route" />
<table class="table table-hover table-responsive table-striped">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Qty.</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr v-for="(product, index) in product_list">
<td>
{{ index + 1 }}
<input type="hidden" :name="'order_items[' + index + '][id]'" :value="product.id" />
</td>
<td>{{ product.name }}</td>
<td>
<input type="number" :name="'order_items[' + index + '][qty]'" #change="product_quantity_changed($event, product)" />
</td>
<td>{{ product.purchase_currency }} {{ product.total_price }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
name: 'form-purchase-order-items',
props: [
'product_search_route',
],
data() {
return {
product_list: []
};
},
mounted() {
},
methods: {
/**
*
* #param product
*/
product_added(product)
{
product.quantity = 1;
product.total_price = product.supplier.purchase_price;
if (!this.product_list.find((v) => { return v.id == product.id }))
this.product_list.push(product);
},
/**
*
* #param product
*/
product_quantity_changed(e, product)
{
var quantity = Number(e.target.value);
this.$set(product, 'quantity', quantity);
this.$set(product, 'total_price', (quantity * product.supplier.purchase_price));
}
},
watch: {
}
}
</script>
The price total does update correctly, seen through Vue DevTools, however, the column <td>{{ product.purchase_currency }} {{ product.total_price }}</td> doesn't reflect the changes made. I've read the documentation and I think this is something that isn't mentioned there.
Edit:
The two members quantity and total_price are being created after the object is received in the product_added(product) callback. This probably makes them non-reactive members of the object.
try #input instead of #change in following html code:
<input type="number" :name="'order_items[' + index + '][qty]'" #change="product_quantity_changed($event, product)" />
I was able to fix this by making quantity and total_price reactive members.
product_added(product)
{
this.$set(product, 'quantity', 1);
this.$set(product, 'total_purchase_price', product.supplier.purchase_price);
if (!this.product_list.find((v) => { return v.id == product.id }))
this.product_list.push(product);
},

Anuglar2- model data jumps

I'm making a Anuglar2 application for people to log how many hours they put into each course, assignments, etc., per week (though there we be more advance options later on).
Right now i have a table which lists out how many hours you spent on each course per day. I want the user to be able to just edit and change any values as he/she goes along. So i have a two dimensional array ( named data), and i attach models to each element in the array, which i then represent as a input element.
Everything works fine, but there is a weird bug. Whenever you delete the value in the input box and re-enter new data, it jumps to the next input element. I cant figure out why. Anyone got any ideas ?
Example in GIF format (sorry for the quality had to use a converter)
gif link in case you cant see it on Stack
home.html
<!-- Table -->
<div class="row">
<div class="col-lg-12 col-sm-12">
<h1 class="page-header">Weekly Status Report</h1>
<table class="table table-responsive table-stripped table-bordered table-hover" *ngIf="courses">
<thead>
<tr class="btn-primary">
<th>Course</th>
<th>Time</th>
<th>SM #</th>
<th>Est</th>
<th *ngFor="let weekday of week">
{{weekday | date:'EEEE'}}
</th>
<th>Total</th>
</tr>
</thead>
<tbody *ngFor="let course of courses; let i = index;">
<tr>
<td >
{{course.name}}
</td>
</tr>
<tr class="alert-info">
<!-- Account for the title row -->
<td>
Date
</td>
<td >
Documentation Type
</td>
<td>
Documentation Text
</td>
</tr>
<tr *ngFor="let content of course.hours" class="alert-warning">
<td>
{{content.day| date}}
</td>
<td [attr.colspan]="3">
{{ title }}
</td>
<td [attr.colspan]="7">
</td>
</tr>
<tr class="alert-success">
<td></td>
<td></td>
<td></td>
<th></th>
<!-- DATA ARRAY -->
<th *ngFor="let d of data[i]; let j = index;">
<div>
<input maxlength="3" size="3" [(ngModel)]="data[i][j]" />
</div>
</th>
<td></td>
</tr>
</tbody>
<tfoot class="btn-primary">
<tr>
<td>Report Totals(all courses)</td>
<td></td>
<td></td>
<td></td>
<td *ngFor="let pivot of [0,1,2,3,4,5,6]">
{{ getSum(pivot) }}
</td>
<td>
{{getTotal()}}
</td>
</tr>
</tfoot>
</table>
</div>
</div>
home.component.ts
week: Array<any> = new Array();
data: Array<any> = new Array();
constructor(private hm: HomeService) {
let subscription = this.hm.getFormat(this.courses, this.week)
.subscribe(
value => {
this.data.push(value);
},
error => {
console.log(error)
},
() => {
console.log("Formated");
}
);
}
home.service.ts
getFormat(courses: any, weekdays: any): Observable<any> {
return new Observable(observer => {
// For all courses
courses.forEach(c => {
// temp week
let week: Array<any> = new Array();
// For each weekday
weekdays.forEach(w => {
let found: boolean = false;
// get hours spent on course
c.hours.forEach(h => {
let hour:Date = new Date (h.day);
// if the hours spent match up on the day push it to the week array
if (w.day.getTime() === hour.getTime()) {
week.push(h.duration);
found = true
}
});
// If no time was found on this take, push a empty string.
if (!found) {
week.push(0);
}
});
// push that week to the component
observer.next(week);
});
observer.complete();
});
}
This is happening because of how data is tracked.
When you change the value from the array it will become another value, thus not being able to track it as it will keep track of the older value.
Your case is similar to the following bad code:
#Component({
selector: 'my-app',
template: `
<div>
<table>
<tr *ngFor="let dim1 of values; let dim1Index = index">
<td *ngFor="let dim2 of dim1; let dim2Index = index">
<input type="text" [(ngModel)]="values[dim1Index][dim2Index]" />
</td>
</tr>
</table>
</div>
`,
})
export class App {
values: string[][];
constructor() {
this.values = [
['a1', 'b1', 'c1'],
['a2', 'b2', 'c2'],
['a3', 'b3', 'c3']
];
}
}
There are multiple solutions:
Solution 1: Using an object to wrap the value
Look at the values array.
#Component({
selector: 'my-app',
template: `
<div>
<table>
<tr *ngFor="let dim1 of values; let dim1Index = index">
<td *ngFor="let dim2 of dim1; let dim2Index = index">
<input type="text" [(ngModel)]="values[dim1Index][dim2Index].value" />
</td>
</tr>
</table>
<br/>
{{ values | json }}
</div>
`,
})
export class App {
values: string[][];
constructor() {
this.values = [
[{ value: 'a1' }, { value: 'b1' }, { value: 'c1' }],
[{ value: 'a2' }, { value: 'b2' }, { value: 'c2' }],
[{ value: 'a3' }, { value: 'b3' }, { value: 'c3' }]
];
}
}
Solution 2: Using a custom trackBy function for *ngFor
Note that this solution is based on trackByIndex function which returns the index of the item, thus the item being located by its index instead of its value.
#Component({
selector: 'my-app',
template: `
<div>
<table>
<tr *ngFor="let dim1 of values; let dim1Index = index; trackBy: trackByIndex">
<td *ngFor="let dim2 of dim1; let dim2Index = index; trackBy: trackByIndex">
<input type="text" [(ngModel)]="values[dim1Index][dim2Index]" />
</td>
</tr>
</table>
<br/>
{{ values | json }}
</div>
`,
})
export class App {
values: string[][];
constructor() {
this.values = [
['a1', 'b1', 'c1'],
['a2', 'b2', 'c2'],
['a3', 'b3', 'c3']
];
}
public trackByIndex(index: number, item) {
return index;
}
}
Therefore in your code you can use:
<tbody *ngFor="let course of courses; let i = index; trackBy: trackByIndex">
next
<th *ngFor="let d of data[i]; let j = index; trackBy: trackByIndex">
and finally define trackByIndex in your component:
public trackByIndex(index: number, item) {
return index;
}

Cannot update template in angular 2 when data is changed

I have a form to insert data to a table. When i delete data from table , the data is removed but the table row is not deleted.
It appears that the data is two way binding so data is removed but the html structure remains same.
Component
export class HomeComponent implements OnInit {
studentform = new FormGroup({
id: new FormControl(),
name: new FormControl(),
address: new FormControl()
});
student: Student[]=[];
std: Student= new Student();
constructor(public homeService: HomeService){ }
OnInit(){
this.getData();
}
getData(){
this.student = this.homeService.GetData();
}
onEdit(id:number){
console.log("Edit:" + id);
}
onDelete(id:number){
this.homeService.delete(id);
this.getData();
}
Save(model:Student){
this.homeService.SaveData(model);
this.studentform.reset();
this.getData();
}
}
Service
#Injectable()
export class HomeService{
student:Student[]=[];
SaveData(model:Student){
this.student.push(model);
}
GetData(){
return this.student;
}
delete(id:number){
for(var i=0;i<this.student.length;i++){
if(this.student[i].id==id){
delete this.student[i]
}
}
}
}
Template
div class="col-md-6">
<h5> Lists </h5>
<table>
<th>ID </th>
<th>Name </th>
<th>Address </th>
<th>Edit </th>
<th>Delete </th>
<tr *ngFor="let x of student">
<td> {{ x.id }} </td>
<td> {{ x.name }} </td>
<td> {{ x.address }} </td>
<td (click)="onEdit(x.id)"> Edit </td>
<td (click)="onDelete(x.id)"> Delete </td>
</tr>
</table>
Help me update the html (template) when data changes.
This is the result after I click table : data is gone but row remains
You are actually deleting the object but it's reference remain in the primary array. Try this instead :
delete(id:number){
for(var i=0;i<this.student.length;i++){
if(this.student[i].id==id){
this.student.splice(i, 1); //delete this.student[i]
break;
}
}
}
delete this.student[i] is not the correct way to remove an element from an array in this situation. You need to use splice().
this.student.splice(i, 1);
Also you should do a truthy check when displaying object fields in the template. Otherwise you will get errors like that. Usually safe-navigation operator(?) will do the trick.
Example:
<td> {{ x?.id }} </td>

Categories

Resources