Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to have one-time push in laravel blade

Tags:

php

laravel

blade

I am trying to create a HTML widget with Laravel blade similar to the following (widget.blade.php):

@push('scripts')
   <script src="{{ asset('js/foo.js') }}"></script>
   <script>
   ...
   </script>
@endpush
@push('styles')
   <link href="{{ asset('css/bar.css') }}" rel="stylesheet">
@endpush
<div>
... HTML contents
</div>

and I use the widget in an other blade like:

<div>
  ... 
  @include('widget')
</div>
<div>
  ... 
  @include('widget')
</div>

The problem is when I use the widget multiple times in a page the 'scripts' and 'styles' repeated multiple times.

How can I prevent Laravel to push 'scripts' and 'styles' multiple times?

like image 658
ma.mehralian Avatar asked Jul 01 '16 00:07

ma.mehralian


1 Answers

As of Laravel 7.25, Blade now includes a new @once component that will only render the items within the tags one time. https://laravel.com/docs/8.x/blade#the-once-directive

In the following answer I assumed you are familiar with Blade extension. This method has been tested on Laravel 5.2 and 5.3 (See note below).

After testing Ismail RBOUH's Answer (so please read it), It seems there are two problems with the solution:

1- The $isDisplayed variable is not in a same scope with the other included widgets so each @include push its scripts to stack. As a result I change it to:

Blade::directive('pushonce', function ($expression) {
    $isDisplayed = '__pushonce_'.trim(substr($expression, 2, -2));
    return "<?php if(!isset(\$__env->{$isDisplayed})): \$__env->{$isDisplayed} = true; \$__env->startPush{$expression}; ?>";
});
Blade::directive('endpushonce', function ($expression) {
    return '<?php $__env->stopPush(); endif; ?>';
});

2- The solution limit the use of @pushonce to one widget. i.e. in the case of 2 or more widgets (widget1.blade.php, widget2.blade.php, ...) it prevent to push other widgets scripts. So, I add domain to @pushonce with the following code:

Blade::directive('pushonce', function ($expression) {
    $domain = explode(':', trim(substr($expression, 2, -2)));
    $push_name = $domain[0];
    $push_sub = $domain[1];
    $isDisplayed = '__pushonce_'.$push_name.'_'.$push_sub;
    return "<?php if(!isset(\$__env->{$isDisplayed})): \$__env->{$isDisplayed} = true; \$__env->startPush('{$push_name}'); ?>";
});
Blade::directive('endpushonce', function ($expression) {
    return '<?php $__env->stopPush(); endif; ?>';
});

Usage:

widget1.blade.php

@pushonce('scripts:widget1')
   <script src="{{ asset('js/foo.js') }}"></script>
   <script>
   ...
   </script>
@endpushonce

widget2.blade.php

@pushonce('scripts:widget2')
   <script src="{{ asset('js/bar.js') }}"></script>
   <script>
   ...
   </script>
@endpushonce

NOTE FOR L 5.3: change the following line:

$domain = explode(':', trim(substr($expression, 2, -2)));

to

$domain = explode(':', trim(substr($expression, 1, -1)));
like image 158
ma.mehralian Avatar answered Oct 10 '22 10:10

ma.mehralian