2

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);
                }
            },
}
Yeo Bryan
  • 331
  • 4
  • 24
  • My guess is you have more script tags higher up the page that aren't shown....and are loading jQuery.js twice – charlietfl Mar 29 '21 at 04:10
  • yes i do have another javascript tag requiring jquery before the javascript section in the fancy tree file is yielded. How would you suggest i go about fixing this? – Yeo Bryan Mar 29 '21 at 05:54
  • bring this ` ` just before ` – Basharmal Mar 29 '21 at 06:26
  • it returns an error where jquery.js:3850 Uncaught TypeError: $(...).fancytree is not a function. – Yeo Bryan Mar 29 '21 at 06:29
  • I have brought the `js` file in at the bottom and also `@stop` the javascript section in `Fancy Tree Blade` try this – Basharmal Mar 29 '21 at 06:38
  • do you mean to move to the fancy tree blade javascript section? i tried that it still doesnt recognize fancy tree as a function – Yeo Bryan Mar 29 '21 at 06:44
  • yeild your `@yield('javascript')` ritght after `@include('voyager::partials.app-footer')` and check `@include('voyager::partials.app-footer')` and look for any kind of js file – Basharmal Mar 29 '21 at 06:57
  • tried doing that as well but to not avail – Yeo Bryan Mar 29 '21 at 07:02
  • Only load jQuery.js once. The second is wiping out the first and any plugin methods you registered before the second one loads – charlietfl Mar 29 '21 at 10:42

1 Answers1

0

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>
Basharmal
  • 1,313
  • 10
  • 30
  • i tried changing the jquery script to what you suggested however i am still presented with the same error where the perfect scroll bar is not a function. I honestly have no idea what i am missing/doing wrong. i tried copy pasting the code from the fancy tree DND demo onto a seperate php project which works fine however it just has issues running on this laravel project which i need it for. @Basharmal – Yeo Bryan Mar 29 '21 at 05:40
  • show me the master blade what you have their sometimes if the app.js file is in the above it will show the same error the app.js file should be at the bottom of – Basharmal Mar 29 '21 at 05:42
  • @include('voyager::partials.app-footer') I do have this set of code in the master file within the body tag BEFORE where the fancy tree content is yielded – Yeo Bryan Mar 29 '21 at 05:52
  • so that's why you are getting the error so take the js/app.js file after the fancy it will solve the issue – Basharmal Mar 29 '21 at 05:54
  • doing that results in an error where .fancytree isnt recognized as a function. – Yeo Bryan Mar 29 '21 at 06:01
  • In the master file i currently have yield('javascript') followed by – Yeo Bryan Mar 29 '21 at 06:02
  • whereas in the section('javascript') i have – Yeo Bryan Mar 29 '21 at 06:02
  • can you post your master.blade – Basharmal Mar 29 '21 at 06:08
  • posted the entire master blade file – Yeo Bryan Mar 29 '21 at 06:17