codenewbie
codenewbie

Reputation: 181

Angular Directive that adds select2 corrupts model?

I've created a table with ng-repeat and a directive to add a select2 ddl when you click on one of the cells. Everything works beautifully, the selected value is updated in the model (everything with the select2 works as expected), but somehow the model seems to be corrupted by having this directive included in the code (whether I have selected an item from the ddl or not).

After the table is loaded with the data, if I try to remove an item from the model (with splice):

$scope.Info.splice(index,1);

or even replace the entire model with a copy of the original model:

$scope.Info = angular.copy($scope.Info2);

I get an error in the console that says 'Array.prototype.forEach' is null or not an object. So far, I've only seen this behavior in IE8 (the browser we support). I do not see this happen in Chrome.

If I remove the 'click-to-edit-select2' from the input in the directive template, I do not see the error, so I can't help but think it's a problem within the directive, or even worse, within the select2 scripts that I have no idea how to pinpoint (hopefully not though).

Apologies in advance! I started to create a plunk, but realized that plunks don't work in IE, so it would be difficult to see the error I'm referring to.

I should also mention, that not only are we supporting IE8, we are also using older versions of scripts: angular.js: 1.2.19 select2-min.js: 3.4.8 ui-bootstrap: 0.8.0 (and templates) bootstrap.js: 2.3.1 bootstrap.css: 2.3.1

Please take a look at the code and let me know if there is a way to eliminate this error:

HTML:

<body ng-app="app" ng-controller="TestController">
<table class="table table-bordered">
        <thead>
          <tr role="row">
            <th tabindex="0" class="sorting" aria-label="" ng-click="predicate = 'Id'; reverse=!reverse">
              ID
            </th>
            <th tabindex="0" class="no_edit sorting" aria-label="">
              Description
            </th>
          </tr>
        </thead>
        <tbody ng-repeat="item in Info">
          <tr>
            <td click-to-edit-select2="item.id">
              {{item.Id}}
            </td>
            <td>
              {{item.Description}}
            </td>
          </tr>
        </tbody>
      </table>
</body>

Controller:

var app = angular.module("app", ["ui.select2", "ui.bootstrap", "click-to-edit-select2"]);
app.controller('TestController', function($scope, $http, $element, $filter, $modal) {
  $scope.Info2 = [{
    "UniqueIndex": "t1",
    "Status": "",
    "Id": "1",
    "Description": "This Description",

  }, {
    "UniqueIndex": "t2",
    "Status": "",
    "Id": "2",
    "Description": "That Description",

  }];

  //This works . . .initializing the table, before the directive is applied
  $scope.Info = angular.copy($scope.Info2); 


  $scope.DeleteRow = function(item) {
        //Either one of these statements throws the error in the console, 
        //and the model is not updated in the view
        $scope.Info = angular.copy($scope.Info2);
        $scope.Info.splice(0, 1);

    }

})

Directive:

angular.module('click-to-edit-select2', [])
  .directive("clickToEditSelect2", function() {
      var editorTemplate = '<td class="click-to-edit-select2">' +
      '<div style="height:20px" ng-click="enableEditor()" ng-hide="view.editorEnabled">' +
      '{{value}} ' +
      '</div>' +
      '<div ng-show="view.editorEnabled" ng-click="save()">' +
      '<input type="hidden" ui-select2="select2Options" ng-model="view.editableValue" ng-change="changeText()" />' +
      '</div>' +
      '</td>';

      return {
          restrict: "A",
          replace: true,
          template: editorTemplate,
          scope: {
              value: "=clickToEditSelect2"
          },
          controller: function($scope, $element, $attrs) { 
              $scope.view = {
                  editorEnabled: false
              };

              $scope.enableEditor = function() {

                      $scope.view.editorEnabled = true;
                      $scope.view.editableValue = $scope.value;

              };

              $scope.disableEditor = function() {
                  $scope.view.editorEnabled = false;
              };

              $scope.save = function() {
                  $scope.value = $scope.view.editableValue.id;
                  $scope.disableEditor();
              };


              var initSelectionCb = function(item, callback) {
                  if (item != "") {
                      var id = item.val();
                      var data = { id: id, text: id };
                      callback(data);
                  }
              };

              $scope.select2Options = { 
                  placeholder: "Select",
                  multiple: false,
                  width: "resolve",
                  initSelection: function(item, callback) {
                      //selects the initial item
                      initSelectionCb.call(self, item, callback);
                  },
                  ajax: {

                      url: "/GetDropDownListForSelect2",
                      type: "POST",
                      contentType: "application/json",
                      data: function(term, page) {

                          var Json = {};
                          Json.term = term;
                          Json.page = page;

                          return Json;

                      },
                      results: function(data, page) {
                          return { results: data.options };
                      }
                  }
              }


          }
      };
  });

Upvotes: 1

Views: 504

Answers (2)

Roman K.
Roman K.

Reputation: 164

Looking at the Angular guide for IE8, its not completely supported. That said, there is one thing you can try to resolve this which is change your

<body ng-app="app" ng-controller="TestController">

to

<body ng-app="app" id="ng-app" ng-controller="TestController">

and add the angular namespace to the top of your pages.

<html xmlns:ng="http://angularjs.org">

You can look it up here.

EDIT: For future refrence, codenewbie has the solution to the Array.prototype.Foreach issue below. Be sure to read that if you're having that issue.

Upvotes: 0

codenewbie
codenewbie

Reputation: 181

Whew! I was able to find the answer here

I put the following code at the top of my script file, and the error disappeared, and the splice and copy methods work in IE8 now:

if (!('forEach' in Array.prototype)) {
    Array.prototype.forEach = function(action, that /*opt*/) {
        for (var i = 0, n = this.length; i < n; i++)
            if (i in this)
            action.call(that, this[i], i, this);
    };
}

Upvotes: 2

Related Questions