Rowspan in Nested Foreach Table in Laravel DOMPDF - php

I tried to generate PDF using barryvdh/laravel-dompdf in Laravel 8. My table is using rowspan. I could generate the PDF successfully, but it's not build the rowspan correctly on the page break like this. This only happened on the last two page. I already tried to setting my CSS and style, used page-break-after:always; , and $loop->iteration but it's still didn't work. This is my code:
View:
<tbody>
#foreach($sasarans as $i => $sasaran)
#foreach($sasaran->indikator as $indikator)
#foreach($indikator->pertanyaan as $pertanyaan)
<tr style="margin-top:-20px; margin-bottom:-25px;">
//Some data here
#foreach($pertanyaan->data_laporan as $data)
//Some data
#if($loop->iteration % 15 == 0)
<div class="page-break"></div>
#endif
#endforeach
</tr>
#endforeach
#endforeach
#endforeach
</tbody>
CSS:
.page-break {
display:block;
page-break-after: always;
}
Please help me because I'm still a beginner. Thank you for your help.

Related

AlpineJS x-for templates

I have a problem with AlpineJS in my complicated app, and I'm finding it very difficult to replicate under a simplified example. This may well mean that it's a bug in Alpine, but I'll ask here for help on the off-chance anyway. I've tried to reduce the code below to only the bare essentials that are necessary to explain the problem, and doing so may have lead to some typos. Therefore please excuse me in advance for any errors that are not related to the problem itself.
I'm using Livewire to synch data between my PHP classes and my AlpineJS front-end. The two variables that are relevant in the PHP class are:
public $colOrder; // users are able to "re-order" columns on their table-view. This preference is saved into their profile and stored in this variable as a 1D array of the column-IDs
public $datasourceData; // contains a 2D data that is pulled from a database with: Model->get()->toArray(); [0 => ['col1'=>'data1,1', 'col2'=>'data1,2'], 1 => ['col1'=>'data2,1', 'col2'=>'data2,2']];
These arrays are then entangled with Alpine variables, and the template is generated from those arrays of data as follows. Ostensibly, this template works fine:
<div x-data="{
eColOrder: #entangle('colOrder').defer,
eData: #entangle('datasourceData').defer
}">
<table class="table" x-cloak>
<thead>
<tr>
<template x-for="(col, ix) in eColOrder" :key="'th-'+ix">
<th x-text="col"></th>
</template>
</tr>
</thead>
<tbody>
<template x-if="eData.length==0">
<tr>
<td :colspan="eColOrder.length" style="padding: 1em">No data found</td>
</tr>
</template>
<template x-if="eData.length>0">
<template x-for="(rec, ix) in eData" :key="'row-'+ix">
<tr>
<td class="action"></td>
<template x-for="(col, pos) in eColOrder" :key="'td-'+ix+'-'+pos">
<td x-text="rec[col]"></td> <!-- I also tried `eData[ix][col]`, but it produced errors in the browser console, even though the on-screen display was fine -->
</template>
</tr>
</template>
</template>
</tbody>
</table>
In this screen-shot, you can see that the user's search (in the top-row) has produced a tabulated grid of data below. Happy days.
The problem arises when the user re-submits a different search. They do so by updating the search fields, and pressing the "search" button again. This re-submits the search (through a Livewire JSON call), refreshing the $datasourceData array with new data, entangling itself with the eData variable in Alpine, and producing the following result:
What appears to be happening, is that the results of the new search are correctly pulled through. But for whatever reason, Alpine hasn't cleared the screen from the last set of search-results. It's interesting that only the data-level of the HTML table is corrupted (that is to say, the <td> cells). Note that the <th> cells have (correctly) not been duplicated above the right-hand-half of the new table.
I've debugged and checked that the data returned from the Eloquent models is correct, and that the structure of the data in the entangled JavaScript variable eData is also correct. This problem is not data-related, it's the rendering that's at fault.
My gut-feel is that this is an Alpine bug, but I haven't been able to prove it yet.
My problem stops there. However, in an attempt to replicate the issue and narrow down the cause of the issue, what I've done is to create a simplified Livewire/Blade/Alpine page. Strictly speaking, I wasn't able to replicate the problem there either directly, but I did (accidentally) manage to replicate a similar output when I entered a deliberate "bug" into my code.
Take the following PHP/Livewire component:
<?php
namespace App\Business\Tbd;
use Livewire\Component;
class StartLw extends Component
{
public array $data = [];
public array $headings = [];
public int $count = 0;
public function mount() {
for ($i=1; $i <= 6; $i++) {
$this->headings[] = "col{$i}";
}
$this->data = [];
}
public function formSubmit() {
$src = 1;
$this->data = [];
for ($i=0; $i < 10; $i++) {
$this->data[$i] = [];
for ($y=1; $y <= 6; $y++) {
$this->data[$i]["col{$y}"] = "source {$src} ({$i},{$y})";
}
}
$this->count++;
}
public function relatedToButSeparateFromForm() {
$src = 2;
$this->data = [];
for ($i=0; $i < 4; $i++) {
$this->data[$i] = [];
for ($y=1; $y <= 6; $y++) {
$this->data[$i]["col{$y}"] = "source {$src} ({$i},{$y})";
}
}
$this->count++;
}
public function render()
{
return view('components.tbd.lw-start-lw')
->layout('layouts.tbd.lw');
}
}
And this cut-down HTML to render the page:
<div class="container" x-data="{
eData: #entangle('data').defer,
eHeadings: #entangle('headings').defer
}">
<div class="row">
<div class="col"><p>{{ $count }}</p></div>
</div>
<div class="row">
<div class="col">
<form method="post" wire:submit.prevent="formSubmit">
<p>
<button type="submit">Load data source 1</button>
<button type="button" wire:click="relatedToButSeparateFromForm">Load data source 2</button>
</p>
</form>
</div>
</div>
<div class="row">
<div class="col">
<table>
<thead>
<tr>
<template x-for="hd in eHeadings">
<th x-text="hd" style="padding: 0.5em; background-color:rgb(220,220,230); border: 1px solid rgb(210,210,230)"></th>
</template>
</tr>
</thead>
<tbody>
<template x-for="(row, ix) in eData" :key="ix">
<tr>
<template x-for="(col, pos) in eHeadings" :key="'td-'+ix+'-'+pos">
<td x-text="row[col]" :class="id" style="padding: 0.5em; background-color:rgb(240,240,255); border: 1px solid rgb(210,210,230)"></td>
</template>
</tr>
</template>
</tbody>
</table>
</div>
</div>
</div>
Note the deliberate error! On the <td> element, :class="id" should really say :class="col". Now if I take out the error, the page works as I would expect it to. But with the error re-introduced into the code (together with a bunch of error messages in the browser console saying: Uncaught ReferenceError: id is not defined), after toggling the two buttons back and forth a bit, I get this:
As I think you'll agree, that picture is spookily reminiscent of the situation I get in my real world application (except that in the real-world-app, I don't end up with any errors in the browser's console).
This leads me to the strong belief that there is a silent bug being triggered somewhere in the Alpine engine which triggers the same net result. I will go and log this on their GitHub support pages too, but I have always found the Stack community to be super useful in the past too. I hope that someone out there is able to help validate that I'm not missing anything obvious!
Posted the issue on the Alpine bug-report pages, and got the response I wanted. See >> https://github.com/alpinejs/alpine/discussions/2523#discussioncomment-1860670
Apparently, it's not an Alpine issue at all. The problem is that Livewire is treading on Alpine's toes. Livewire "watches" the DOM for updates, and it seems that it's then failing to release (or clean-up, or whatever the correct term is) certain subsections of the DOM as Alpine refreshes it with the new load of data. This explains why earlier incarnations of the DOM are hanging about for longer than they are required.
Resolution is to force Livewire to not-watch the DOM for differences by using the wire:ignore directive. This can be put on the <table> itself, or any parent element thereof. In my example I put it on the immediately encapsulating <div>:
<div class="whoopsie" wire:ignore>
<table>
<!-- etc -->
<tbody>
<template x-for="(col, pos) in eColOrder" :key="'td-'+ix+'-'+pos">
<td x-data="row[col]"></td>
</template>
</tbody>
<!-- etc -->
</table>
</div>

Alternating rows background depending a changing value in Laravel

Here is a blade template of my table, where I have issues changing rows depending on a value. If the command number changes I want the row and all the following rows with that command number to change and then alternate between white/grey colors everytime it's a different one.
I know it looks pretty basic but I can't make the condition in my algorithm. My first if looks if the previous iteration has a different command number and change the class, but I'm missing the one checking if the previous iteration has the same command number so keep the same background. I don't know how to check something css class and alternate the 2 backgrounds white/grey. I tried with $attributes from Laravel but it is always undefined.
I hope I was clear with my issue, also the table is already sorted by increasing command_number.
If you need the controller I can provide it.
Thanks
tr.second th, tr.second td {
background-color: #D1D1D1;
}
//////
<tbody>
#foreach($data as $key => $line)
#if($key > 0)
<tr #if ($data[$key]['command_number'] != $data[$key-1]['command_number']) class="second"
#elseif()
#endif>
#foreach($line as $item)
#if(($item == $line['quantity']) || ($item == $line['delivery']))
<td>
<strong>{{$item}}</strong>
</td>
#elseif($item == $line['hours'])
<td>
<strong style="color: red;">{{$item}}</strong>
</td>
#else
<td>
{{$item}}
</td>
#endif
#endforeach
</tr>
#endif
#endforeach
</tbody>
If you want to change the class based on a previous result, why don't you track the css class to use with a toggle variable outside of the loop like so:
// in your blade file
#php
$toggle = false;
#endphp
#foreach(range(0, 10) as $number)
<div class="#if($toggle) someClass #else otherClass #endif unrelatedClasses">
</div>
#php
$toggle = !$toggle
#endphp
#endforeach
Alternatively, the toggle should function in your case:
// You could change the value of toggle based on your use case, like:
#php
$toggle = ($data[$key]['command_number'] != $data[$key-1]['command_number']);
#endphp
Instead of using $key, you can also use $loop->index to get the current iteration.
Loop variable for even/odd cases
For other use cases, the loop variable has several properties and methods you can use (like even, odd). Check out the Blade documentation.
CSS selector for even/odd cases
There are also CSS solutions using selectors like: nth-child(even) and nth-child(odd) for even and odd numbers respectively.
<tbody>
#php($currentKey = null)
#foreach($data as $key => $line)
#if($key > 0)
<tr #if ($data[$key]['command_number'] != $currentKey) class="second"
#elseif()
#endif>
#foreach($line as $item)
#if(($item == $line['quantity']) || ($item == $line['delivery']))
<td>
<strong>{{$item}}</strong>
</td>
#elseif($item == $line['hours'])
<td>
<strong style="color: red;">{{$item}}</strong>
</td>
#else
<td>
{{$item}}
</td>
#endif
#endforeach
</tr>
#endif
#php($currentKey = $data[$key]['command_number'])
#endforeach
</tbody>

Laravels foreach output is not complete

I wrote an eloquent statement in my model that outputs a whole lot of data which is fully modelled for my output. It returns the right content when I dump it in the backend (if you look at the comment below).
But when I try to get it formatted with blade for my table, it cuts a whole lot of data at the top. It starts somewhere at the mid of all.
I tried a lot of styling like put some standard CSS from bootstrap and nothing at all, because I thought that this is a display error, but it seems not.
Route:
Route::get('/', function () {
$hs = App\Wl_steige::all()->first();
$hs = $hs->getByStation('ptMetro')->groupBy('BEZEICHNUNG');
//dump($hs = $hs->getByStation('ptMetro')->groupBy('BEZEICHNUNG'))
return view('welcome', ['stellen' => collect($hs)]);
});
View:
<div class="card">
<div class="card-body">
<table class="table table-dark">
<tbody>
#foreach($stellen AS $line => $stations)
<tr>
<th>Linie {{ $line }}</th>
</tr>
#foreach($stations AS $station)
<tr>
<td>{{ $station->NAME }}</td>
<td>{{ $station->VERKEHRSMITTEL }}</td>
<td>{{ $station->RBL_NUMMER }}</td>
<td>{{ $station->STEIG_WGS84_LAT }}</td>
<td>{{ $station->STEIG_WGS84_LON }}</td>
</tr>
#endforeach
#endforeach
</tbody>
</table>
</div>
</div>
I expected that it would begin at the top and not somewhere in the middle.
There was a "hidden" text-align: center; from the former Laravel template which - I dont know why but made my text float out of the screen.
I didnt saw it at first because it was somewhere Cached.
After I cleared at first hand Laravels cache files php artisan cache:clear and then Cmd + R for Chrome, it worked fine for me.

Changing background color not working when a condition is checked inside table row

Here i have a 'work' array (generating from excel file) which has values employee_id(here id),projects ands hours.I also have another array 'employees' which has all the employees in my database. Here i need to check if the employee exist in my database by comparing with the ids. If employee exists, i need to change the background color of the table row.I tried this method but not working, please help .
#foreach($val['work'] as $k3=> $val3)
#if(isset($val3['hours']) && isset($val3['projects']))
<tr #foreach($employees as $employee) #if($employee->emp_id ==$val3['id'][1]) style="background-color:#ffe1df;" #endif #endforeach>
<td>{{$val3['id'][1]}}</td>
<td>{{$val3['name'][2]}}</td>
</tr>
#endif
#endforeach
Try setting a flag value :
#foreach($val['work'] as $k3=> $val3)
#if(isset($val3['hours']) && isset($val3['projects']))
#php $flag=0; #endphp
#foreach($employees as $employee) #if($employee->emp_id == $val3['id'][1]) #php $flag=1 #endphp #endif #endforeach
<tr #if($flag==0) style="background-color:#ffe1df;" #endif>
<td>{{$val3['id'][1]}}</td>
<td>{{$val3['name'][2]}}</td>
</tr>
#endif
#endforeach

Laravel,blade, vue.js and #if (#{{ }})

I am having a problem with variables devielve vue.js me ... I explain better
<tr
#if(#{{users.id}} != 1) // this is the error
Class="danger"
#else
class="success"
#endif
>
I can not define a #if (# {{user.id in laravel
before defined
#foreach($datos as $dato)
<tbody id={{$dato->id}}>
<tr
#if($dato->id !=1)
class="danger"
#else
Class="success"
#endif >
and it worked but I had to put a select box, and Use varaible vue.js to collect and return the data I...
excuse my English is not my mother tongue and hinders me
I do not believe you can use vuejs code (such as your #{{users.id}}) inside a laravel #if() because everything inside the #if() is being treated as PHP code. If it makes sense in your particular application, make use of vuejs's v-if implementation, such as <div v-if="users.id !== 1"> INSERT WHATEVER HERE </div>

Categories