Search code examples
jquerylaravelautocompletebootstrap-typeahead

Cannot get bootstrap typeahead to work with Laravel blade template -> `TypeError: $(...).typeahead is not a function`


I have a form view, "addMachine.blade.php", which is currently a pop-up modal form accessed through the navigation bar. I also setup Typeahead autocomplete in the form.

These are the routes for the addMachine view and autocompleteUser

Route::get('/machine/add', 'Machine\AddMachineController@index')->name('addMachine');
Route::get('/machine/add/operator_autocomplete', [
        'as'=>'autocompleteUser',
        'uses'=>'Machine\AddMachineController@autocompleteUser']);

Typeahead autocomplete works when I access my website by going to http://localhost/machine/add.

However, when I try to access the form through the navigation bar and have it open as a pop-up modal (which is what I want it to do), I get the following error from Chrome's console: TypeError: $(...).typeahead is not a function. I have tried moving the typeahead and jquery links to my app.blade.php but I still get the same error.

This is the code for my form view, "addMachine.blade.php".

-- removed code, see edit below for new addMachine.blade.php code --

This is the function from my AddMachineController which handles the autocomplete

/**
 * Handle a autocomplete user request.
 *
 * @return \Illuminate\Http\Response
 */
public function autocompleteUser(Request $request)
{
    $data = User::where("name", "LIKE", "%{$request->input('query')}%")
        ->orWhere("id", "LIKE", "%{$request->input('query')}%")
        ->orderBy('name')
        ->take(5)
        ->get();
    return response()->json($data);
}

This is my app.blade.php

-- removes code, see edit below for new app.blade.php code --

And finally, this is my navigationBar.blade.php

<nav class="navbar navbar-inverse">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false"
                aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
        </button>
            <a class="navbar-brand" href="/home">SEI</a>
        </div>

        @if (!Auth::guest())
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
            </ul>
            <ul class="nav navbar-nav navbar-right">

                <-- Some more links -->

                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
                                        Machine <span class="caret"></span>
                                    </a>
                    <ul class="dropdown-menu" role="menu">
                        @if (Auth::user()->isMQE())
                        <li><a href="{{ route('addMachine') }}" data-toggle="modal" data-target="#addMachinePopupModal">Add New Machine</a></li>
                        @endif
                    </ul>
                </li>

                <-- Some more links -->

            </ul>
        </div>
        <div id="addMachinePopupModal" class="modal fade">
            <div class="modal-dialog">
                <div class="modal-content">
                </div>
            </div>
        </div>
        @endif
    </div>
</nav>

Any help is appreciated. Also, I am not sure if this is the best way to have a popup modal form be displayed by a navigation bar. If anyone has a better solution for anything I am doing, please feel free to share. I am new to web development and love to learn :)

Note: Using Laravel 5.4 and Php7

EDIT: I can see my code for the addMachine view when I use the "Inspect" tool in Chrome. However, when I use "View Page Source" it isn't there.

I am guessing now that that is the reason behind the typeahead error and I am not displaying pop-up modal forms from my navigation bar in a smart way. Any suggestions?

EDIT2: It does seem like the pop-up modal is the problem. I changed my app.blade.php to the following code

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

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

    <title>{{ config('app.name', 'SEI-MQE') }}</title>

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

<body>
    <div id="app">
        @include('include.navigationBar')

        @include('include.flashMessage')

        @yield('content')
    </div>

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

</html>

Then I changed my addMachine.blade.php to this

@extends('layouts.app')

@section('content')
<div class="container">
   <div class="row">
        <div class="col-md-8 col-md-offset-2">    
            <div class="panel panel-default">
                <div class="panel-heading text-center">Add New Machine</div>

                <div class="panel-body">
                    <form class="form-horizontal" method="POST" action="{{ route('addMachine') }}">
                        {{ csrf_field() }}
                        <div class="form-group{{ $errors->has('name') ? ' has-error' : '' }}">
                            <label for="name" class="col-md-4 control-label">Name</label>

                            <div class="col-md-6">
                                <input id="name" type="text" class="form-control" name="name" value="{{ old('name') }}" required autofocus>
                                @if ($errors->has('name'))
                                <span class="help-block">
                                    <strong>{{ $errors->first('name') }}</strong>
                                </span>
                                @endif
                            </div>
                        </div>

                        <div class="form-group{{ $errors->has('part_id') ? ' has-error' : '' }}">
                            <label for="part_id" class="col-md-4 control-label">Part Id#</label>

                            <div class="col-md-6">
                                <input class="typeahead form-control" autocomplete="off" type="text" id="part_id" name="part_id" value="{{ old('part_id') }}">
                                @if ($errors->has('part_id'))
                                <span class="help-block">
                                    <strong>{{ $errors->first('part_id') }}</strong>
                                </span> 
                                @endif
                            </div>
                        </div>

                        <div class="form-group">
                            <label for="department_id" class="col-md-4 control-label">Department</label>
                            <div class="col-md-6">
                                <p style="margin-top: 7px;">
                                    <select name="department_id" id="department_id" type="department_id">
                                    <?php
                                        foreach ($departments as $department) {
                                            echo "<option value=".$department['id'].">".$department['name']."</option>";
                                        } 
                                    ?>
                                    </select>
                            </div>
                        </div>  

                        <div class="form-group">
                            <label for="type_id" class="col-md-4 control-label">Machine Type</label>
                            <div class="col-md-6">
                                <p style="margin-top: 7px;">
                                    <select name="type_id" id="type_id" type="type_id">
                                    <?php
                                        foreach ($types as $type) {
                                            echo "<option value=".$type['id'].">".$type['name']."</option>";
                                        } 
                                    ?>
                                    </select>
                            </div>
                        </div>

                        <div class="form-group">
                            <label for="production_status_id" class="col-md-4 control-label">Production Status</label>
                            <div class="col-md-6">
                                <p style="margin-top: 7px;">
                                    <select name="production_status_id" id="production_status_id" type="production_status_id">
                                    <?php
                                        foreach ($productionStatuses as $productionStatus) {
                                            echo "<option value=".$productionStatus['id'].">".$productionStatus['name']."</option>";
                                        } 
                                    ?>
                                    </select>
                            </div>
                        </div>

                        <div class="form-group">
                            <div class="col-md-6 col-md-offset-4">
                                <button type="submit" class="btn btn-primary">
                                            Add Machine
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

@section('scripts')
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-3-typeahead/4.0.2/bootstrap3-typeahead.min.js"></script>
<script type="text/javascript">
    var autocompletePartPath = "{{ route('autocompletePart') }}";
    $('#part_id').typeahead( {
        source:  function (query, process) {
            return $.get(autocompletePartPath, { query: query }, function (data) {
                return process(data);
            });
        },
        displayText: function (item) {
            return `${item.id} - ${item.name} ${item.revision}`;
        },
        afterSelect: function (item) {
            $('#part_id').val(item.id);
        },
        fitToElement: true
    });
</script>
@endsection

I changed my navigation bar html to open the addMachine view normally rather than by pop-up modal and the typeahead works now.

Can anyone explain to me how I can make my addMachine view a pop-up modal form that opens from the navigation bar AND has typeahead working?


Solution

  • Finally figured it out. Solution for my problem was just to move the jquery and typeahead source scripts from addMachine.blade.php into app.blade.php like so:

    <!DOCTYPE html>
    <html>
    
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
        <!-- CSRF Token -->
        <meta name="csrf-token" content="{{ csrf_token() }}">
    
        <title>{{ config('app.name', 'SEI-MQE') }}</title>
    
        <!-- Styles -->
        <link href="{{ asset('css/app.css') }}" rel="stylesheet">
    </head>
    
    <body>
        <div id="app">
            @include('include.navigationBar')
    
            @include('include.flashMessage')
    
            @yield('content')
        </div>
    
        <!-- Scripts -->
        <script src="{{ asset('js/app.js') }}"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-3-typeahead/4.0.2/bootstrap3-typeahead.min.js"></script>
    </body>
    
    </html>
    

    Solution is simple so I feel dumb, but for anyone who wants an example of having the navigation bar to open a pop-up modal form with typeahead autocomplete, read my solution in the question.