Angular JS - Sorting with ng-repeat

I'm completely new to AngularJS and facing a filtering issue with a custom function. My goal is to have:

  • Checkboxes for product categories
  • Checkboxes for product sub-categories
  • A showcase of products

When a user clicks on a category, the sub-categories should update (be enabled, selected), followed by the products being filtered accordingly.

In my app.js file, I currently have:

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

var rootUrl = $("#linkRoot").attr("href");

ProductApp.controller('ProductController', function ($scope, $http, $timeout) {

    // API calls to get data
    $http.get(rootUrl + '/GetJsonCategories').success(function (data) {
        $scope.categories = data;
    });

    $http.get(rootUrl + '/GetJsonSubCategories').success(function (data) {
        $scope.subcategories = data;
    });

    $http.get(rootUrl + '/GetJsonProducts').success(function (data) {
        $scope.products = data;
    })
    .then(function (data, status, headers, config) {
        // Callback function
        $timeout(function () {
            runJqueryScripts();
        }, 0);
    })

    $scope.showAll = true;

    function ForceFilterFn() {
        for (product in $scope.products) {
            $scope.filterFn($scope.products[product]);
        }
    };

    $scope.filterFn = function (product) {
        if ($scope.showAll) { return true; }

        var sel = false;

        for (category in $scope.subcategories) {
            var t = $scope.subcategories[category];

            if (t.selected) {
                if (product.CategoryId === t.CategoryId ) {
                    return true;
                }
            }
        }
        return sel;
    };

    $scope.ChangeCategory = function () {
        alert("Category Changed");
    };

    $scope.ChangeSubCategory = function () {
        for (t in $scope.subcategories) {
            if ($scope.subcategories[t].selected) {
                $scope.showAll = false;
                return;
            }
        }
        $scope.showAll = true;
    };
});

function equalHeight(group) {
    tallest = 0;
    group.each(function () {
        thisHeight = $(this).height();
        if (thisHeight > tallest) {
            tallest = thisHeight;
        }
    });
    group.each(function () { $(this).height(tallest); });
}

function runJqueryScripts() {
    equalHeight($(".thumbnail"));
    $("[rel='tooltip']").tooltip();

    $('.thumbnail').hover(
        function () {
            $(this).find('.caption').slideDown(250);
        },
        function () {
            $(this).find('.caption').slideUp(250);
        }
    );
}

As for the HTML markup:

<div ng-app="ProductApp" class="container">

        <!-- upper section -->
        <div class="row">
            <div class="col-sm-3">
                <!-- left -->
                <h3><i class="glyphicon glyphicon-cog"></i> Filters</h3>
                <hr>

                <ul ng-controller="ProductController" class="nav nav-stacked" id="categoryfilter">
                    <li>
                        <a id="MainCategory" href="">
                            <i class="glyphicon glyphicon-tag"></i> Product categories
                        </a>
                    </li>
                    <li ng-repeat="category in categories">
                        <input type="checkbox" ng-model="category.selected" value="{{category.CategoryId}}" ng-change="ChangeCategory()"> {{category.Text}}
                    </li>
                </ul>

                <ul ng-controller="ProductController" class="nav nav-stacked" id="subcategoryfilter">
                    <li>
                         <a id="SubCategory" href="">
                             <i class="glyphicon glyphicon-tags"></i> Product sub-categories
                         </a>
                     </li>
                     <li ng-repeat="subcategory in subcategories">
                         <input type="checkbox" ng-model="subcategory.selected" value="{{subcategory.CategoryId}}" ng-change="ChangeSubCategory()"> {{subcategory.Text}}
                     </li>

                 </ul>

                 <hr>

             </div><!-- /span-3 -->
             <div class="col-sm-9">

                 <!-- column 2 -->
                 <h3><i class="glyphicon glyphicon-dashboard"></i> Products </h3>

                 <hr>

                 <div ng-controller="ProductController" class="row">
                      <div ng-repeat="product in products | filter:filterFn" class="col-xs-6 col-sm-4">
                          <div class="thumbnail">
                              <img ng-src="@Url.Action("LoadImage", "Images")?ImagePath={{ product.ImagePath}}" alt="Product image" class="img-responsive" />
                              <div class="caption">
                                  <strong> {{ product.ProductName }}</strong>
                                  <p> {{ product.Description }}</p>
                                  <p>
                                      <span class="label label-primary pull-left">Price</span>
                                      <span class="label label-primary label-as-badge pull-right"> {{ product.Price }} </span>
                                  </p>
                                  <p>
                                      <a href="#" class="btn btn-primary center-block" role="button">
                                          <span class="glyphicon glyphicon-shopping-cart" style="vertical-align:middle"></span> Order
                                      </a>
                                  </p>
                              </div>
                              <strong>{{ product.ProductName }}</strong>
                          </div>
                      </div>

                  </div>


                  <!--/col-span-6-->

              </div><!--/row-->
          </div><!--/col-span-9-->

      </div><!--/row-->

@section Scripts{
<script type="text/javascript" src="@Url.Content("~/Scripts/angular.min.js")"></script>
<script type="text/javascript" src="@Url.Content("~/Scripts/app.js")"></script>
}

Currently, there seems to be an issue with the filtering functionality as the products aren't updating. It appears that $scope.filterFn is only called once during page load. Any assistance would be greatly appreciated.

Thank you in advance!

Answer №1

Perhaps the issue lies in having multiple instances of ProductController assigned to the ProductCategory and SubCategory blocks. These controllers, although different instances, each have a copy of the same variable, products. To resolve this, it is recommended to bind ProductController once to the parent element instead of duplicating it for the child elements.

You may want to structure your code like this:

<div ng-app="ProductApp" class="container">

    <!-- upper section -->
    <div class="row" ng-controller="ProductController" >
        <div class="col-sm-3">
            <!-- left -->
            <h3><i class="glyphicon glyphicon-cog"></i> Filters</h3>
            <hr>

            <ul class="nav nav-stacked" id="categoryfilter">
                <li>
                    <a id="MainCategory" href="">
                        <i class="glyphicon glyphicon-tag"></i> Product categories
                    </a>
                </li>
                <li ng-repeat="category in categories">
                    <input type="checkbox" ng-model="category.selected" value="{{category.CategoryId}}" ng-change="ChangeCategory()"> {{category.Text}}
                </li>
            </ul>

            <ul class="nav nav-stacked" id="subcategoryfilter">
                <li>
                    <a id="SubCategory" href="">
                        <i class="glyphicon glyphicon-tags"></i> Product sub-categories
                    </a>
                </li>
                <li ng-repeat="subcategory in subcategories">
                    <input type="checkbox" ng-model="subcategory.selected" value="{{subcategory.CategoryId}}" ng-change="ChangeSubCategory()"> {{subcategory.Text}}
                </li>

            </ul>

            <hr>

        </div><!-- /span-3 -->
        <div class="col-sm-9">

            <!-- column 2 -->
            <h3><i class="glyphicon glyphicon-dashboard"></i> Products </h3>

            <hr>

            <div class="row">
                <div ng-repeat="product in products | filter:filterFn" class="col-xs-6 col-sm-4">
                    <div class="thumbnail">
                        <img ng-src="@Url.Action("LoadImage", "Images")?ImagePath={{ product.ImagePath}}" alt="Product image" class="img-responsive" />
                        <div class="caption">
                            <strong>{{ product.ProductName }}</strong>
                            <p>{{ product.Description }}</p>
                            <p>
                                <span class="label label-primary pull-left">Price</span>
                                <span class="label label-primary label-as-badge pull-right">{{ product.Price }}</span>
                            </p>
                            <p>
                                <a href="#" class="btn btn-primary center-block" role="button">
                                    <span class="glyphicon glyphicon-shopping-cart" style="vertical-align:middle"></span> Order
                                </a>
                            </p>
                        </div>
                        <strong>{{ product.ProductName }}</strong>
                    </div>
                </div>

            </div>


            <!--/col-span-6-->

        </div><!--/row-->
    </div><!--/col-span-9-->

</div><!--/row-->

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring the Integration of Material UI DatePicker with Firestore in ReactJS: Converting Firestore Timestamps to Date Format

The database is correctly recording the date, however, when displayed, the DatePicker does not recognize the date from the database as it is in timestamp format (seconds and nanoseconds). <DatePicker margin="normal" label="Data do pedido" ...

Inject Angular 2 component into designated space

I am working on a website that requires a settings dialog to be loaded in a designated area upon clicking a button. The settings dialog is a component that retrieves data from REST endpoints. I am hesitant to simply insert the component and hide it as I ...

Vue method not being triggered despite emit in inline template

Trying to figure out what I'm missing here, it seems like this should be the solution: Vue: method is not a function within inline-template component tag Yet, the method still isn't working as expected. <b-table :items="filtered" ...

Adding numbers to a textbox using a JavaScript algorithm

There are two textboxes in this scenario. The first box should always contain 4 digits when you leave it, while the second box should always contain 10 digits. A javascript function needs to be implemented so that when a user leaves one of the textboxes, ...

Placement of image links

Wondering if there's a way to accomplish this. Can a hyperlink be placed on specific coordinates within an image? Example: _____________________________________ | | | xxx | | xxx ...

What is the best way to invoke a service within the angularJs configuration settings?

I'm encountering an issue with a service that needs to be called in the app's config module. It's throwing an error when I attempt to inject it. Strangely enough, this service functions perfectly when injected and executed in controllers. An ...

While attempting to deploy my project on Vercel by pulling in my code from GitHub, I ran into this error

As a self-taught Front End developer diving into building and hosting my first web page using React, I've encountered this warning. Any suggestions on how to resolve it? Cloning gitohub.com/Passion94/React-Apps (Branch: main, Commit: da89a2a) Cloning ...

Arranging Check Boxes Side by Side

I am struggling to properly align the three checkboxes and tables next to each other. I have managed to get the checkboxes on the same row, but aligning them neatly has proven to be a challenge. Using Notepad++ as my development tool, I am facing formattin ...

Tips for implementing React Browser Router within Material UI Drawer

I'm currently exploring how to implement Browser Router in React to populate the content section of a Material UI Drawer. While my code successfully links menu options to components displayed within the drawer's content section, a problem arises ...

Sending a complex JavaScript object to the MVC controller with a consistent 400 response code

Once again facing another MVC issue. Someday, I'll look back at my "MVC learning days" and smile. So here's the problem - a constant 400 response. Despite reading numerous posts on SO, I just can't seem to get my logic working. Maybe if I s ...

The options for answering (True, False, Yes, and No) are not currently visible

I am struggling with displaying buttons for "True", "False", "Yes", and "No" in an appended row. Here is the application: Application To use the application, follow these steps: 1. Open the application and click on the green plus button. A modal window ...

How to ensure an object is consistently rendered above all others using Three.js

Currently working on adding a dynamic command arrow that follows the cursor on the display. However, I have encountered an issue where it gets obscured by other elements on the screen. Is there a method to adjust the z-buffer or implement a workaround to ...

The Problem of Restoring Column Height in Tabulator 4.6.3 Filters

The Issue After activating and deactivating header filters, the column height does not return to its original state. Is this the expected behavior? Is there a way to reset the column height? Check out this JS Fiddle example: https://jsfiddle.net/birukt ...

What is the best way to connect tags with their corresponding tag synonyms?

I'm currently developing a system where users can link tags to posts, similar to how it's done on SO. I'm facing some challenges when it comes to implementing tag synonyms. Let's take a look at the Tags table: | TagName | |-------- ...

Unraveling modified JSON data in handlebars: a guide

I'm trying to customize the display of JSON keys with different names. Specifically, I want to show the months as jan, feb, mar... etc. Here is my current approach: However, instead of seeing jan, feb, mar..., my output shows 1, 2, 3, 4... I'm ...

The issue of Jquery selectors not functioning properly when used with variables

Currently working on a script in the console that aims to extract and display the user's chat nickname. Initially, we will attempt to achieve this by copying and pasting paths: We inspect the user's name in the Chrome console and copy its selec ...

Using the window.history.pushState function triggers a page reload every time it is activated

I've been attempting to navigate from page to page without the need for a reload. I came across suggestions that using window.history.pushState() could achieve this, however, it appears that the page is still reloading. Furthermore, an attempt ...

Change this multiple condition into an ng-class conditional syntax

Hey there, I could really use some assistance in converting this if-else statement with 'or' condition into a format that can be utilized in ng-class. So here's my current ng-class setup with the conditions, however, it seems to be giving m ...

How to Monitor Store Changes within a Vue File Using Vue.js

I am working with 2 vue files, header.vue and sidebar.vue. Both components are imported into layout.vue. Here are the steps I am following: 1. Initially, when the page loads, I update the store in header.vue with some values inside the created hook. 2. ...

Retrieve the value of the object within the mysterious index loop in JavaScript

I have retrieved search results from the data, and each time the index of my search result varies. At one point, the result may appear in the 4th index, while at another time it might be in the 100th index. How can I retrieve the rank value from within t ...