AngularJS - Ordering and Parsing data in a ng-repe

2019-08-01 22:15发布

问题:

Im trying to write a fairly complex ng-repeat that potentially uses filtering and parseing.

What i am first doing is ng-repeating a table heading of 12 months from the current month.

I am then displaying two rows - Searches with impact and Searches with NO impact

What i am trying to do is loop through my JSON file that contains my search data, and counts how many Searches with Impact and Search with No Impact, have been carried out for that particular month based on the SearchedOn field.

The logic behind understanding Searches with Impact and Search with No Impact is based on the IsIncludedInSearchImpactCalculation field being true or false.

What also needs to happen is that if my JSON file contains element of data that is greater than 12months old (based on the SearchedOn field), then this should be not included in the calculation.

HTML:

<div ng-app="test">
    <div ng-controller="Ctrl">
        <table>
            <thead>
                <tr>
                    <th></th>
                    <th ng-repeat="currMonth in months">{{currMonth}}</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <th>Searches with impact</th>
                    <td></td>
                    <td>3</td>
                    <td></td>
                    <td>1</td>
                    <td>1</td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td>3</td>
                    <td>1</td>
                    <td></td>
                    <td>1</td>
                </tr>
                <tr>
                    <th>Searches with NO impact</th>
                    <td>2</td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td>1</td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
            </tbody>
        </table>
        <br>
        <p>Searches with impact:</p>
        <li ng-repeat="searchedOn in Searches | filter:{IsIncludedInSearchImpactCalculation:true}">{{searchedOn.SearchedOn}}</li>
        <br>
        <p>Searches with NO impact:</p>
        <li ng-repeat="searchedOn in Searches | filter:{IsIncludedInSearchImpactCalculation:false}">{{searchedOn.SearchedOn}}</li>
    </div>
</div>

JS:

angular.module('test', []).controller('Ctrl', function ($scope) {
    var date = new Date();
    var months = [],
        monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
    for (var i = 0; i < 12; i++) {
        months.push(monthNames[date.getMonth()] + ' ' + date.getFullYear());

        // Subtract a month each time
        date.setMonth(date.getMonth() - 1);
    }
    $scope.months = months;



    $scope.Searches = [{
        "SearchedOn": "19/04/2014",
            "IsIncludedInSearchImpactCalculation": true
    }, {
        "SearchedOn": "18/04/2014",
            "IsIncludedInSearchImpactCalculation": true
    }, {
        "SearchedOn": "01/05/2014",
            "IsIncludedInSearchImpactCalculation": false
    }, {
        "SearchedOn": "21/05/2014",
            "IsIncludedInSearchImpactCalculation": false
    }, {
        "SearchedOn": "20/07/2013",
            "IsIncludedInSearchImpactCalculation": true
    }, {
        "SearchedOn": "07/01/2014",
            "IsIncludedInSearchImpactCalculation": true
    }, {
        "SearchedOn": "08/12/2013",
            "IsIncludedInSearchImpactCalculation": false
    }, {
        "SearchedOn": "24/02/2014",
            "IsIncludedInSearchImpactCalculation": true
    }, {
        "SearchedOn": "30/06/2013",
            "IsIncludedInSearchImpactCalculation": true
    }, {
        "SearchedOn": "28/06/2014",
            "IsIncludedInSearchImpactCalculation": true
    }, {
        "SearchedOn": "14/08/2013",
            "IsIncludedInSearchImpactCalculation": true
    }, {
        "SearchedOn": "11/04/2014",
            "IsIncludedInSearchImpactCalculation": true
    }, {
        "SearchedOn": "13/08/2013",
            "IsIncludedInSearchImpactCalculation": true
    }, {
        "SearchedOn": "03/08/2013",
            "IsIncludedInSearchImpactCalculation": true
    }, {
        "SearchedOn": "20/01/2011",
            "IsIncludedInSearchImpactCalculation": true
    }];
});

Here's my fiddle: http://jsfiddle.net/oampz/j9LBe/

In my fiddle, i have manually hard coded what the table should look like based on the lists.


UPDATE:

Adding:

<td ng-repeat="item in filtered = (Searches | filter:{IsIncludedInSearchImpactCalculation:true})"></td>
                <td>Filtered list has {{filtered.length}} items</td>

Gets me the total number of items in a row for Searches with impact or Searches with NO impact see fiddle: http://jsfiddle.net/oampz/j9LBe/1/

Now all that is needed is to group by month based on the previous ng-repeat:

 <th ng-repeat="currMonth in months">{{currMonth}}</th>

回答1:

Fiddle Here

It simply boils down to this filter:

$scope.searchFilter = function(included,date){
    return function(search){
        if(search.IsIncludedInSearchImpactCalculation!==included) return false;

        //needed to parse and use date constructor on your json's dates
        var dateParts = search.SearchedOn.split("/");
        var searchedOn = new Date(dateParts[2],dateParts[1]-1,dateParts[0]);//months are 0-11

        if(date.month === searchedOn.getMonth() && date.year===searchedOn.getFullYear()) return true;
        else return false;
    };
}

and used it like this:

<th>Searches with impact</th>
      <td ng-repeat="currMonth in months">{{(Searches|filter:searchFilter(true,currMonth)).length}}</td>

or this:

<th>Searches with NO impact</th>
      <td ng-repeat="currMonth in months">{{(Searches|filter:searchFilter(false,currMonth)).length}}</td>

This basically does what you are asking in the title, based on the parent loop (ng-repeat of months) parses and filters data in inner loop(which is caused by | filter passing every element of array to a function.)

Basically we need to ng-repeat months again but this time for printing count of included and not included searches for each month. So our filter needs 3 parameters, first whether it's included or not, second the month in question and lastly the searches to check for matching months.

angular filters expect an array to filter and passes each item to the filter function. So we return a function that gets single search as a parameter, do our thing and return true or false depending on if it's matched our criteria or not.

As a result | filter returns a new array filled with ones returned true from the filter function(this way we can ng-repeat with filters etc). Since we have our matched array now, we can simply get it's length as count.

I know sounds kind of confusing but check the fiddle it's not as confusing as i explained.

Feel free to ask anything that is not clear.



回答2:

Try this out

script

Working Demo

var months = [],
        monthNames = [{ monthNo: 1, monthName: "Jan" },
                      { monthNo: 2, monthName: "Feb" },
                      { monthNo: 3, monthName: "Mar" },
                      { monthNo: 4, monthName: "Apr" },
                      { monthNo: 5, monthName: "May" },
                      { monthNo: 6, monthName: "Jun" },
                      { monthNo: 7, monthName: "Jul" },
                      { monthNo: 8, monthName: "Aug" },
                      { monthNo: 9, monthName: "Sep" },
                      { monthNo: 10, monthName: "Oct" },
                      { monthNo: 11, monthName: "Nov" },
                      { monthNo: 12, monthName: "Dec" }];

    for (var i = 0; i < 12; i++) {
        var obj = {};
        obj.month = monthNames[date.getMonth()];
        obj.year = date.getFullYear();
        months.push(obj);
        // Subtract a month each time
        date.setMonth(date.getMonth() - 1);
    }

html

<th ng-repeat="curr in months|orderBy:'month.monthNo'">{{curr.month.monthName}}  {{curr.year}}</th>


回答3:

Working Demo - JSFiddle

Script:

Retrieve the last 12 months and store them in an array. Notice we are storing an array of object literals (year, month, value).

      function getMonths() {
            var date = new Date();

            var months = [];

            for (var i = 0; i < 12; i++) {
                months.push({
                    year: date.getFullYear(),
                    month: date.getMonth()+1,
                    value: new Date(date.getFullYear(), date.getMonth(), 1)
                });

                // Subtract a month each time
                date.setMonth(date.getMonth() - 1);
            }

            return months;
        }

        $scope.months = getMonths();

Define Your Search Filters. These search filters will be used to narrow down the results by search impact, and group the results by month. The 'count' filter is the final filter that returns a count of the results.

Filter by Search Impact:

   app.filter('bySearchImpact', function() {
        return function(input, isIncludedInSearchImpact) {
            var output = [];
            for (var i in input) {
                if (input[i].IsIncludedInSearchImpactCalculation == isIncludedInSearchImpact) {
                    output.push(input[i]);
                }
            }

            return output;
        }
    });

Filter by Month:

    app.filter('byMonth', function () {

        function isSameMonth(date1, date2) {
            var month1 = date1.split('/')[1];
            var year1 = date1.split('/')[2];
            return parseInt(year1) == date2.year &&
                parseInt(month1) == date2.month;
        }
        return function (input, month) {

            var output = [];
            for (var i in input) {
                if (isSameMonth(input[i].SearchedOn, month)) {
                    output.push(input[i]);
                }
            }

            return output;
        };

    });

Count Filter:

    app.filter('count', function() {
        return function(input) {
            return input.length;
        };
    });

HTML:

Now that we have filters for filtering by SearchImpact and Month, as well as a filter for returning the count, we can apply them to get the final results:

Display Table Headers (notice how we make use of Angular's date filter)

 <th ng-repeat="month in months">{{month.value | date: 'MMM yyyy' }}</th>

Searches With Impact:

 <td ng-repeat="month in months">{{Searches | bySearchImpact:true | byMonth:month | count}}</td>

Searches With NO Impact:

 <td ng-repeat="month in months">{{Searches | bySearchImpact:false | byMonth:month | count }}</td>

Display Search Dates With Impact:

<li ng-repeat="searchedOn in Searches |  bySearchImpact:true">{{searchedOn.SearchedOn}}</li>

Display Search Dates With NO Impact:

<li ng-repeat="searchedOn in Searches | bySearchImpact:false">{{searchedOn.SearchedOn}}</li>