Widgets (aka cards, aka charts, aka graphs) provide a simple way to insert blade files into admin panel pages. You can use them to insert cards, charts, notices or custom content into pages.
In order to use the Widget
class, you should make sure your main views (for new admin panel pages) extend the backpack::blank
or backpack_view('blank')
blade template. This template includes two sections where you can push widgets:
before_content
after_content
You can easily push widgets to these sections, by using the autoloaded Widget
class. You can think of the Widget
class as a global container for widgets, for the current page being rendered. That means you can call the Widget
container inside a Controller
, inside a view
, or inside a service provider you create - wherever you want.
use Backpack\CRUD\app\Library\Widget;
Widget::add($widget_definition_array)->to('before_content');
// alternatively, use a fluent syntax to define each widget attribute
Widget::add()
->to('before_content')
->type('card')
->content(null);
When passing a widget array, you need to specify at least these attributes:
[
'type' => 'card' // the kind of widget to show
'content' => null // the content of that widget (some are string, some are array)
],
Most widget types also have these attributes present, which you can use to tweak how the widget looks inside the page:
'wrapper' => [
'class' => 'col-sm-6 col-md-4', // customize the class on the parent element (wrapper)
'style' => 'border-radius: 10px;',
]
To manipulate widgets, you can use the methods below. The action will be performed on the page being constructed for the current request. And the Widget
class is a global container, so you can add widgets to it both from the Controller, and from the view.
// to add a widget to a different section than the default 'before_content' section:
Widget::add($widget_definition_array)->to('after_content');
Widget::add($widget_definition_array)->section('after_content');
Widget::add($widget_definition_array)->group('after_content');
// to create a widget, WITHOUT adding it to a section
Widget::make($widget_definition_array);
// to define the contents of a widget, pass the definition array to the make()/add() methods
Widget::add($widget_definition_array);
Widget::make($widget_definition_array);
// alternatively, define each widget attribute one by one, using a fluent syntax
Widget::add()
->to('after_content')
->type('card')
->content('something');
// to reference a widget later on, give it a unique 'name'
Widget::add($widget_definition_array)->name('my_widget');
// you can then easily modify it
Widget::name('my_widget')->content('some other content'); // change the 'content' attribute
Widget::name('my_widget')->forget('attribute_name'); // unset a widget attribute
Widget::name('my_widget')->makeFirst(); // make a widget the first one in its section
Widget::name('my_widget')->makeLast(); // to make a widget the last one in its section
Widget::name('my_widget')->remove(); // remove the widget from its section
Shows a notification bubble, with the heading and text you specify:
[
'type' => 'alert',
'class' => 'alert alert-danger mb-2',
'heading' => 'Important information!',
'content' => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Corrupti nulla quas distinctio veritatis provident mollitia error fuga quis repellat, modi minima corporis similique, quaerat minus rerum dolorem asperiores, odit magnam.',
'close_button' => true, // show close button or not
]
For different colors, you can use the following classes: alert-success
, alert-warning
, alert-info
, alert-danger
alert-primary
, alert-secondary
, alert-light
, alert-dark
.
Widget Preview:
Shows a Bootstrap card, with the heading and body you specify. You can customize the class name to get cards of different color, size, etc.
[
'type' => 'card',
// 'wrapper' => ['class' => 'col-sm-6 col-md-4'], // optional
// 'class' => 'card bg-dark text-white', // optional
'content' => [
'header' => 'Some card title', // optional
'body' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non mi nec orci euismod venenatis. Integer quis sapien et diam facilisis facilisis ultricies quis justo. Phasellus sem <b>turpis</b>, ornare quis aliquet ut, volutpat et lectus. Aliquam a egestas elit. <i>Nulla posuere</i>, sem et porttitor mollis, massa nibh sagittis nibh, id porttitor nibh turpis sed arcu.',
]
]
For different background colors, you can use the following classes: bg-success
, bg-warning
, bg-info
, bg-danger
bg-primary
, bg-secondary
, bg-light
, bg-dark
.
Other useful helper classes: text-center
, text-white
.
Widget Preview:
Shows a pie chart / line chart / bar chart inside a Bootstrap card, with the heading and body you specify.
To create and use a new widget chart, you need to:
Step 1. Install laravel-charts, that offers a single PHP syntax for 6 different charting libraries:
composer require consoletvs/charts:"6.*"
Step 2. Create a new ChartController:
php artisan backpack:chart WeeklyUsers
This will create:
app\Http\Controllers\Admin\Charts\WeeklyUsersChartController
routes/backpack/custom.php
Step 3. Add the widget that points to that ChartController you just created:
Widget::add([
'type' => 'chart',
'controller' => \App\Http\Controllers\Admin\Charts\WeeklyUsersChartController::class,
// OPTIONALS
// 'class' => 'card mb-2',
// 'wrapper' => ['class'=> 'col-md-6'] ,
// 'content' => [
// 'header' => 'New Users',
// 'body' => 'This chart should make it obvious how many new users have signed up in the past 7 days.<br><br>',
// ],
]);
Step 4. Configure the ChartController you just created:
public function setup()
(MANDATORY)
$this->chart
, using the methods detailed in the laravel-charts documentation;public function data()
(OPTIONAL, but recommended)
$this->chart->dataset()
to configure what the chart should contain;Optionally:
-use ConsoleTVs\Charts\Classes\Chartjs\Chart;
+use ConsoleTVs\Charts\Classes\Echarts\Chart;
+use ConsoleTVs\Charts\Classes\Fusioncharts\Chart;
+use ConsoleTVs\Charts\Classes\Highcharts\Chart;
+use ConsoleTVs\Charts\Classes\C3\Chart;
+use ConsoleTVs\Charts\Classes\Frappe\Chart;
$library
or getLibraryFilePath()
on your ChartController:protected $library = 'http://path/to/file';
// or
public function getLibraryFilePath()
{
return asset('path/to/your/js/file');
// or
return [
asset('path/to/first/js/file'),
asset('path/to/second/js/file'),
];
}
That's it! Refresh the page to see your new chart widget. Below, you'll find a few examples of how the ChartController can end up looking.
Example 1: ChartController that loads results using AJAX:
<?php
namespace App\Http\Controllers\Admin\Charts;
use App\User;
use Backpack\CRUD\app\Http\Controllers\ChartController;
use Backpack\NewsCRUD\app\Models\Article;
use Backpack\NewsCRUD\app\Models\Category;
use Backpack\NewsCRUD\app\Models\Tag;
use ConsoleTVs\Charts\Classes\Chartjs\Chart;
class NewEntriesChartController extends ChartController
{
public function setup()
{
$this->chart = new Chart();
// MANDATORY. Set the labels for the dataset points
$labels = [];
for ($days_backwards = 30; $days_backwards >= 0; $days_backwards--) {
if ($days_backwards == 1) {
}
$labels[] = $days_backwards.' days ago';
}
$this->chart->labels($labels);
// RECOMMENDED.
// Set URL that the ChartJS library should call, to get its data using AJAX.
$this->chart->load(backpack_url('charts/new-entries'));
// OPTIONAL.
$this->chart->minimalist(false);
$this->chart->displayLegend(true);
}
/**
* Respond to AJAX calls with all the chart data points.
*
* @return json
*/
public function data()
{
for ($days_backwards = 30; $days_backwards >= 0; $days_backwards--) {
// Could also be an array_push if using an array rather than a collection.
$users[] = User::whereDate('created_at', today()
->subDays($days_backwards))
->count();
$articles[] = Article::whereDate('created_at', today()
->subDays($days_backwards))
->count();
$categories[] = Category::whereDate('created_at', today()
->subDays($days_backwards))
->count();
$tags[] = Tag::whereDate('created_at', today()
->subDays($days_backwards))
->count();
}
$this->chart->dataset('Users', 'line', $users)
->color('rgb(77, 189, 116)')
->backgroundColor('rgba(77, 189, 116, 0.4)');
$this->chart->dataset('Articles', 'line', $articles)
->color('rgb(96, 92, 168)')
->backgroundColor('rgba(96, 92, 168, 0.4)');
$this->chart->dataset('Categories', 'line', $categories)
->color('rgb(255, 193, 7)')
->backgroundColor('rgba(255, 193, 7, 0.4)');
$this->chart->dataset('Tags', 'line', $tags)
->color('rgba(70, 127, 208, 1)')
->backgroundColor('rgba(70, 127, 208, 0.4)');
}
}
Example 2. Pie chart with both labels and dataset defined in the setup()
method (no AJAX):
<?php
namespace App\Http\Controllers\Admin\Charts\Pies;
use Backpack\CRUD\app\Http\Controllers\ChartController;
use ConsoleTVs\Charts\Classes\Chartjs\Chart;
class ChartjsPieController extends ChartController
{
public function setup()
{
$this->chart = new Chart();
$this->chart->dataset('Red', 'pie', [10, 20, 80, 30])
->backgroundColor([
'rgb(70, 127, 208)',
'rgb(77, 189, 116)',
'rgb(96, 92, 168)',
'rgb(255, 193, 7)',
]);
// OPTIONAL
$this->chart->displayAxes(false);
$this->chart->displayLegend(true);
// MANDATORY. Set the labels for the dataset points
$this->chart->labels(['HTML', 'CSS', 'PHP', 'JS']);
}
}
Allows you to include multiple widgets within a "div" element with the attributes of your choice. For example, you can include multiple widgets within a <div class="row"></div>
with the code below:
[
'type' => 'div',
'class' => 'row',
'content' => [ // widgets
[ 'type' => 'card', 'content' => ['body' => 'One'] ],
[ 'type' => 'card', 'content' => ['body' => 'Two'] ],
[ 'type' => 'card', 'content' => ['body' => 'Three'] ],
]
]
Anything you specify on this widget, other than type
and content
, has to be a string, and will be considered an attribute of the "div" element.
For example, in the following snippet, class
and custom-attribute
are attributes of the "div" element:
[
'type' => 'div',
'class' => 'row my-custom-widget-class',
'custom-attribute' => 'my-custom-value',
'content' => [ // widgets
[ 'type' => 'card', 'content' => ['body' => 'One'] ],
[ 'type' => 'card', 'content' => ['body' => 'Two'] ],
[ 'type' => 'card', 'content' => ['body' => 'Three'] ],
]
]
and the generated output will be:
<div custom-attribute="my-custom-value" class="row my-custom-widget-class">
// The HTML code of the three card widgets will be here
</div>
Shows a Bootstrap jumbotron component, with the heading and body you specify.
[
'type' => 'jumbotron',
'heading' => 'Welcome!',
'content' => 'My magnific headline! Lets build something awesome together.',
'button_link' => backpack_url('logout'),
'button_text' => 'Logout',
// OPTIONAL:
'heading_class' => 'display-3 text-white',
'content_class' => 'text-white',
]
Widget Preview:
Add a Livewire component to a page. If you haven't created your component yet, head to Livewire documentation and create the component you want to use.
Note Livewire v2: Livewire v2 does not automatically inject the @livewireScripts
and @livewireStyles
tags. If you are NOT using Livewire outside of this widget you can load them here by setting livewireAssets => true
[
'type' => 'livewire',
'content' => 'my-livewire-component', // the component name
'parameters' => ['user' => backpack_user(), 'param2' => 'value2'], // optional: pass parameters to the component
'livewireAssets' => false, // optional: set true to load livewire assets in the widget
]
Note: The parameters
attribute will be passed to the component on initialization, and should be present in the mount($user, $param2)
.
use Livewire\Component;
class HelloWorld extends Component
{
public $name;
public function mount(string $name)
{
$this->name = $name;
}
public function render()
{
return view('livewire.hello-world');
}
}
<!-- livewire/hello-world.blade.php -->
<div>
Hello {{ $name }}
</div>
// add the widget to the page
Widget::add()->type('livewire')->content('hello-world')->parameters(['name' => 'John Doe'])->wrapperClass('col-md-12 text-center');
Widget Preview:
Shows a colorful card to signify the progress towards a goal. You can customize the class name to get cards of different color, size, etc.
[
'type' => 'progress',
'class' => 'card text-white bg-primary mb-2',
'value' => '11.456',
'description' => 'Registered users.',
'progress' => 57, // integer
'hint' => '8544 more until next milestone.',
]
For different background colors, you can use the following classes: bg-success
, bg-warning
, bg-info
, bg-danger
bg-primary
, bg-secondary
, bg-light
, bg-dark
.
Other useful helper classes: text-center
, text-white
.
Widget Preview:
Shows a white card to signify the progress towards a goal. You can customize the class name to get progress bars of different color.
[
'type' => 'progress_white',
'class' => 'card mb-2',
'value' => '11.456',
'description' => 'Registered users.',
'progress' => 57, // integer
'progressClass' => 'progress-bar bg-primary',
'hint' => '8544 more until next milestone.',
]
For different progress bar colors, you can use the following classes: bg-success
, bg-warning
, bg-info
, bg-danger
bg-primary
, bg-secondary
, bg-light
, bg-dark
.
Widget Preview:
Loads a JavaScript file from a location you specify using a <script>
element.
[
'type' => 'script',
'content' => 'assets/js/custom-script.js',
// optional
// 'stack' => 'before_scripts', // default is after_scripts
]
You can also specify a link (https://path-to-file.com/file.js
) as the content, and it will load the file from a CDN. In that case you might also want to add additional attributes, which you can easily do, for example:
Widget::add()->type('script')
->content('https://code.jquery.com/ui/1.12.0/jquery-ui.min.js')
->integrity('sha256-0YPKAwZP7Mp3ALMRVB2i8GXeEndvCq3eSl/WsAl1Ryk=')
->crossorigin('anonymous');
Loads a CSS file from a location you specify using a <link>
element.
[
'type' => 'style',
'content' => 'assets/css/custom-script.css',
// optional
// 'stack' => 'before_styles', // default is after_styles
]
You can also specify a link (https://path-to-file.com/file.css
) as the content, and it will load the file from a CDN. In that case you might also want to add additional attributes, which you can easily do, for example:
Widget::add()->type('style')
->content('https://cdn.jsdelivr.net/npm/@shoelace-style/[email protected]/dist/themes/light.css')
->integrity('sha256-0YPKAwZP7Mp3ALMRVB2i8GXeEndvCq3eSl/WsAl1Ryk=')
->crossorigin('anonymous');
Loads a blade view from a location you specify. Any attributes you give it will be available in the $widget
variable inside that view.
[
'type' => 'view',
'view' => 'path.to.custom.view',
'someAttr' => 'some value',
]
It helps load blade files that are not specifically created to be widgets, that live in a different path than resources/views/vendor/backpack/ui/widgets
, as if they were widgets.
You can override a widget type by placing a file with the same name in your resources\views\vendor\backpack\ui\widgets
directory. When a file is there, Backpack will pick that one up, instead of the one in the package. You can do that from command line using php artisan backpack:widget --from=widget-name
Examples:
resources\views\vendor\backpack\ui\widgets\card.blade.php
file would overwrite the card
widget functionality;php artisan backpack:widget --from=card
will take the view from the package and copy it to the directory above, so you can edit it;Keep in mind that when you're overwriting a default widget type, you're forfeiting any future updates for that widget. We can't push updates to a file that you're no longer using.
Widgets consist of only one file - a blade file with the same name as the widget type (ex: card.blade.php
). You can create one by placing a new blade file inside resources\views\vendor\backpack\ui\widgets
. Be careful to choose a distinctive name, otherwise you might be overwriting a default widget type (see above).
For example, you can create a well.blade.php
:
@includeWhen(!empty($widget['wrapper']), 'backpack::widgets.inc.wrapper_start')
<div class="{{ $widget['class'] ?? 'well mb-2' }}">
{!! $widget['content'] !!}
</div>
@includeWhen(!empty($widget['wrapper']), 'backpack::widgets.inc.wrapper_end')
You can then use the well
widget in a Controller or View:
@extends(backpack_view('blank'))
@php
Widget::add([
'type' => 'well',
'wrapper' => ['class' => 'col-sm-12'],
'content' => 'This text will be in a div with the class "<i>well</i>".',
]);
@endphp
@section('content')
@endsection
To use information from the database, you can:
\App\Models\Product::count()
;View::composer('backpack::widgets.well, 'App\Http\View\Composers\WellComposer');
Inside the widget blade files, you include custom CSS and JS, by pushing to the stacks in the layout:
@includeWhen(!empty($widget['wrapper']), 'backpack::widgets.inc.wrapper_start')
<div class="{{ $widget['class'] ?? 'well mb-2' }}">
{!! $widget['content'] !!}
</div>
@includeWhen(!empty($widget['wrapper']), 'backpack::widgets.inc.wrapper_end')
@push('after_styles')
<link href="{{ asset('packages/select2/dist/css/select2.min.css') }}" rel="stylesheet" type="text/css" />
<style>
.some_class {
color: red;
}
</style>
@endpush
@push('after_scripts')
<script src="{{ asset('packages/select2/dist/js/select2.min.js') }}"></script>
<script>
jQuery(document).ready(function($) {
// trigger select2 for each untriggered select2 box
$('.select2_field').each(function (i, obj) {
if (!$(obj).hasClass("select2-hidden-accessible"))
{
$(obj).select2({
theme: "bootstrap"
});
}
});
});
</script>
@endpush
You can choose the view namespace when loading a widget:
// using the fluent syntax, use the 'from' alias
Widget::add($widget_definition_array)->from('package::widgets');
// using the widget definition array, specify its 'viewNamespace'
Widget::add([
'type' => 'card',
'viewNamespace' => 'package::widgets',
'wrapper' => ['class' => 'col-sm-6 col-md-4'],
'class' => 'card text-white bg-primary text-center',
'content' => [
// 'header' => 'Another card title',
'body' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis non mi nec orci euismod venenatis. Integer quis sapien et diam facilisis facilisis ultricies quis justo. Phasellus sem <b>turpis</b>, ornare quis aliquet ut, volutpat et lectus. Aliquam a egestas elit.',
],
]);
Similarly, if you want to create widgets somewhere else than in resources/views/vendor/backpack/ui/widgets
, you can pass that directory as the namespace of your widget. For example, resources/views/admin/widgets
would have admin.widgets
as the namespace.
Then you'll love our premium add-ons - productivity tools and tons of new features.