Global search box in angular

All we need is an easy explanation of the problem, so here it is.

I want to implement a search box that changes what it searches based on whichever controller is being used. If you are on the “posts” view it will search the posts api, if you are on the videos view, it searches the videos api. It seems the search box would need its own controller maybe. I’m pretty sure I need to inject a search service into all the model controllers but I’m not exactly sure how to change the url it searches or tie the input to the different controller scopes.

So any ideas how to have a global search box that changes where it searches based on whichever controller is making use of it and tying its state back into a changing view?

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

To make a resource call dynamic api i would first create two $resources that map to your two endpoints, posts and videos. Then put an ng-change event on your global search that calls a function in your base controller.

This function firsts need to figure out what api to search. Then make the appropriate api call. The important part is in the callback and i think this is what you are looking for.
In the callback you could $broadcast the resp data from your api query. Each of your controllers will be listening for an event with an $on function. The listeners will then populate the correct scope variable with the callback data.

Pseudo below.

Same html layout with ng-change

<html>

<body ng-controller="AppController">
    <form>
        <label>Search</label>
        <input ng-model="global.search" ng-change="apiSearch()" type="text" class="form-control" />
    </form>

    <div ui-view="posts">
        <div ng-controller="PostController">
            <p ng-repeat="post in posts | filter: global.search">{{ post.name }}</p>
        </div>

    </div>

    <div ui-view="videos">
        <div ng-controller="VideoController">
            <p ng-repeat="video in videos | filter: global.search">{{ video.name }}</p>
        </div>

    </div>

</body>

</html>

AppController

.controller('AppController', function ($scope, PostService, VideoService) {

    $scope.apiSearch = function() {

        // Determine what service to use. Could look at the current url. Could set value on scope
        // every time a controller is hit to know what your current controller is. If you need
        // help with this part let me know.

        var service = VideoService, eventName = 'video';
        if ($rootScope.currentController == 'PostController') {
            service = PostService;
            eventName = 'post';
        }

        // Make call to service, service is either PostService or VideoService, based on your logic above.
        // This is pseudo, i dont know what your call needs to look like.
        service.query({query: $scope.global.search}, function(resp) {

            // this is the callback you need to $broadcast the data to the child controllers
           $scope.$broadcast(eventName, resp);
        });


    }

})

Each of your child controllers that display the results.

.controller('PostController', function($scope) {

    // anytime an event is broadcasted with "post" as the key, $scope.posts will be populated with the
    // callback response from your search api.
    $scope.$on('post', function(event, data) {
        $scope.posts = data;
    });

})

.controller('VideoController', function($scope) {

    $scope.$on('video', function(event, data) {
        $scope.videos = data;
    });

})

Method 2

Client side filtering.

If you are not looking for anything to crazy that can be achieved in a super simple way for global search. I didnt even know if this would work so i just did a quick test and it does. Obviously this could be solved in a much more detailed and controlled way using services and injecting them where they are needed. But since i don’t know excatly what you are looking for i will provide this solution, if you like it, great accept it. If you don’t i could probably help you with service injection solution

Quick solution is to have an app wide contoller with $rootScope ng-model. Lets call it global.search.

    $rootScope.global = {
        search: ''
    };

For the app wide search input.

<form>
    <label>Search</label>
    <input ng-model="global.search" type="text" class="form-control" />
</form>

In separate partials you just need to filter data based on the global.search ng-model. Two examples

<p ng-repeat="post in posts | filter: global.search">{{ post.name }}</p>

Second template with different scope

<p ng-repeat="video in videos | filter: global.search">{{ video.name }}</p>

Note how they both implement | filter: global.search. Whenever global.search changes, any filters in the current view will be changed. So posts will be filtered on the posts view, and videos on the videos view. While still using the same global.search ng-model.

I tested this, it does work. If you need more detail explaining the setup and child controller hierarchy let me know. Here is a quick look at a full template

<html>

<body ng-controller="AppController">
    <form>
        <label>Search</label>
        <input ng-model="global.search" type="text" class="form-control" />
    </form>

    <div ui-view="posts">
        <div ng-controller="PostController">
            <p ng-repeat="post in posts | filter: global.search">{{ post.name }}</p>
        </div>

    </div>

    <div ui-view="videos">
        <div ng-controller="VideoController">
            <p ng-repeat="video in videos | filter: global.search">{{ video.name }}</p>
        </div>

    </div>

</body>

</html>

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply