0

I am a newbie to Angular JS and I have been trying to build a demo ASP .Net MVC Application using it. Putting it simply, I have a list of countries each having a list of 3 associated cities

var myModule = angular
                .module("myModule", [])
                .controller("myController", function ($scope) {
                    var countries = [
                        {
                            name: "UK",
                            cities: [
                                    {name: "London"},
                                    {name: "Birmingham" },
                                    {name: "Manchestar" }
                            ],
                            flag: "/Images/UK.gif",
                            foundationDate: new Date("May 20,1916"),
                            likes: 0,
                            dislikes: 0
                        },
                        {   
                            name: "USA",
                            cities: [
                                    {name: "Los Angeles"},
                                    {name: "Houston"},
                                    {name: "Florida"},
                            ],
                            flag: "/Images/USA.png",
                            foundationDate: new Date("August 01,1887"),
                            likes: 0,
                            dislikes: 0
                        },
                        {
                            name: "INDIA",
                            cities: [
                                    { name: "Hyderabad" },
                                    { name: "Mumbai" },
                                    { name: "Kolkata" },
                            ],
                            flag: "/Images/INDIA.jpg",
                            foundationDate: new Date("August 15,1947"),
                            likes: 0,
                            dislikes: 0
                        }
                    ];

and in my view page, I am displaying them all.

I have a Search text box that can search across Country Names and also across the city names .For that there is the $scope.search function.

                    $scope.search = function (country) {
                        if ($scope.searchText == undefined) {
                            return true; 
                        }
                        angular.forEach(country.cities, function (item) {
                            if (item.name.toLowerCase().indexOf($scope.searchText.toLowerCase()) != -1) 
                                return true;
                        });
                        return false;
                    }
});

Here is the view code

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"  ng-app="myModule">
<head>
    <title></title>
    <script src="Scripts/angular.js"></script>
    <script src="Scripts/MyModuleScript.js"></script>
    <style>
        table, th, td {
            border-collapse:collapse;
            border:1px solid black;
        }
        th, td {
            text-align:center;
            vertical-align:middle;
        }
    </style>
</head>
<body  ng-controller="myController">
  Rows to display : <input type="number" step="1" min="0" max="3" ng-model="rowLimit" />
    <br />
    <br />
    Search : <input type="text" placeholder="Search Countries & Cities" ng-model="searchText" />
    <br />
    <br />
    <div>
        <table style="padding:5px">
            <thead>
                <tr>
                    <th>Country</th>
                    <th>City 1</th>
                    <th>City 2</th>
                    <th>City 3</th>
                    <th>Flag</th>
                    <th>Foundation Date</th>
                    <th>Likes</th>
                    <th>Dislikes</th>
                    <th colspan="2">Actions</th>
                </tr>
            </thead>
            <tbody>
                <tr ng-repeat="country in countries | orderBy : '+foundationDate' | limitTo: rowLimit | filter : search">
                    <td>{{ country.name }}</td>
                    <td ng-repeat="city in country.cities">
                        {{ city.name }}
                    </td>
                    <td><img ng-src="{{ country.flag }}" style="height:100px;width:150px"/> </td>
                    <td>{{ country.foundationDate | date : "dd/MM/yyyy" }}</td>
                    <td>{{ country.likes }}</td>
                    <td>{{ country.dislikes }}</td>
                    <td><input type="button" value="Like" ng-click="incrementLikes(country)"</td>
                    <td><input type="button" value="Dislike" ng-click="incrementDislikes(country)"</td>
                </tr>
            </tbody>
        </table>
    </div>
</body>
</html>

But the search function is not working properly . Here is a screenshot of the html view page

enter image description here

I know somehow the logic is not at all devised correctly for the search function. Could someone please help me through ?

6
  • filter: searchText, instead of search. It matches up to your ng-model Commented Mar 29, 2016 at 19:13
  • Actually no. I have search across the country name and the cities names also associated with the country. So $scope.search is there . Commented Mar 29, 2016 at 19:15
  • also, Florida is not a city, but a state Commented Mar 29, 2016 at 19:15
  • Technically there is a Florida, Ohio, but yeah, it primarily is known as a state. Commented Mar 29, 2016 at 19:16
  • Sorry my bad . It was not at all intentional. Commented Mar 29, 2016 at 19:17

2 Answers 2

2

Assuming that the search function is being called you are only returning a boolean value for the filter to search the object. Since the model object you are passing to the filter does not have a boolean property to search against that would yield no results. What you want to do is to filter the string value of searchText against the filtered model.

If you only want to search explicitly for city name of the model I would suggest passing the property name to the filter.

<tr ng-repeat="country in countries | filter : {cities.name : searchText} : true | limitTo: rowLimit | orderBy : '+foundationDate'">

To search for country and city names I would suggest creating a custom filter and pass the filter the searchText model.

[ModuleNameHere].filter('filterCountryCityNames', function() {
    return function(obj, searchValue) {
        var options = [];           

        if (searchValue === undefined || searchValue === null)
            return obj;
        searchValue = searchValue.toLowerCase();
        angular.forEach(obj, function(value) {

        var foundCity = value.cities.map(function(r) {
            return r.name.toLowerCase().indexOf(searchValue) >= 0;
        }).sort().pop();

        if (value.name.toLowerCase().indexOf(searchValue) >= 0 || foundCity) {
            options.push(value);
        }
    });
        return options;
    }
})

You would use this like the built in Angular filter in the html markup.

<tr ng-repeat="country in countries | filterCountryCityNames : searchText | orderBy : '+foundationDate'| limitTo: rowLimit ">
Sign up to request clarification or add additional context in comments.

5 Comments

He needs both city and state though.
@Vance Rivera I totally understand what you are trying to achieve but just a bit curious what is the first parameter obj is for ? You are sending only the searchText parameter right ? Then why the first parameter ?
The first param is the model obj you are trying to filter. Angular automatically passes the model through to the filter. Anything after the colon is an extra parameter you are passing but the filter will inherently be passed the obj you want to filter.
@LukaJacobowitz, you are correct luka but your answer did not search the country either. new update looks like it works. I updated that for you though.
The plus side is that you can reuse this filter on any of your views in this application.
1

I think you're problem might just be the anonymous function in your forEach. It will return true to the anonymous function but not to the outer function $scope.search(). So instead of returning directly from a function mutate a variable and return that at the end, or use reduce, instead of forEach.

 $scope.search = function (country) {
                    if ($scope.searchText == undefined) {
                        return true; 
                    }

                    if (country.name.toLowerCase().indexOf($scope.searchText.toLowerCase()) != -1)
                        return true;

                    var found = false;
                    angular.forEach(country.cities, function (item) {
                        if (item.name.toLowerCase().indexOf($scope.searchText.toLowerCase()) != -1) 
                            found = true;
                    });
                    return found;
                }

5 Comments

But that would search across all the columns right ?
Could you please help with the same. I do not have much idea on that . I am just starting on it.
That helped. And yes thanks for pointing out where I made the error.
Sorry it took so long!
No problem Sir :) Really appreciate your effort.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.