Google-like search box with an AngularJS directive

Ela picture Ela · Feb 13, 2015 · Viewed 18.2k times · Source

I am writing an application which UI wise is almost exactly like Google. I arrive at landing page I have a search box which on submit directs you to results page. Here you have the same search box and other directives in which you can switch between modes: eg. web, image. Currently I have:

on landing page: form with action="results.html" which passes the parameters in the url.

<form name="search" role="form" action="results.html">
    <div class="input-group input-group-search">

        <input type="text" class="form-control"  ng-model="blab" name="q" required>
        <span class="input-group-addon">
            <button class="btn-search" ng-disabled="search.$invalid">
                <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
            </button>
        </span>
        <input type="hidden" name="mode" value="web"/>
    </div>
</form>

on results I am just using ng-submit="search()" and ng-model on the input. The search box is within the searchController.

What is the right method for doing this as a custom directive, with the following behaviour:

  • On landing page on submit direct to results page
  • On results page make a search without reloading the page and change the location to the right parameters?

Answer

Pixxl picture Pixxl · Feb 13, 2015

I run something similar on my site currently. I however did not wrap my search into a directive because it is it's own page.

For how I have it setup, I have a search page site.com/search (which would be your landing page for example) That page has its own controller/view SearchController. On the same page lies a separate container which can essentially list items that are inside an array. Finally, the entire page has an ApplicationController.

Now, the SearchController and ApplicationController are obviously separate and therefore cannot access each-other's properties or methods. What they can do however, is either share a factory/service or broadcast information. For the simplicity of this example we will have them share a service called SearchService.

If you still wish to use a directive, you can easily turn the SearchController into a directive and utilize the same concept for yourself.

Basic Plunkr Example Here


SearchService

The SearchService will contain useful properties and methods for searching, but all you need right now is just a simple Array to contain a list of search results.

myApp.factory('SearchService', function() {
    var SearchService;
    SearchService = {};

    // The array that will contain search results
    SearchService.arrSearchResults = [];

    return SearchService;
  }
);

ApplicationController

The ApplicationController scope will have a reference to SearchService so that you can use ng-repeat and list out the actual contents of the search results.

myApp.controller('ApplicationController', [
  '$scope', 'SearchService', function($scope, SearchService) {

      // Create a reference to the SearchService and add it to the 
      // $scope so it will be available on the page
      $scope.searchService = SearchService;

  }
]);

SearchController

The SearchController scope will also have a reference to SearchService so that it can modify the SearchService.arrSearchResults array, thus altering what will be displayed on the page. It will also contain methods to interact with the form.

It will also alter the URL location when a search is executed.

myApp.controller('SearchController', ['$scope', 'SearchService', '$http', '$location', function($scope, SearchService, $http, $location) {

    // Your search input
    $scope.blab = "";

    // Your search function
    $scope.search = function() {

    // Make sure blab has content (always good to double check)
    if($scope.blab != "") {

        // Alter URL to show new request
        $location.search('q', $scope.blab);

        // Make a GET request to your URL that will 
        // return data for you to populate
        $http.get('/someUrl').
            success(function(data, status, headers, config) {

                // this callback will be called asynchronously
                // when the response is available

                alert("Search results found!");

                // Assuming the data returned is a list of items
                // or object items
                // (i.e. [ "Search Result1", "Search Result2", ... ]
                SearchService.arrSearchResults = data;

            }).
            error(function(data, status, headers, config) {

                // called asynchronously if an error occurs
                // or server returns response with an error status.

                alert("Something failed! No results found!");

                // Empty the array of search results 
                // to show no results
                SearchService.arrSearchResults = [];
            });
    };
}]);

The Page

<!doctype html>
<head>
    <title>Search Example Page</title>

    <!-- Insert Angular.JS source files here -->
</head>
<body ng-controller="ApplicationController" ng-app="myApp">

    <!-- ngView -->
    <div role="main" data-ng-view>

    </div>

    <!-- Search Results -->
    <div ng-repeat="searchItem in searchService.arrSearchResults">

        <p style="border-bottom: 2px solid #000">Search Result: <br/>{{searchItem}}</p>

    </div>

</body>
</html>

Tabs

For switching the type of search result (web, image, etc) you can create a variable within the SearchService that controls the state of the search, and thus what type of search to run.

SearchService.typeOfSearch = "web"; This sets the state to web and thus can be interacted within the page and app.

You can then have various ng-repeat throughout the page all showing results for different states:

<!-- Search Results - Web -->
<div ng-if="searchService.typeOfSearch='web'" ng-repeat="searchItem in searchService.arrSearchResults">

    <p style="border-bottom: 2px solid blue">Search Result: <br/>{{searchItem}}</p>

</div>


<!-- Search Results - Image -->
<div ng-if="searchService.typeOfSearch='image'" ng-repeat="searchItem in searchService.arrSearchResults">

    <p style="border-bottom: 2px solid red">Search Result: <br/>{{searchItem}}</p>

</div>

I have updated the Plunkr to demonstrate.