4

I've got a question about filtering within a ng-repeat. But this isn't a simple question since I'm not really having a problem that needs a solution. In this case, I'm looking for the best possible answer that doesn't require a lot of code but still does the trick as to how I want it to. It will become more clear in my description.

a short introduction:
So I'm currently working on a shopping application. The shopping application has a connection with an existing PrestaShop webshop through the web service API. A rest API for PrestaShop. So after creating a customer section, products section, and a cart system, I would like to be able to update the "stock amount" of each product within the application. Many people would tell me: Well that's easy, just create a form with ng-repeat response data from the database and the possibility of updating the stock on let's say a button ng-click event.

Well I've done that but now the tricky part, and to explain I will provide some less important examples to make it more clear of what I'm asking:

The prestashop database
The prestashop database is a database that exists of over 250 tables. Among those tables are various connections through id's and attribute id's. Some simple examples are:

  1. The customer table has an id_address row which connects to the address table. Within this table, all the 'customersaddress info is placed. So far, let's say updating a customer I've created a combination of update thecustomertable with thecustomer_idbut also update theaddresstable where theaddress_idis equal to theid_addressfrom the selectedcustomerwithin thecustomer` table.

  2. Products also have some linked tables. Think about prices, taxes, sizes, and colors. The id_product_attribute is an attribute_id that could correspond to a color or size.

As explained above the prestashop database structure is very detailed which confines to using various filters for showing the wanted data.

Using the webservice
So for using the webservice I'm using a combination of angularJS, javascript, php, and JSON. The JSON is the actual data. by using $http.get(url, options) I'm able to get the data from the database. Then for showing the data I'm using response. A example is shown below:

$http.get('config/get/getCustomers.php', { cache: true }).then(function (response) {
    $scope.customers = response.data.customers.customer
});

using $scope.customers = response.data.customers.customer gives me the option to show the customer data by using, for example, a ng-repeat as in the example below

    $http.get('config/get/getCustomers.php', { cache: true }).then(function (response) {
        $scope.customers = response.data.customers.customer
    });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="row listrow" ng-repeat="customer in customers | orderBy: customer.id">
        <div class="col-lg-8">
            <p class="customer" ng-bind="customer.id + ' ' + customer.firstname + ' ' + customer.lastname">
      </p>
        </div>
</div>

In this case, the response would be empty since there is no database connection. To create a full example for testing here is some JSON data you could use from the customers table.

{"customers":{"customer":[{"id":"1","id_default_group":"0","id_lang":"0","newsletter_date_add":"0000-00-00 00:00:00","ip_registration_newsletter":{},"last_passwd_gen":"2017-10-30 09:27:03","secure_key":{},"deleted":"0","passwd":"$2y$10$eZvPTD9bkxwSfWN7qovrmee\/Den2TqZ5a6YjcGC\/lha3oU59MCjOO","lastname":"TestStefans","firstname":"John","email":"pub@prestashop.com","id_gender":"0","birthday":"0000-00-00","newsletter":"0","optin":"0","website":{},"company":{},"siret":{},"ape":{},"outstanding_allow_amount":"0.000000","show_public_prices":"0","id_risk":"0","max_payment_days":"0","active":"1","note":{},"is_guest":"0","id_shop":"1","id_shop_group":"1","date_add":"2017-04-26 14:01:31","date_upd":"2017-10-27 15:56:54","reset_password_token":{},"reset_password_validity":"0000-00-00 00:00:00","associations":{"groups":{"@attributes":{"nodeType":"group","api":"groups"},"group":{"id":"3"}}}},{"id":"2","id_default_group":"0","id_lang":"1","newsletter_date_add":"0000-00-00 00:00:00","ip_registration_newsletter":{},"last_passwd_gen":"2017-10-27 15:56:55","secure_key":{},"deleted":"0","passwd":"$2y$10$C8M6TIuzmZjP9kh06Hai8.XyuNG9WcSi9L34oXqAuidsJkoYog4WW","lastname":"Demoers","firstname":"Demoers","email":"demo@sdwebdesign.nl","id_gender":"0","birthday":"0000-00-00","newsletter":"0","optin":"0","website":{},"company":{},"siret":{},"ape":{},"outstanding_allow_amount":"0.000000","show_public_prices":"0","id_risk":"0","max_payment_days":"0","active":"1","note":{},"is_guest":"0","id_shop":"1","id_shop_group":"1","date_add":"2017-04-26 14:01:33","date_upd":"2017-10-27 15:56:55","reset_password_token":{},"reset_password_validity":"0000-00-00 00:00:00","associations":{"groups":{"@attributes":{"nodeType":"group","api":"groups"},"group":{"id":"3"}}}}]}}

So with this information here is my question:
While working on the application the following data is given within the stock_availables table.

The stock_availables table

So while using the $http.get() and the response functions I was able to create a form for updating the stock. But now take a look at the id_product_attribute. This row corresponds to a product_attribute. In this case a size or color. Now I would like to filter these results. In this case by using for example ng-if="id_product_attribute == 0" I'm able to filter in a way that only all with this id_attribute are shown. But I would like to show all of them. So also with id="1", id="2", etc. But there is where I'm wondering, is there a way to do this in a sufficient way that wouldn't require a ng-if for each id? Since there are around 50 different id_attribute's which each corresponds to a color or size. So is there a function that could get each different id and filter that in a sufficient way than using many ng-if clauses. Look at the following full code as an example.

myApp.controller('ProductsController', function($scope, $http){

    $http.get('config/get/getProducts.php', {cache: true}).then(function(response){
        $scope.products = response.data.products.product
    });
    // Stock check + view
    $http.get('config/get/getStock.php', {cache: true}).then(function (response) {
        $scope.stock_availables = response.data.stock_availables.stock_available
    });

// Update Stock
    $scope.updateStock = function(stock_available) {

        var stock_id = stock_available.id;
        var product_id = stock_available.id_product;
        var stock_quantity = stock_available.quantity;

        console.log(stock_id + product_id + stock_quantity);
        
        // Post to the database //
        
    };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="row listrow">
        <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
            <ng-form name="stock">
                <div ng-repeat="stock_available in stock_availables">
                    <div class="row full-width padding-tb-15" ng-if="stock_available.id_product == product.id  && stock_available.id_product_attribute == 0" ng-repeat="product in products">
                        <div class="col-lg-12 form-stock-group hidden">
                            <input title="id" type="text" name="stockId" class="form-control" aria-describedby="" ng-value="stock_available.id">
                        </div>
                        <div class="col-lg-4">
                            <h5 ng-bind="product.name.language"></h5>
                        </div>
                        <div class="col-lg-3">
                            <h5 ng-bind="product.ean13"></h5>
                        </div>
                        <div class="col-lg-2">
                            <h5 ng-bind="product.reference"></h5>
                        </div>
                        <div ng-if="stock_available.id_product == product.id && stock_available.id_product_attribute == 0" ng-repeat="stock_available in stock_availables" class="col-lg-1 text-center">
                            <input title="stock_id" type="text" name="stock_id" class="form-control hidden" aria-describedby=""  ng-model="stock_available.id" ng-value="stock_available.id">
                            <input title="stock_id_product" type="text" name="stock_id_product" class="form-control hidden" aria-describedby="" ng-model="stock_available.id_product" ng-value="stock_available.id_product">
                            <h5 title="quantity" name="stockQuantity" aria-describedby="" ng-model="stock_available.quantity" ng-bind="stock_available.quantity"></h5>
                        </div>
                        <div ng-if="stock_available.id_product == product.id && stock_available.id_product_attribute == 0" ng-repeat="stock_available in stock_availables" class="col-lg-1 text-center">
                            <input title="updateStockAmount" class="form-control form-control-stock" type="number">
                        </div>
                        <div class="col-lg-1 text-center">
                            <a ng-click="printStockLabel()">
                                <i class="fa fa-print" aria-hidden="true"></i>
                            </a>
                        </div>
                    </div>
                </div>
            </ng-form>
        </div>
    </div>

I trying to create the following view:

  1. Product-id 1: then all sizes, colors, etc.
  2. Product-id 2: then all sizes, colors, etc.

Simple design

Yes it is possible with writing 50 different ng-if statements but I'm asking if there is a better, easier and more sufficient way to do this?

A simple build of the dummy code can be found here

As always,

Thanks in advance!

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Deathstorm
  • 818
  • 11
  • 36
  • it would be helpful to provide a fiddle demonstrating your problem without the some dummy, easy to read data. – korteee Nov 21 '17 at 14:32
  • i've added a plunkr, @Korte – Deathstorm Nov 21 '17 at 15:32
  • There is a lot of information in this question that I don't think is relevant. And the plunkr doesn't work - there are console errors. Can you clarify precisely what you are trying to do? From what I can tell, you just want to filter based on a product ID, and then apply a second filter based on some other attributes(size, color, etc). Is that correct? – james00794 Nov 21 '17 at 20:44
  • @james0074 , yes that is what i'm trying to achieve. The reason for the maybe irrelevant information in your eyes is because of the fact that the `webservice` of prestashop works this way and i wanted to show that within the question. About the plunkr i will check that in the morning. But yess i'm trying to filter 2 things. First i want to show all the product_id's. Then for each product id the existing associacions, the attribute id for color or size. – Deathstorm Nov 21 '17 at 20:48

5 Answers5

1

Have you considered using the database and letting the DB generate a specific query for this screen, which groups all the products together in a single row?.

so you currently have a bunch of "repeated" product IDs (since the query returns detailed information for color and size), but need to operate on the single record.

You can do that in the backend using a GROUP BY SQL query statement.

If the endpoint is being used by more programs, simply make your endpoint to be "only for this screen" and apply the necessary changes.

If changing the SQL on the backend is too complex (or politically inviable), you can always group_by using javascript:

https://www.consolelog.io/group-by-in-javascript

group_by takes as input a dataset and a column (i.e.).

given a dataset like:

c1 c2 c3
1  10 10
1  11 10
3  12 20

// this operation:
groups = group_by(dataset, "c1")

//returns this:

groups:

group 1 (2 elements)
    c2  c3
    10  10
    11  10
group 3 (1 element)
    c2  c3
    12  10

this means you could feed the grouped dataset back into the grid program, so it can show groups as groups and items as items, by iterating first over the groups and later over the items.

There are many ways to implement group_by, like these:

What is the most efficient method to groupby on a javascript array of objects?

HTH

Felipe Valdes
  • 1,998
  • 15
  • 26
1

Maybe you should ask yourself what part of your application have the responsibility of sorting those attributes. This could lead to a more obvious solution for you. If the responsibility goes to the API, then either your request or your API should return a ready-to-use JSON for your WebApp. If the responsibility goes to the front WebApp, either do it in the js code or in the template.

My guess is that your API should do that, in which case i may not be the most advisor. But if you decide to do this in the WebApp, I definitely think that you should not use ngIf to do that, but instead build Pure Functions in your JS to build one or several array that you will be able to use with not too much of functionnaly code in your template.

For example you could .map() your response and return a well iterable object, and/or chained it with a .concat() or whatever. If you need ideas to build such functions check those example http://reactivex.io/learnrx/

Kleioz
  • 294
  • 3
  • 13
1

By reading your question, I would suggest instead of having the ng-if n times, you can have the logic for filtering inside your controller like

  $scope.getFilteredList = function() {
     return $scope.stock_availables.filter(function(stock) {
       return stock.id_product === $scope.selectedID;   // $scope.selectedID will be the id to be filter, in your example '0'
     });
  }

And your ng-repeat will be changed as

<div ng-repeat="stock_available in getFilteredList">
Aswin Ramesh
  • 1,654
  • 1
  • 13
  • 13
0

I think it's better for you to let DB order it for you. But you can try AngularJS orderBy or filter

var module = angular.module('MyApp', []);

module.controller('MyController', function($scope){

  $scope.list = [
  { 'id': 0, 'count': 10 },
  { 'id': 1, 'count': 9 },
  { 'id': 2, 'count': 8 }];

});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="MyApp">
  <div ng-controller="MyController">

    <div ng-repeat="member in list | orderBy:'-count'">
      {{member.id}}
    </div>
    <hr>
    <div ng-repeat="member in list | filter: {id:0}">
      {{member.id}}
    </div>
    <hr>
    <div ng-repeat="member in list | orderBy: '-count' | filter: {id : '!0'}">
      {{member.id}}
    </div>
  </div>
</div>
KL_
  • 1,503
  • 1
  • 8
  • 13
0

I also would suggest using the db query to provide the ordering of your elements. However, we don't always have access to change a backend or how it functions.

SO ..

I would suggest using a sort function to convert your response data before setting $scope.customers

$http.get('config/get/getCustomers.php', { cache: true }).then(function (response) {
        var temp = response.data.customers.customer;
        temp.sort(fucntion (item1, item2) {
            if (item1.product === item2.product) {
                if (item1.size === item2.size) {
                    return item1.color > item2.color;
                }
                return item1.size > item2.size;
            }
            return item1.product > item2.product;
        }
        $scope.customers = temp
 });
Jesse Nelson
  • 776
  • 8
  • 21