Where to initialize jquery for laravel

Viewed 522

I am trying to add the drag and drop functionality to my fancy tree. However, there seem to be an issue with where i am initializing the jquery library resulting in error messages indicating that the jquery function i am trying to use is not available etc.

Specific error message = app.js:24 jQuery.Deferred exception: $(...).perfectScrollbar is not a function TypeError: $(...).perfectScrollbar is not a function

If i were to directly initialize a perfect scroll bar, it will continue reflecting other libraries are not a function etc

Simplistic overview of code (Edited to show that master blade is already requiring jquery once)

--Master Blade--

    <!DOCTYPE html>
    <html lang="{{ config('app.locale') }}" @if (config('voyager.multilingual.rtl')) dir="rtl" @endif>
    <head>
        <title>@yield('page_title', setting('admin.title') . " - " . setting('admin.description'))</title>

    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="csrf-token" content="{{ csrf_token() }}"/>
    <!-- Google Fonts -->
    <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,700" rel="stylesheet">

    <!-- Favicon -->
    <link rel="shortcut icon" href="{{ voyager_asset('images/logo-icon.png') }}" type="image/x-icon">

    <!-- App CSS -->
    <link rel="stylesheet" href="{{ voyager_asset('css/app.css') }}">

    <!-- fancy tree jquery/css-->
    @yield('css')


    @if(config('voyager.multilingual.rtl'))
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-rtl/3.4.0/css/bootstrap-rtl.css">
        <link rel="stylesheet" href="{{ voyager_asset('css/rtl.css') }}">
    @endif

    <!-- Few Dynamic Styles -->
    <style type="text/css">
        .voyager .side-menu .navbar-header {
            background:#FFFFFF;
            border-color:#FFFFFF;
            {{--background:{{ config('voyager.primary_color','#22A7F0') }};--}}
            {{--border-color:{{ config('voyager.primary_color','#22A7F0') }};--}}
        }
        .widget .btn-primary{
            border-color:{{ config('voyager.primary_color','#22A7F0') }};
        }
        .widget .btn-primary:focus, .widget .btn-primary:hover, .widget .btn-primary:active, .widget .btn-primary.active, .widget .btn-primary:active:focus{
            background:{{ config('voyager.primary_color','#22A7F0') }};
        }
        .voyager .breadcrumb a{
            color:{{ config('voyager.primary_color','#22A7F0') }};
        }
        .app-container .side-menu .panel.widget h5 {
            float: left;
            display: block;
            position: absolute;
            top: 0px;
            width: 180px;
            text-align: left;
            opacity: 0;
            transition: opacity .3s ease;
            margin-top: 17px;
            left: 68px;
            overflow: hidden;
            height: 29px;
        }
        .app-container .side-menu .panel.widget h6 {
            float: left;
            display: block;
            position: absolute;
            top: 15px;
            width: 180px;
            text-align: left;
            opacity: 0;
            transition: opacity .3s ease;
            margin-top: 17px;
            left: 68px;
            overflow: hidden;
            height: 29px;
        }
        .app-container.expanded .panel.widget h5 {
            opacity: 1;
        }
        .app-container.expanded .panel.widget h6 {
            opacity: 1;
        }
        .app-container .side-menu:hover .panel.widget h5 {
            opacity: 1;
        }
        .app-container .side-menu:hover .panel.widget h6 {
            opacity: 1;
        }
    </style>

    @if(!empty(config('voyager.additional_css')))<!-- Additional CSS -->
        @foreach(config('voyager.additional_css') as $css)<link rel="stylesheet" type="text/css" href="{{ asset($css) }}">@endforeach
    @endif

    @yield('head')
</head>

<body class="voyager @if(isset($dataType) && isset($dataType->slug)){{ $dataType->slug }}@endif">

<div id="voyager-loader">
    <?php $admin_loader_img = Voyager::setting('admin.loader', ''); ?>
    @if($admin_loader_img == '')
        <img src="{{ voyager_asset('images/logo-icon.png') }}" alt="Voyager Loader">
    @else
        <img src="{{ Voyager::image($admin_loader_img) }}" alt="Voyager Loader">
    @endif
</div>

<?php
if (starts_with(Auth::user()->avatar, 'http://') || starts_with(Auth::user()->avatar, 'https://')) {
    $user_avatar = Auth::user()->avatar;
} else {
    $user_avatar = Voyager::image(Auth::user()->avatar);
}
?>

<div class="app-container">
    <div class="fadetoblack visible-xs"></div>
    <div class="row content-container">
        @include('voyager::dashboard.navbar')
        @include('voyager::dashboard.sidebar')
        <script>
            (function(){
                    var appContainer = document.querySelector('.app-container'),
                        sidebar = appContainer.querySelector('.side-menu'),
                        navbar = appContainer.querySelector('nav.navbar.navbar-top'),
                        loader = document.getElementById('voyager-loader'),
                        hamburgerMenu = document.querySelector('.hamburger'),
                        sidebarTransition = sidebar.style.transition,
                        navbarTransition = navbar.style.transition,
                        containerTransition = appContainer.style.transition;

                    sidebar.style.WebkitTransition = sidebar.style.MozTransition = sidebar.style.transition =
                    appContainer.style.WebkitTransition = appContainer.style.MozTransition = appContainer.style.transition =
                    navbar.style.WebkitTransition = navbar.style.MozTransition = navbar.style.transition = 'none';

                    if (window.localStorage && window.localStorage['voyager.stickySidebar'] == 'true') {
                        appContainer.className += ' expanded no-animation';
                        loader.style.left = (sidebar.clientWidth/2)+'px';
                        hamburgerMenu.className += ' is-active no-animation';
                    }

                   navbar.style.WebkitTransition = navbar.style.MozTransition = navbar.style.transition = navbarTransition;
                   sidebar.style.WebkitTransition = sidebar.style.MozTransition = sidebar.style.transition = sidebarTransition;
                   appContainer.style.WebkitTransition = appContainer.style.MozTransition = appContainer.style.transition = containerTransition;
            })();
        </script>
        <!-- Main Content -->
        <div class="container-fluid">
            <div class="side-body padding-top">
                @yield('page_header')
                <div id="voyager-notifications"></div>
                @yield('content')
            </div>
        </div>
    </div>
</div>
@include('voyager::partials.app-footer')

<!-- Javascript Libs -->
<script type="text/javascript" src="{{ voyager_asset('js/app.js') }}"></script>

<!-- <script type="text/javascript" src="{{ voyager_asset('js/app.js') }}"></script> -->


<script>
    @if(Session::has('alerts'))
        let alerts = {!! json_encode(Session::get('alerts')) !!};
        helpers.displayAlerts(alerts, toastr);
    @endif

    @if(Session::has('message'))

    // TODO: change Controllers to use AlertsMessages trait... then remove this
    var alertType = {!! json_encode(Session::get('alert-type', 'info')) !!};
    var alertMessage = {!! json_encode(Session::get('message')) !!};
    var alerter = toastr[alertType];

    if (alerter) {
        alerter(alertMessage);
    } else {
        toastr.error("toastr alert-type " + alertType + " is unknown");
    }

    @endif
</script>

@yield('javascript')
<!-- Javascript Libs -->
<!-- <script type="text/javascript" src="{{ voyager_asset('js/app.js') }}"></script> -->


@if(!empty(config('voyager.additional_js')))<!-- Additional Javascript -->
    @foreach(config('voyager.additional_js') as $js)<script type="text/javascript" src="{{ asset($js) }}"></script>@endforeach
@endif

</body>
</html>

--Fancy Tree Blade --

Extends Master Blade
@section('content')
--fancy tree code--
@stop
@section('javascript)
<script src="//code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="//code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<script src="/src/jquery-ui-dependencies/jquery.fancytree.ui-deps.js" type="text/javascript"></script>
<script src="/src/jquery.fancytree.js" type="text/javascript"></script>
<script src="/src/jquery.fancytree.dnd.js" type="text/javascript"></script>
<script src="/src/jquery.fancytree.edit.js" type="text/javascript"></script>
<script type="text/javascript">
$(function(){
        // using default options
        $("#tree").fancytree({
            extensions: ["dnd"],
            checkbox: false,
            icon: false,
            generateIds: true,
            dnd: {
                autoExpandMS: 400,
                focusOnClick: true,
                preventVoidMoves: true, // Prevent dropping nodes 'before self', etc.
                preventRecursiveMoves: true, // Prevent dropping nodes on own descendants
                dragStart: function(node, data) {
                /** This function MUST be defined to enable dragging for the tree.
                 *  Return false to cancel dragging of node.
                 */
                return true;
                },
                dragEnter: function(node, data) {
                /** data.otherNode may be null for non-fancytree droppables.
                 *  Return false to disallow dropping on node. In this case
                 *  dragOver and dragLeave are not called.
                 *  Return 'over', 'before, or 'after' to force a hitMode.
                 *  Return ['before', 'after'] to restrict available hitModes.
                 *  Any other return value will calc the hitMode from the cursor position.
                 */
                // Prevent dropping a parent below another parent (only sort
                // nodes under the same parent)
        /*           if(node.parent !== data.otherNode.parent){
                    return false;
                }
                // Don't allow dropping *over* a node (would create a child)
                return ["before", "after"];
        */
                return true;
                },
                dragDrop: function(node, data) {
                /** This function MUST be defined to enable dropping of items on
                 *  the tree.
                 */
                data.otherNode.moveTo(node, data.hitMode);
                }
            },
}
1 Answers

Most of the time getting this error

TypeError: $(…).perfectScrollbar is not a function

Is because of the wrong Order of scripts and sometimes it is because of jQuery enabled version. in the master.blade, page bring the @yield('javascript') right after calling @include('voyager::partials.app-footer') and make sure there isn't any js file in @include('voyager::partials.app-footer') if there it will cause the same error again so take out the js file and import it in your master.blade file.

Try this

I am trying to add the drag and drop functionality to my fancy tree. However, there seem to be an issue with where i am initializing the jquery library resulting in error messages indicating that the jquery function i am trying to use is not available etc.

Specific error message = app.js:24 jQuery.Deferred exception: $(...).perfectScrollbar is not a function TypeError: $(...).perfectScrollbar is not a function

If i were to directly initialize a perfect scroll bar, it will continue reflecting other libraries are not a function etc

Simplistic overview of code (Edited to show that master blade is already requiring jquery once)

--Master Blade--

    <!DOCTYPE html>
    <html lang="{{ config('app.locale') }}" @if (config('voyager.multilingual.rtl')) dir="rtl" @endif>
    <head>
        <title>@yield('page_title', setting('admin.title') . " - " . setting('admin.description'))</title>

    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="csrf-token" content="{{ csrf_token() }}"/>
    <!-- Google Fonts -->
    <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,700" rel="stylesheet">

    <!-- Favicon -->
    <link rel="shortcut icon" href="{{ voyager_asset('images/logo-icon.png') }}" type="image/x-icon">

    <!-- App CSS -->
    <link rel="stylesheet" href="{{ voyager_asset('css/app.css') }}">

    <!-- fancy tree jquery/css-->
    @yield('css')


    @if(config('voyager.multilingual.rtl'))
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-rtl/3.4.0/css/bootstrap-rtl.css">
        <link rel="stylesheet" href="{{ voyager_asset('css/rtl.css') }}">
    @endif

    <!-- Few Dynamic Styles -->
    <style type="text/css">
        .voyager .side-menu .navbar-header {
            background:#FFFFFF;
            border-color:#FFFFFF;
            {{--background:{{ config('voyager.primary_color','#22A7F0') }};--}}
            {{--border-color:{{ config('voyager.primary_color','#22A7F0') }};--}}
        }
        .widget .btn-primary{
            border-color:{{ config('voyager.primary_color','#22A7F0') }};
        }
        .widget .btn-primary:focus, .widget .btn-primary:hover, .widget .btn-primary:active, .widget .btn-primary.active, .widget .btn-primary:active:focus{
            background:{{ config('voyager.primary_color','#22A7F0') }};
        }
        .voyager .breadcrumb a{
            color:{{ config('voyager.primary_color','#22A7F0') }};
        }
        .app-container .side-menu .panel.widget h5 {
            float: left;
            display: block;
            position: absolute;
            top: 0px;
            width: 180px;
            text-align: left;
            opacity: 0;
            transition: opacity .3s ease;
            margin-top: 17px;
            left: 68px;
            overflow: hidden;
            height: 29px;
        }
        .app-container .side-menu .panel.widget h6 {
            float: left;
            display: block;
            position: absolute;
            top: 15px;
            width: 180px;
            text-align: left;
            opacity: 0;
            transition: opacity .3s ease;
            margin-top: 17px;
            left: 68px;
            overflow: hidden;
            height: 29px;
        }
        .app-container.expanded .panel.widget h5 {
            opacity: 1;
        }
        .app-container.expanded .panel.widget h6 {
            opacity: 1;
        }
        .app-container .side-menu:hover .panel.widget h5 {
            opacity: 1;
        }
        .app-container .side-menu:hover .panel.widget h6 {
            opacity: 1;
        }
    </style>

    @if(!empty(config('voyager.additional_css')))<!-- Additional CSS -->
        @foreach(config('voyager.additional_css') as $css)<link rel="stylesheet" type="text/css" href="{{ asset($css) }}">@endforeach
    @endif

    @yield('head')
</head>

<body class="voyager @if(isset($dataType) && isset($dataType->slug)){{ $dataType->slug }}@endif">

<div id="voyager-loader">
    <?php $admin_loader_img = Voyager::setting('admin.loader', ''); ?>
    @if($admin_loader_img == '')
        <img src="{{ voyager_asset('images/logo-icon.png') }}" alt="Voyager Loader">
    @else
        <img src="{{ Voyager::image($admin_loader_img) }}" alt="Voyager Loader">
    @endif
</div>

<?php
if (starts_with(Auth::user()->avatar, 'http://') || starts_with(Auth::user()->avatar, 'https://')) {
    $user_avatar = Auth::user()->avatar;
} else {
    $user_avatar = Voyager::image(Auth::user()->avatar);
}
?>

<div class="app-container">
    <div class="fadetoblack visible-xs"></div>
    <div class="row content-container">
        @include('voyager::dashboard.navbar')
        @include('voyager::dashboard.sidebar')
        <script>
            (function(){
                    var appContainer = document.querySelector('.app-container'),
                        sidebar = appContainer.querySelector('.side-menu'),
                        navbar = appContainer.querySelector('nav.navbar.navbar-top'),
                        loader = document.getElementById('voyager-loader'),
                        hamburgerMenu = document.querySelector('.hamburger'),
                        sidebarTransition = sidebar.style.transition,
                        navbarTransition = navbar.style.transition,
                        containerTransition = appContainer.style.transition;

                    sidebar.style.WebkitTransition = sidebar.style.MozTransition = sidebar.style.transition =
                    appContainer.style.WebkitTransition = appContainer.style.MozTransition = appContainer.style.transition =
                    navbar.style.WebkitTransition = navbar.style.MozTransition = navbar.style.transition = 'none';

                    if (window.localStorage && window.localStorage['voyager.stickySidebar'] == 'true') {
                        appContainer.className += ' expanded no-animation';
                        loader.style.left = (sidebar.clientWidth/2)+'px';
                        hamburgerMenu.className += ' is-active no-animation';
                    }

                   navbar.style.WebkitTransition = navbar.style.MozTransition = navbar.style.transition = navbarTransition;
                   sidebar.style.WebkitTransition = sidebar.style.MozTransition = sidebar.style.transition = sidebarTransition;
                   appContainer.style.WebkitTransition = appContainer.style.MozTransition = appContainer.style.transition = containerTransition;
            })();
        </script>
        <!-- Main Content -->
        <div class="container-fluid">
            <div class="side-body padding-top">
                @yield('page_header')
                <div id="voyager-notifications"></div>
                @yield('content')
            </div>
        </div>
    </div>
</div>
@include('voyager::partials.app-footer')

@yield('javascript')

<!-- Javascript Libs -->
<script type="text/javascript" src="{{ voyager_asset('js/app.js') }}"></script>

<!-- <script type="text/javascript" src="{{ voyager_asset('js/app.js') }}"></script> -->


<script>
    @if(Session::has('alerts'))
        let alerts = {!! json_encode(Session::get('alerts')) !!};
        helpers.displayAlerts(alerts, toastr);
    @endif

    @if(Session::has('message'))

    // TODO: change Controllers to use AlertsMessages trait... then remove this
    var alertType = {!! json_encode(Session::get('alert-type', 'info')) !!};
    var alertMessage = {!! json_encode(Session::get('message')) !!};
    var alerter = toastr[alertType];

    if (alerter) {
        alerter(alertMessage);
    } else {
        toastr.error("toastr alert-type " + alertType + " is unknown");
    }

    @endif
</script>


<!-- Javascript Libs -->
<!-- <script type="text/javascript" src="{{ voyager_asset('js/app.js') }}"></script> -->


@if(!empty(config('voyager.additional_js')))<!-- Additional Javascript -->
    @foreach(config('voyager.additional_js') as $js)<script type="text/javascript" src="{{ asset($js) }}"></script>@endforeach
@endif

</body>
</html>

--Fancy Tree Blade --

Extends Master Blade
@section('content')
--fancy tree code--
@stop
@section('javascript)
<script type="text/javascript">
$(function(){
        // using default options
        $("#tree").fancytree({
            extensions: ["dnd"],
            checkbox: false,
            icon: false,
            generateIds: true,
            dnd: {
                autoExpandMS: 400,
                focusOnClick: true,
                preventVoidMoves: true, // Prevent dropping nodes 'before self', etc.
                preventRecursiveMoves: true, // Prevent dropping nodes on own descendants
                dragStart: function(node, data) {
                /** This function MUST be defined to enable dragging for the tree.
                 *  Return false to cancel dragging of node.
                 */
                return true;
                },
                dragEnter: function(node, data) {
                /** data.otherNode may be null for non-fancytree droppables.
                 *  Return false to disallow dropping on node. In this case
                 *  dragOver and dragLeave are not called.
                 *  Return 'over', 'before, or 'after' to force a hitMode.
                 *  Return ['before', 'after'] to restrict available hitModes.
                 *  Any other return value will calc the hitMode from the cursor position.
                 */
                // Prevent dropping a parent below another parent (only sort
                // nodes under the same parent)
        /*           if(node.parent !== data.otherNode.parent){
                    return false;
                }
                // Don't allow dropping *over* a node (would create a child)
                return ["before", "after"];
        */
                return true;
                },
                dragDrop: function(node, data) {
                /** This function MUST be defined to enable dropping of items on
                 *  the tree.
                 */
                data.otherNode.moveTo(node, data.hitMode);
                }
            },
}

<script src="//code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="//code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
    <script src="/src/jquery-ui-dependencies/jquery.fancytree.ui-deps.js" type="text/javascript"></script>
    <script src="/src/jquery.fancytree.js" type="text/javascript"></script>
    <script src="/src/jquery.fancytree.dnd.js" type="text/javascript"></script>
    <script src="/src/jquery.fancytree.edit.js" type="text/javascript"></script>
Related