AlpineJS x-for templates - php

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>

Related

How to acces class attributes from another file in PHP

I want to access class attributes from another of my files but I don't know how.
I am creating a class whose goal is to generate a CRUD table with the data passed in the parameters (table name, ID and table fields).
I have created the class in the file main.php, but I am also using an external file called res.php, this file will be in charge of making the calls to the database and building the table.
Once the class is finished and ready to be used, its function will be that in any file of all of my projects. If I instantiate an object of that class, a CRUD table will be created using the following:
$test = new GenerateCrud('users_test', 'id', ['usuario', 'apellido1', 'apellido2', 'email']);
$test->showTable();
My problem: To create the table, I have to access the attributes of the class, since I need to print the table name, fields, etc.
What happens to me? I don't know how to do it.
My class file main.php:
<?php
class GenerateCrud {
// Properties.
public $tableName;
public $id;
public $tableFields = array();
// Constructor.
function __construct($tableName, $id, $tableFields){
$this->tableName = $tableName;
$this->id = $id;
$this->tableFields = $tableFields;
}
}
?>
My external file res.php:
<?php
include_once('main.php');
?>
<div class="container caja">
<div class="row">
<div class="col-lg-12 col-sm-12">
<div>
<table id="tablaUsuarios" class="table table-striped table-bordered table-condensed" style="width:100%" >
<thead class="text-center">
<tr>
<!-- CREATE FOR EACH TO SHOW THE TABLE FIELDS -->
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function() {
var tableName = "<?php echo $this->tableName; ?>"; // HEREEEEEEEEEEEEEEEEEEE
console.log(tableName);
});
</script>
<?php
}
?>
I am aware that the variable $this does not point to anything, since I have not instantiated an object, but, then, is it as easy as instantiating an object and that's it? I won't get the information wrongly altered or anything like that? Won't it cause me problems later when I instantiate the object again?
I know I'm not describing the problem very well and I'm aware that I lack knowledge, that's obvious, that's why I'm here asking to try to learn.
If someone can explain to me the reason for their explanation to get to understand this, I would appreciate it very much.
Thank you and have a nice day.
in order to access property tableName you have to initialize GenerateCrud object first. Nor in main.php nor in res.php I don't see object initialization.
You said that to generate CRUD table you use
$test = new GenerateCrud('users_test', 'id', ['usuario', 'apellido1', 'apellido2', 'email']);
$test->showTable();
but where it is added in your code?
is it in main.php or res.php
I will suggest to you that you will add this peace of code to the top part of your main.php file and then you will be able to get tableName property
something like this
<?php
include_once('main.php');
$test = new GenerateCrud('users_test', 'id', ['usuario', 'apellido1', 'apellido2', 'email']);
?>
<div class="container caja">
<div class="row">
<div class="col-lg-12 col-sm-12">
<div>
<table id="tablaUsuarios" class="table table-striped table-bordered table-condensed" style="width:100%" >
<thead class="text-center">
<tr>
<!-- CREATE FOR EACH TO SHOW THE TABLE FIELDS -->
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function() {
var tableName = "<?= $test->tableName; ?>"; // HEREEEEEEEEEEEEEEEEEEE
console.log(tableName);
});
</script>
Assuming this code:
$test = new GenerateCrud('users_test', 'id', ['usuario', 'apellido1', 'apellido2', 'email']);
$test->showTable();
is in your res.php file (or in a file which is included from it), then try
echo $test->tableName;
$test is the name of the object you instantiated. $this is only used inside the class, to allow it to self-reference.

Laravel "No query results for model [App\Schedule]" error when not calling model

I have a perplexing issue that I cannot seem to figure out. I am trying to load a webpage from a button, but it keeps poping up the error No query results for model [App\Schedule]. I have no idea why it's doing that, as there is nowhere I am trying to access that model when trying to load the webpage. Below is what I have so far (it's a work in progress)
Here is the link:
<a class="btn btn-primary" role="button" href="/schedule/availability">Set Availability</a>
Here is the Route:
Route::get('/schedule/availability', 'ScheduleController#getAvailabilityCalendar');
Here is the method inside of the ScheduleController:
public function getAvailabilityCalendar(){
$dt = Carbon::now('America/Los_Angeles');
$return['DayOfWeek'] = $dt->dayOfWeek;
$return['DisplayDays'] = (($dt->daysInMonth + $return['DayOfWeek'] + 1) <= 35) ? 34:41;
return view('contractors.availability', compact('return'));
}
And here is the view that is returned:
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Sunday </th>
<th>Monday </th>
<th>Tuesday </th>
<th>Wednesday </th>
<th>Thursday </th>
<th>Friday </th>
<th>Saturday </th>
</tr>
</thead>
<tbody>
<tr>
#for ($i = 0, $j=-$return['DayOfWeek']; $i <= $return['DisplayDays']; $i++, $j++)
#if($i < $return['DayOfWeek'])
<td></td>
#else
#if($i %7 == 0)
</tr><tr>
#endif
#if($j < 30)
<td>{{\Carbon\Carbon::now('America/Los_Angeles')->addDays($i-$return['DayOfWeek']+1)->format('F jS')}}
<hr>
<div class="form-group">
<label data-toggle="popover" data-trigger="hover" data-content="Add Times available in the following format: 10:00am-4:00pm. You may also add multiple blocks of time separated by comma. Example: 10:00am-12:00pm, 2:00pm-4:00pm">Available Time</label>
<input type="text" class="form-control availability" />
</div>
<div class="checkbox">
<label>
<input type="checkbox" class="all-day-check" />All Day</label>
</div>
</td>
#else
<td></td>
#endif
#endif
#endfor
</tr>
</tbody>
</table>
</div>
<script>
$(document).on('change', '.all-day-check', function(){
if (this.checked) {
$(this).closest("input.availability").prop("disabled", true);
}else{
$(this).closest("input.availability").prop("disabled", false);
}
});
</script>
I can't find anywhere that would need a reference to the App\Schedule model, which is a model in my project. I am at a total loss as to what it could be or why it's trying to run something I never asked it to run.
Edit: There is also no other instance of /schedule/availability in the routes file as well, I triple checked.
Update: Here is the Constructor for ScheduleController:
public function __construct()
{
$this->middleware('auth'); //Check is user is logged in
}
Here is the stacktrace I received (For some reason, it didn't show up in my error log so I only have the screenshot)
As it turns out in the discussion in comments above. The culprit is narrowed down to a conflicting route.
Route:something('/schedule', ...)
was found before
Route::get('/schedule/availability', 'ScheduleController#getAvailabilityCalendar');
and the Route:something fires before the desired route. It should be noted that route defined first will be the first one to serve. This is the reason why Route:resource should be put after your custom Route:get or another routes to avoid confusion.
Next item in the list when changing routes but it does not updated, alwasy fire a php artisan route:clear. It will clear the route caches - can check in the docs.

Laravel5 Pagination Links Getting 404 Error

I developed a laravel web application and created a pagination view of business list...
I have two business rows in my business table and collecting each one row in a view give pagination for accessing others...
Controller..
public function businessindex(){
$arr = DB::table('business')->paginate(1);
$arr->setPath('business');
return View::make('admin.SABC.businessindex')->with('data',$arr);
}
Routes.php
Route::get('business',['as'=>'business','uses'=>'SABusinessCustomersController#businessindex']);
View.
<tbody>
<?php $i=1;?>
#foreach($data as $datas)
<tr>
<td>{{$i++}}</td>
<td>{{$datas->ci_busns_name}}</td>
<td>{{$ctgry}}</td>
<td>{{date('M-d-Y',strtotime($datas->created_at))}}</td>
<td><span class="{{$grenclass}}">{{$status}}</span><span class="{{$redclass}}" id="{{$datas->pk_business_id}}">{{$oppostatus}}</span></td>
<td><span class="{{$lgrenclass}}">{{$lglstatus}}</span><span class="{{$lredclass}}" id="{{$datas->pk_business_id}}">{{$opplgl}}</span></td>
<td>
<span><a class="editThis" id="{{Crypt::encrypt($datas->pk_business_id)}}" href="{{URL::route('edit_business_view',[Crypt::encrypt($datas->pk_business_id)])}}"></a></span>
<span><a onclick="return confirm('Are you sure you want to delete this item?');"
class="dltThis" href="{{URL::route('brand_delte',[$datas->pk_business_id])}}"></a></span>
</td>
</tr>
#endforeach
</tbody>
</table>
<span class="totalContspan">Showing 0 to {{$i-1}} of {{$i-1}} entries</span>
<div class="paghdlr">{!! str_replace('/?', '?', $data->render()) !!}</div>
<div class="clearfix"></div>
Now I am getting like above screenshot,, its sending too many ajax request continuously in my page. I can see that from network tab of Firebug.
When Click on the links Its throwing an ajax request and get 404 error, I doesn't try the laravel ajax pagination in my site. Only required the Laravel Pagination.
How can i solve this issue..?

Codeigniter get the value of a <td> element onclick

I am trying to get table data when the user clicks on the text inside element. I have tried number of java script but I cant get the value to be send back to the controller for further processing. I would really appreciate if you can help me out. I have added necessary code for you to see if I am doing something wrong.
<div class="box-content">
<table id="suppliertable" class="table table-striped table-bordered bootstrap-datatable datatable">
<thead>
<tr>
<th>Supplier</th>
<th>Open Range</th>
<th>Fill Content</th>
<th>Total Match</th>
</tr>
</thead>
<tbody>
<?php foreach($present_all_suppliers as $v): ?>
<tr>
<td class="center" style="color:#0c595b;"><?php echo $v->supplierCompany;?> </a> </td>
<td class="center">70%</td>
<td class="center">12%</td>
<td class="center">82%</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
Java Script:
document.getElementById ( "tdid" ).textContent
var tdElem = document.getElementById ( "tdid" );
var tdText = tdElem.innerText | tdElem.textContent;
This is going to require some fancy ajax and a javascript enclosure. Hopefully this example will help.
http://jsfiddle.net/qpe2wrmt/1/
function addEventHandler(elem,eventType,handler){
if (elem.addEventListener)
elem.addEventListener(eventType,handler,false);
else if (elem.attachEvent)
elem.attachEvent ('on'+eventType,handler);
}
var items = document.getElementsByClassName("clickable");
i = items.length;
while(i--){
(function(item){
addEventHandler(item,"click",function(){
// make your ajax call here
window.alert("clicked on a thing: "+item.innerHTML);
});
})(items[i]);
}
1) Add a 'clickable' class to each td you want to be cliackable
2) Loop through all clickable items by classname and assign an event handler to each.
3) Make an XHR request when clicked on.
You need to pass the data back to the controller from Javascript. That means that when the user clicks on the <td> in question an AJAX request must be sent to the appropriate controller and the controller can then execute some server action or return some data that would be used by the AJAX callback function.
Here is an example:
http://www.w3schools.com/php/php_ajax_php.asp
This shows how to use AJAX and PHP to make dynamic changes to the HTML DOM objects.
Here is another question on stackoverflow about onclick and AJAX and PHP:
Call a PHP function after onClick HTML event
What you need to learn is AJAX and how to build your application to use it if you want to make the page look and feel more modern (HTML5).

Laravel 4 PHP pagination inside view

I use PHP to load XML files. Can I paginate it through the view?
Here's the code I use. When I pull the names from the XML file, I need to paginate them. Could I use the laravels ->paginate();
I use:
#extends('base')
#section('title', 'Monsters')
#stop
#section('main')
<div class="container">
<div class="doc-content-box">
<legend>Monsters</legend>
<?php
$monstersdir = 'C:\Users\E1\Desktop\theforgottenserver-v0.2.15-win64console\Mystic Spirit\data\monster';
if(is_dir($monstersdir)) {
$list = simplexml_load_file($monstersdir.'\monsters.xml');
?>
<table class="table table-striped table-condensed table-content">
<tbody>
<?php
foreach($list as $monster) {
if ($monster['name'] > null) {
echo '
<tr>
<td style="">'.$monster['name'].'</td>
</tr>';
}
}
}
else{
echo '<div class="alert alert-danger"><span class="label label-important">Error parsing your Monsters XML file</span><br /><br /> Invalid path!</div> ';
}
?>
</tbody>
</table>
</div>
</div>
#stop
Any hints? It's really needed. :P
You can create a paginator manually in Laravel by calling Paginator::make(). Invoke the method with 3 arguments, i.e: the items want to display, the total number of items, and how many of those items should be displayed per page. See the docs for an example.

Categories