Angular - ng-repeat failing to update when nested array is modified

Utilizing ng-repeat to render data fetched via a GET request that returns an array.

HTML

<div ng-controller="candidateCtrl" >
    <div class="table-responsive">
      <table class="table table-striped">
        <thead>
            <tr>
                <th>NO</th>
                <th>NAMA</th>
                <th>NIP</th>
                <th>INSTANSI</th>
                <th><span ng-show="animateCandidateAdmin" class="ion-load-a"></span></th>
            </tr>
        </thead>
        <tbody ng-repeat="candidate in candidatesAdmin">
            <tr class="well whel">
                <td>{{$index + 1}}</td>
                <td>{{candidate.candidate_name}}</td>
                <td>{{candidate.candidate_nip}}</td>
                <td>{{candidate.candidate_institusi}}</td>
                <td>
                    <button class="btn btn-xs btn-success" ng-show="candidate.m_assesment_assesment_id == NULL" ng-click="addCandidate3(candidate.candidate_id)">
                </td>
            </tr>

        </tbody>
    </table>
  </div><!-- OFF-MAINBAR -->
  <div ng-repeat="item in percentage_penilaian" >
       <div id="candidate_{{item.m_assesment_assesment_id}}" >
           <div class="panel-body">
               <div class="table-responsive">
                   <table class="table table-striped">
                      <thead>
                          <tr>
                              <th>NO</th>
                              <th>NAMA</th>
                              <th>NIP</th>
                              <th>INSTANSI</th>
                              <th>BOBOT</th>
                              <th>SKOR</th>
                              <th>NILAI</th>
                              <th><span ng-show="animateCandidateManagerial" class="ion-load-a"></span>
                              </th>
                           </tr>
                       </thead>
                       <tbody>
                           <tr ng-repeat="candidate in candidates[item.m_assesment_assesment_id]" class="well whel">
                               <td>{{$index + 1}}</td>
                               <td>{{candidate.candidate_name}}</td>
                               <td>{{candidate.candidate_nip}}</td>
                               <td>{{candidate.candidate_institusi}}</td>
                               <td>{{candidate.percentage}}%</td>
                               <td ng-show="candidate.skor != NULL">                                                                             
                                    <button ng-click="$eval(arrAddCandidate[percentage_penilaian[$parent.$index+1].m_assesment_assesment_id])(candidate.candidate_id)"><i class="ion-arrow-right-a"></i> Follow {{percentage_penilaian[$parent.$index+1].assesment_name}}</button>
                               </td>
                             </tr>
                           </tbody>
                       </table>
                   </div><!-- OFF-MAINBAR -->
               </div>
           </div>
       </div>
   </div>

JS

    <script>
    var SITEURL = "<?php echo site_url() ?>";
    var selectionApp = angular.module("selectionApp", ["isteven-multi-select"]);

    selectionApp.controller('candidateCtrl', function ($scope, $http) {
        $scope.candidates = [];
        $scope.arrAddCandidate = [];

        $scope.getPercentagePenilaian = function () {
            var url = SITEURL + 'xxx/xxx/' + 14;
            $http.get(url).then(function (response) {
                $scope.percentage_penilaian = response.data;
                for(var i in response.data){ 
                     $scope.arrAddCandidate[response.data[i].m_assesment_assesment_id] = "addCandidate"+response.data[i].m_assesment_assesment_id;                                                                                                                                                                              

                }
            })

        };

        $scope.getCandidateAdmin = function () {
           var url = SITEURL + 'api/get_candidate_admin/' + 14;
           $http.get(url).then(function (response) {                                                                                  
             $scope.candidatesAdmin = response.data;
           })

        };                                                                          

        $scope.get_3 = function () {
            var url = SITEURL + 'xxx/xxx/3__14';
            $http.get(url).then(function (response) {
                $scope.$apply(function () {
                    $scope.candidates[3] = response.data;
                    // $scope.candidates.push(response.data);
                });
            })

        };
        $scope.addCandidate3 = function (id) {
            $scope.animateCandidateAdmin = true;
            var postData = $.param({
                                candidate_id: id,
                                assesment_id: 3                                                                                                });
            $.ajax({
                method: "POST",
                url: SITEURL + "xx/xxx/xxxxx",
                data: postData,
                success: function (response) {
                    if(response=='sukses'){
                        $scope.animateCandidateAdmin = false;
                        $scope.getCandidateAdmin();
                        $scope.get_3();
                    }
                }
            });

        };

        $scope.get_5 = function () {
            var url = SITEURL + 'xx/xxx/5__14';
            $http.get(url).then(function (response) {
                $scope.$applyAsync(function () {
                    $scope.candidates[5] = response.data;
                    // $scope.candidates.push(response.data);
                });
            })

        };
        $scope.addCandidate5 = function (id) {
            $scope.animateCandidateAdmin = true;
            var postData = $.param({
                                candidate_id: id,
                                assesment_id: 5                                                                                                });
            $.ajax({
                method: "POST",
                url: SITEURL + "xx/xxx/xxxxx",
                data: postData,
                success: function (response) {
                    if(response=='sukses'){
                        $scope.animateCandidateAdmin = false;
                        $scope.getCandidateAdmin();
                        $scope.get_5();
                    }
                }
            });

        };
        angular.element(document).ready(function () {
            $scope.getPercentagePenilaian();
            $scope.get_3;
            $scope.get_5;
        });
    });                
</script>

Obtained output after executing $scope.getCandidateAdmin:

[{"candidate_id":"24","candidate_name":"example","candidate_nip":"12345","candidate_institusi":"Institution A","selection_selection_id":"14"}]

Data returned by $scope.getPercentagePenilaian:

[{"id":"14","m_assesment_assesment_id":"3","percentage":"50"},
{"id":"15","m_assesment_assesment_id":"5","percentage":"10"}]

Content yielded through $scope.get_3:

[{"id":"43","selection_selection_id":"14","m_assesment_assesment_id":"3"
,"candidate_id":"24","m_candidate_id" :"1","candidate_name":"example","candidate_nip":"12345","candidate_institusi":"Institution A","competency_skor":null}]

Despite updating the $scope.candidates array upon adding a candidate, the corresponding table does not reflect these alterations. The reason behind this discrepancy remains unclear.

Answer №1

Possible Problem Detected

An issue may be arising from the fact that 'm_assesment_assesment_id' is being stored as a String instead of an Integer, leading to the root cause of your trouble.

[
  { "id":"14",
    "m_assesment_assesment_id":"3",  // Please note: numbers are enclosed in quotes
    "percentage":"50"
  },
  { "id":"15",
    "m_assesment_assesment_id":"5", // It's important to understand that JSON parsers will treat these as Strings, not actual numbers
    "percentage":"10"}
]

Context for Identifying the Issue

The ng-repeat function is using the property item.m_assesment_assesment_id, causing Angular to interpret these values as Strings. Consequently, when this String is passed into the Array, it ends up being perceived as a JavaScript Object rather than an array. The following example illustrates this:

// Both lines below represent the same thing
// Using dot accessor
item.m_assesment_assesment_id
// Using square brackets for data access through Object Notation
item["m_assesment_assesment_id"]

This situation leads JavaScript to regard your Array as an Object, where item.m_assesment_assesment_id serves as a key instead of an index.

<!-- item.m_assesment_assesment_id is evaluated as a String due to the JSON format specifying String representation, triggering Object evaluation over Array evaluation -->
<tr ng-repeat="candidate in candidates[item.m_assesment_assesment_id]" class="well whel">

Here is a helpful summary on Array Evaluation: https://www.w3schools.com/js/js_arrays.asp

Associative Arrays Many programming languages support arrays with named indexes.

Arrays with named indexes are called associative arrays (or hashes).

JavaScript does not support arrays with named indexes.

In JavaScript, arrays always use numbered indexes.

Potential Solutions

There are several ways to address this issue. If you have the ability to modify the JSON structure, I recommend rectifying the problem there:

{  
   "id":"14",
   "m_assesment_assesment_id": 3,  // Absence of quotation marks signifies Integer type
   "percentage":"50"
}

However, altering the server code generating the response might be necessary, and there could be architectural considerations favoring the current string implementation.

Alternatively, you can adjust your ng-repeat like so:

<tr ng-repeat="candidate in candidates[parseInt(item.m_assesment_assesment_id)]" class="well whel">

Keep in mind that this approach incurs a performance overhead since additional processing is required during Angular's $digest() event. While insignificant for smaller datasets, it should be taken into account.

If modifying the JSON format on the server side or potentially adding strain to the $digest event isn't feasible, consider having your control manage the response data from AJAX calls. This approach aligns with recommendations from Angular's documentation.

angular.module("selectionApp", ["isteven-multi-select"])
  .controller('candidateCtrl', function ($scope, $http) {
        $scope.candidates = [];
        $scope.arrAddCandidate = [];

        $scope.getPercentagePenilaian = function () {
            var url = SITEURL + 'xxx/xxx/' + 14;
            $http.get(url).then(function (response) {
                // UTILIZE A UTILITY METHOD TO CONVERT STRINGS INTO INTEGERS HERE
                $scope.percentage_penilaian = processPercentagePenilaian(response.data);
                for(var i in response.data){ 
                    $scope.arrAddCandidate[response.data[i].m_assesment_assesment_id] = "addCandidate"+response.data[i].m_assesment_assesment_id;                                                                                                                                                                              

            }
        })

    };

   // *** MORE of your code exists between these functions

   // ALSO YOU HAVE TO CALL THIS IN THE CALLBACK OF THE APPROPRIATE AJAX CALLBACK
   var processPercentagePenilaian = function(items) {
      // Loop through the Items
      angular.forEach(items, function(item, key) {
         // For Every Item, convert the m_assesment_assesment_id to an Integer
         item.m_assesment_assesment_id = parseInt(item.m_assesment_assesment_id);
      });

// End Controller
});

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

Tally the total number of items within an array using AngularJS

Here is my code snippet: $scope.data.months = []; angular.forEach(response, function (value, key) { $scope.data.months.push({'month':value}); }); The output of console.log(response) looks like this: Array[3] 0 : "April 2017" 1 ...

Encountering AJAX Error 0 with jQueryUI Autocomplete upon pressing enter key

Currently, I am facing an issue with a search box that utilizes the jqueryUI .autocomplete feature to retrieve data through AJAX for providing suggestions. The problem arises when a user presses the enter key before the AJAX call to the source completes, r ...

Using AngularJS $http in JSP results in a messy and unreadable URL

Below is the code snippet from my app.js file: var loginModule = angular.module('loginModule',[]); loginModule.config(['$httpProvider', function ($httpProvider) { $httpProvider.defaults.headers.post['Content-Type'] = ...

Discover the perfect method for combining two objects while updating any empty values with a new specified value. Furthermore, in the case where the new value is also

My task involves working with an array of objects where each time I select a value, it gets pushed into the array. My goal is to merge two objects that share the same key "code" and remove any empty values. (4) [{…}, {…}, {…}, {…}] 0: {code: "abc ...

transferring information from the Quill text editor to the Node.js server

Is there a way to send data from Quilljs on the frontend to node.js on the backend? I've been searching for examples, but haven't found anything related to the backend. I tried reading the documentation for Quilljs, but I'm still struggling ...

Incorporating CSS styles into a dynamic JavaScript computation

I'm attempting to apply inline CSS to a JS calculation (e.g. 3*2*1) in order to make it appear red. I have experimented with using a span tag, but it only displays the calculation and not the result. I also tried utilizing an internal style sheet. Ho ...

Data cannot be transferred to a child element unless it has been initialized during the definition phase

Passing an array data from parent to child component has brought up some interesting scenarios: parent.component.html: <child-component ... [options]="students" > </child-component> Status I: Setting the array on definition ...

Exploring Vue JS: Decoupling variables in separate files and effective ways of accessing them

In my Vue project, I am looking to create a dedicated .js file to store multiple variables that can be easily accessed by other components. I am seeking advice on the most efficient method to achieve this and would greatly appreciate any examples provide ...

Display two images consecutively within a single img tag

My goal is to display two images using a single <img /> tag. The first small image will be loaded and shown using the src attribute, while the second large image will be contained within the data-src attribute. Only one image will be displayed at a t ...

Reveal and conceal using CSS animations

I am working on adding animation effects to show and hide HTML elements triggered by a button click. The button toggles a "hide" class to display or hide the element. Check out my code snippet: const toggleButton = document.querySelector('button ...

How can you gradually fade out the bottom edge of a rendered component until it is re-rendered?

Currently, I am developing a reviews microservice for an e-commerce platform using react and react-bootstrap. My goal is to showcase 5 reviews initially, followed by a button to expand and reveal more reviews. In order to achieve this, I envision the botto ...

Utilizing the split function within an ngIf statement in Angular

<div *ngIf="store[obj?.FundCode + obj?.PayWith].status == 'fail'">test</div> The method above is being utilized to combine two strings in order to map an array. It functions correctly, however, when attempting to incorporate the spli ...

Enhancing the user experience by providing auto-complete suggestions while still maintaining the original form functionality

Encountering a curious bug that appears to be affecting only IE and Webkit browsers. I've set up a simple form as follows: <div id="output">Form output is shown here</div> <form id="myForm" action="#" method="post"> <input type= ...

Leverage JSON data using props in React JS

Currently, my data is formatted in JSON and successfully populated into props known as foxFooterData. Upon inspecting the console.log result of foxFooterData.data, the following information is visible: https://i.sstatic.net/8htNG.png I am attempting to r ...

$watchCollection not firing when object is modified

I've been trying to understand why the watchCollection function is not triggering on object changes. .factory('Products', function($http, $timeout){ function Products(data){ if (data) { this.setData(data); ...

Using JSON.stringify to format data and making an asynchronous $http request

I am working with a JavaScript string that looks like this: user_fav = "21,16"; I need to process this string through a function so that it becomes a JSON array with an id key, like this: {"id":21},{"id":16} This JSON array is then used in an $http req ...

Ways to stop a JS plugin affecting HTML anchors and preventing them from automatically scrolling to the top

My friend is working on a website and he's using a plugin called Flip Lightbox. The basic functionality of the plugin is that when you click on an image, it flips over and pops out as a lightbox. Everything works fine, however, if you scroll down the ...

Is there a way to verify if the email entered into the input field is included in the list provided below?

Whenever an email is entered into the text box, a validation message should appear next to the input field. The inputted email should be checked against the list of emails below, and if it exists, an error message needs to be displayed. I need assistance w ...

Encountered a type error while attempting to render and define table columns within a material table component in

Hey there! I'm currently using the Material table to create a table view, and here are the columns that I have defined: const columns: TableColumn[] = [ { title: 'TYPE', field: 'type_of_action', highligh ...

Launching an IONIC application within an iframe of a different framework

Is it feasible to display an Ionic app within an IFrame HTML element hosted on a server-based app? If so, how can I incorporate the necessary IONIC/Angular libraries into my framework? For instance, is it possible to render an Ionic app within the view of ...