Search code examples
javascriptlaravellaravel-bladelaravel-7scaffolding

include js file in blade templating


I am coding an app using laravel and blade template engine and. I am including my page specific scripts in a section called page-scripts that gets yielded in the main layout where the main script is placed but that doesn't work and I keep getting "cannot read property x of null" which means the dom elements are not visible by the script.

This is the main layout

<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1">

 <!-- CSRF Token -->
 <meta name="csrf-token" content="{{ csrf_token() }}">

 <title>{{ config('app.name', 'Laravel') }}</title>

 <!-- Scripts -->
 <script src="{{ asset('js/app.js') }}" defer></script>
 @yield('page-scripts')

 <!-- Fonts -->
 <link rel="dns-prefetch" href="//fonts.gstatic.com">
 <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
 <link href="https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200;0,400;0,800;0,900;1,800&display=swap" rel="stylesheet">

 <!-- Styles -->
 <link href="{{ asset('css/main.css') }}" rel="stylesheet">
</head>
<body>
 <div id="app">

     @auth
         <nav class="flex items-center justify-between flex-wrap bg-red-500 p-6 font-nunito">
             <div class="flex items-center flex-shrink-0 text-white mr-6">
                 <span class="font-semibold text-xl tracking-tight">Innova</span>
             </div>
             <div class="block lg:hidden">
                 <button class="flex items-center px-3 py-2 border rounded text-white border-teal-400 hover:text-white hover:border-white">
                     <svg class="fill-current h-3 w-3" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>Menu</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"/></svg>
                 </button>
             </div>
             <div class="w-full block flex-grow lg:flex lg:items-center lg:w-auto">
                 <div class="text-sm lg:flex-grow">
                     <a href="#responsive-header" class="block mt-4 lg:inline-block lg:mt-0 text-white hover:text-white mr-4">
                         Lista canali
                     </a>
                     <a href="#responsive-header" class="block mt-4 lg:inline-block lg:mt-0 text-white hover:text-white mr-4">
                         Generale
                     </a>
                     <a href="#responsive-header" class="block mt-4 lg:inline-block lg:mt-0 text-white hover:text-white">
                         Profilo
                     </a>
                 </div>
                 <div>
                     <a href="{{ route('logout') }}"
                        onclick="event.preventDefault();
                    document.getElementById('logout-form').submit();" class="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-red-500 hover:bg-white mt-4 lg:mt-0">Logout</a>
                     <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                         @csrf
                     </form>
                 </div>
             </div>
         </nav>
     @endauth

     <main class="py-4">
         @yield('content')
     </main>
 </div>
</body>
</html> 

here I have one of my pages


@section('page-scripts')
    <script type="text/javascript" src="{{ URL::asset('js/page.js') }}"></script>

@endsection

@section('content')
    <form class="w-full max-w-lg" action="{{'/post/'.Request::path()}}" method="POST">
        @csrf
        <div class="flex flex-wrap -mx-3 mb-6 font-nunito">
            <div class="w-full md:w-1/2 px-3 mb-6 md:mb-0">
                <label class="block tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-first-name">
                    Condividi qualcosa con gli altri innovatori
                </label>
                <input  class="appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-first-name" name="title" placeholder="Titolo"/>
                @error('title')
                <p class="text-red-500 text-xs italic">Il post deve avere un titolo</p>
                @enderror
                <textarea  class="appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-first-name" name="body" placeholder="Testo"></textarea>
                @error('body')
                <p class="text-red-500 text-xs italic">Niente post muti ci spiace</p>
                @enderror

                <div id="tag-output"></div>
                @error('tag')
                <p class="text-red-500 text-xs italic">il post deve contenere tag</p>
                @enderror
                <input  id='tag-input' class="appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-first-name" placeholder="Tags"/>
                <input  id='tag-value'   style="display: none" name="tags" placeholder="Tags"/>


                <button class="flex-shrink-0 bg-red-500 hover:bg-red-500 border-red-500 hover:border-red-500 text-sm border-4 text-white py-1 px-2 rounded" >
                    Condividi
                </button>

            </div>
        </div>
    </form>
    @forelse($channel->posts as $post)
    <h1>{{$post->title}}</h1>
    @foreach($post->comments as $comment)
        <div id="{{$comment->id}}" class="comment-container">
        <p>{{$comment->body}}</p>
        <button id="reply-button-{{$comment->id}}">reply</button>
        <form id='reply-form-{{$comment->id}}' action="/{{$channel->id}}/reply/{{$comment->id}}" method="POST" class="invisible">
            @csrf
            <input  class=" appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-first-name" name="body" placeholder="commenta"/>
            @error('body')
            <p class="text-red-500 text-xs italic">la risposta deve avere un testo</p>
            @enderror
        </form>
        </div>
    @endforeach
        <form action="/{{$channel->id}}/comment/{{$post->id}}" method="POST">
            @csrf
            <input  class="appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-first-name" name="body" placeholder="commenta"/>
            <button class="flex-shrink-0 bg-red-500 hover:bg-red-500 border-red-500 hover:border-red-500 text-sm border-4 text-white py-1 px-2 rounded" >
                Commenta
            </button>
            @error('body')
            <p class="text-red-500 text-xs italic">il commento deve avere un testo</p>

            @enderror
        </form>
    @empty
    <p>No posts yet</p>
    @endforelse


@endsection

Including all my scripts in the main layout wouldn't work because they will be visible also to other pages that don't have the elements of the general page

How do I make this work?


Solution

  • JS is handled differently. It is not yielded but stacked.

    Change

    @yield('page-scripts')
    

    to

    @stack('page-scripts')
    

    and use

    @push('page-scripts')
        <script type="text/javascript" src="{{ URL::asset('js/page.js') }}"></script>
    @endpush
    

    In your views. See more at: https://laravel.com/docs/7.x/blade#stacks