В приложении laravel 10 / livewire 3 я создаю отчетchartjs на основе данных из базы данных и 4 фильтров. У меня все работает с блейд-файлом:
<div class = "editor_field_block_wrapper">
<div class = "editor_field_block_device_splitter">
<div class = "w-4/12 pb-0 pl-2 md:pt-3 ">
<label for = "filterModelType" class = "editor_field_block_device_label">Model type: <span
class = "editor_form_aria_required" aria-required = "true"> * </span></label>
</div>
<div class = "p-2 w-full">
<select wire:model.blur = "filterModelType" class = "editor_form_input" tabindex = "20"
id = "filterModelType">
<option value = ""> - Select all -</option>
@foreach($modelTypeSelectionItems as $key=>$label)
<option value = "{{$key}}">{{$label}}</option>
@endforeach
</select>
@error('filterModelType')
<span class = "error_text">{{$message}}</span>
@enderror
</div>
</div>
</div>
<div class = "editor_field_block_wrapper">
<div class = "editor_field_block_device_splitter">
<div class = "w-4/12 pb-0 pl-2 md:pt-3 ">
<label for = "filterDateFrom" class = "editor_field_block_device_label">From date</label>
</div>
<div class = "p-2 w-full">
<x-inputs.datepicker id = "filterDateFrom" wire:model.lazy = "filterDateFrom" tabindex = "20"/>
@error('filterDateFrom') <span
class = "editor_form_validation_error">{{ $message }}</span> @enderror
</div>
</div>
</div>
<div class = "editor_field_block_wrapper">
<div class = "editor_field_block_device_splitter">
<div class = "w-4/12 pb-0 pl-2 md:pt-3 ">
<label for = "filterDateTill" class = "editor_field_block_device_label">Till date</label>
</div>
<div class = "p-2 w-full">
<x-inputs.datepicker id = "filterDateTill" wire:model.lazy = "filterDateTill" tabindex = "30"/>
@error('filterDateTill') <span
class = "editor_form_validation_error">{{ $message }}</span> @enderror
</div>
</div>
</div>
</fieldset>
@if ($this->totalReactionCount > 0)
<div class = "min-w-full">
<canvas id = "reactionsChartStatisticsChart" style = "background: #000000"></canvas>
</div>
@else
<div class = "m-2 p-2 warning_text">
{!! AppIconFacade::get(IconEnum::Warning ) !!}
There are no data found !
</div>
@endif
@assets
<script src = "https://cdn.jsdelivr.net/npm/chart.js"></script>
@endassets
@script
<script>
const ctx = document.getElementById('reactionsChartStatisticsChart');
const reactions = $wire.reactions;
const labels = reactions.map(item => item.action_label)
const values = reactions.map(item => item.reaction_count)
var chartObj = new Chart(ctx, {
type: 'pie',
data: {
labels: labels,
datasets: [{
label: '# of Reactions',
data: values,
borderWidth: 2
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
},
plugins: {
legend: {
position: 'top',
labels: {
font: {
size: 18
},
fontColor: 'white'
}
},
title: {
display: true,
text: 'Reactions by type with total quality {{ $totalReactionCount }}'
}
}
}
});
</script>
@endscript
console.info(chartObj)
// chartObj.update() // IF TO UNCOMMENT - RAISED ERROR : Uncaught SyntaxError: Unexpected end of input
// chartObj.refresh() // IF TO UNCOMMENT - RAISED ERROR : chartObj.refresh is not a function
</div>
</div>
и когда страница перезагружается в первый раз, у меня есть действительная диаграмма с максимальной шириной в упаковке html-элементов:
Когда я меняю некоторые параметры фильтра, данные перезагружаются, и размер диаграммы становится небольшим:
для третьего фильтра изменен элемент реагированияChartStatisticsChart, потерявший свойства диаграммы, и у меня есть черный блок:
Поискав в сети, я нашел несколько методовchartObj.update(), но они мне не помогли.
Каким образом я могу проверить/исправить график?
Код компонента источника данных:
В классе компонента ReactionsChartStatistics.php у меня есть:
<?php
namespace App\Livewire\Admin;
use App\Enums\ReactionActionEnum;
use App\Models\Reaction;
...
class ReactionsChartStatistics extends Component
{
public array $reactions = [];
public int $totalReactionCount = 0;
public function mount()
{
// Get data for filtering block
$this->usersSelectionItems = User::get()->pluck('name','id')->toArray();
$this->modelTypeSelectionItems = $this->getModelTypeSelectionItems();
$statisticsDays = ConfigValueEnum::get(ConfigValueEnum::NEWS_REACTIONS_STATISTICS_DAYS);
$this->filterDateFrom = Carbon::now()->addDays(-$statisticsDays)->startOfDay();
$this->filterDateTill = Carbon::now()->endOfDay();
}
public function render(): View
{
$reactionTb = (new Reaction)->getTable();
$this->totalReactionCount = 0;
$this->reactions = Reaction
::getByCreatedAt($this->filterDateFrom, '>=')
->getByCreatedAt($this->filterDateTill, '<')
->groupBy('action')
->orderBy('reaction_count', 'desc')
->select(
$reactionTb . '.action',
DB::raw('count(' . $reactionTb . '.id) as reaction_count'))
->get()->toArray();
}
foreach ($this->reactions as $key => $reaction) {
$this->reactions[$key]['action_label'] = ReactionActionEnum::getLabel($reaction['action']);
$this->totalReactionCount += $reaction['reaction_count'];
}
return view('livewire.admin.reactions-chart-statistics')->layout('components.layouts.admin');
}
}
Добавьте провод:игнорировать в <div>, содержащий график, чтобы Livewire не обновлял его:
@if ($this->totalReactionCount > 0)
<div wire:ignore class = "min-w-full"> {{-- HERE -- }}
Чтобы обновить диаграмму при обновлении входных данных, мы можем отправить событие из бэкэнда.
Класс
class ReactionsChartStatistics extends Component
{
public array $reactions = [];
public int $totalReactionCount = 0;
public $filterModelType;
public $filterDateFrom;
public $filterDateTill;
public $modelTypeSelectionItems;
protected function prepareData()
{
// This code was previously found in the render() method
$reactionTb = (new Reaction)->getTable();
$this->totalReactionCount = 0;
$this->reactions = Reaction
::getByCreatedAt($this->filterDateFrom, '>=')
->getByCreatedAt($this->filterDateTill, '<')
->groupBy('action')
->orderBy('reaction_count', 'desc')
->select(
$reactionTb . '.action',
DB::raw('count(' . $reactionTb . '.id) as reaction_count'))
->get()->toArray();
}
foreach ($this->reactions as $key => $reaction) {
$this->reactions[$key]['action_label'] = ReactionActionEnum::getLabel($reaction['action']);
$this->totalReactionCount += $reaction['reaction_count'];
}
}
public function mount()
{
// Get data for filtering block
$this->usersSelectionItems = User::get()->pluck('name','id')->toArray();
$this->modelTypeSelectionItems = $this->getModelTypeSelectionItems();
$statisticsDays = ConfigValueEnum::get(ConfigValueEnum::NEWS_REACTIONS_STATISTICS_DAYS);
$this->filterDateFrom = Carbon::now()->addDays(-$statisticsDays)->startOfDay();
$this->filterDateTill = Carbon::now()->endOfDay();
$this->prepareData(); // <~~~ Added
}
// Raises an event when the parameters are updated
public function updated($property, $value)
{
$this->prepareData();
$this->dispatch('refresh-chart');
}
public function render()
{
return view('livewire.admin.reactions-chart-statistics')->layout('components.layouts.admin');
}
}
Javascript в представлении
@script
<script>
const ctx = document.getElementById('reactionsChartStatisticsChart');
const getLabels = () => $wire.reactions.map(item => item.action_label);
const getValues = () => $wire.reactions.map(item => item.reaction_count);
let chartObj = new Chart(ctx, {
type: 'pie',
data: {
labels: getLabels(),
datasets: [{
label: '# of Reactions',
data: getValues(),
borderWidth: 2
}]
},
options: {
scales: {
y: { beginAtZero: true }
},
plugins: {
legend: {
position: 'top',
labels: {
font: { size: 18 },
fontColor: 'white'
}
},
title: {
display: true,
text: 'Reactions by type with total quality {{ $totalReactionCount }}'
}
}
}
});
// This event listener updates the chart
$wire.on("refresh-chart", () => {
chartObj.data.labels = getLabels();
chartObj.data.datasets = [{ data: getValues() }];
chartObj.update()
});
</script>
@endscript
В бэкэнде я перенес подготовку данных в специальный метод подготовить данные(), который сначала вызывается в методе mount(), а затем в методе update(). Метод update() вызывается Livewire каждый раз, когда входные данные меняют значение, а также используется для отправки события обновления диаграммы.
Во внешнем интерфейсе я добавил прослушиватель событий $wire.on("refresh-chart", ....), который обновляет данные диаграммы, а затем вызываетchartObj.update() для обновления DOM.
провод:игнорировать в диаграмме <div> все еще требуется
Как заполняется переменная $reactions? Пожалуйста, покажите также соответствующий код класса
Пожалуйста, проверьте код компонента источника данных.
Нет, это не работает. Я заполнил диаграмму данными при загрузке страницы, но любой следующий div диаграммы, изменяющий данные, не ссылается на новый набор данных из базы данных.