AngularUI router issue "TypeError: Unable to read property 'name' from undefined"

I have a DIV set that contains A and is using AngularUI to load Angular templates in a DIV-like view. I am implementing a set of tabs that switch between view templates based on which the user clicks. I have several of them in my application and they all work as designed. However, on one of my pages I keep getting "TypeError: Can not read property" name "of undefined" which exploded in the call to angular-ui-router isMatch () in $ StateRefActiveDirective ($ state, $ stateParams, $ interpolation ) on the line "return $ state.includes (state.name) && & && matchesParams ();" because the state is undefined. I went through calls to this code and it successfully handles all ui-srefs in my markup. Then, after they have been processed,they are tightening. The only real difference between other pages with these tabs in them is that I have other pages without any other markup on them other than the tabs and their u-views. On this I have 2 bootstrap columns, col-md-5 and col-md-7. Col-md-5 contains a table of objects. When the user clicks on one of the lines, it displays col-md-7, the $ state is changed to one of its tabs, and the data for the object is loaded for the tab by its controller.When the user clicks on one of the lines, it displays col-md-7, the $ state is changed to one of its tabs, and the data for the object is loaded for the tab by its controller.When the user clicks on one of the lines, it displays col-md-7, the $ state is changed to one of its tabs, and the data for the object is loaded for the tab by its controller.

On other pages, tabs are always displayed and clicking on them takes you to different ui views, just like my problem child did. One real difference between the two is the directive I wrote to disable boolean based links. The uiSrefIf directive is at the bottom of the JavaScript list. The idea is that when the File tab is displayed, the other tabs are visible but disabled.

Here's the JavaScript and markup:

angular.module('meritApp.screens.importUsers', [
    'ui.router', 'ngSanitize', 'ui.select', 'ui.bootstrap', 'meritApp.global.services.profile',
    'meritApp.global.services.common', 'angularFileUpload', 'meritApp.global.services.managedFiles'
  ])
  .config(function($stateProvider) {

    $stateProvider
      .state('importUsers', {
        url: "/admin/importUsers",
        templateUrl: "app/screens/admin/importUsers/importUsers.html?v=" + APP_VERSION
      });

    $stateProvider
      .state('importUsers.file', {
        url: "/file",
        templateUrl: "app/screens/admin/importUsers/file.html?v=" + APP_VERSION
      });

    $stateProvider
      .state('importUsers.inserted', {
        url: "/inserted",
        templateUrl: "app/screens/admin/importUsers/inserted.html?v=" + APP_VERSION
      });

    $stateProvider
      .state('importUsers.insertError', {
        url: "/insertError",
        templateUrl: "app/screens/admin/importUsers/insertError.html?v=" + APP_VERSION
      });

    $stateProvider
      .state('importUsers.skipped', {
        url: "/skipped",
        templateUrl: "app/screens/admin/importUsers/skipped.html?v=" + APP_VERSION
      });

    $stateProvider
      .state('importUsers.updated', {
        url: "/updated",
        templateUrl: "app/screens/admin/importUsers/updated.html?v=" + APP_VERSION
      });

  })
  .controller('importUsersController', [
    '$scope', '$state', '$log', '$rootScope', '$filter', 'appConfig', '$http', 'roleSvc',
    function($scope, $state, $log, $rootScope, $filter, appConfig, $http, roleSvc) {

      $rootScope.$broadcast("SET_BREADCRUMB", []);

      $scope.itemsPerPage = 10;
      $scope.currentPage = 1;

      $rootScope.importing = false;

      $rootScope.roleChanged = function(user, role) {
        // todo - set the user role
      };

      $scope.newImport = function() {
        $rootScope.importing = true;
        $rootScope.savedLocation = $rootScope.selectedLocation;
        $rootScope.selectedLocation = null;
      };

      $rootScope.defaultRole = {
        name: "Associate"
      };
      roleSvc.listAllActive(0, 40).then(
        function(response) {
          $rootScope.roles = $filter('orderBy')(response.data.results, "name");
          angular.forEach($rootScope.roles, function(role) {
            if (role.name == "Associate") {
              $rootScope.defaultRole = role;
            }
          });
        },
        function(err) {
          console.log("roleSvc.listAllActive Error: " + err.status + " " + err.statusText);
        });

      var init = function() {
        $scope.loading = true;
        $scope.locations = [];

        var skip = ($scope.currentPage - 1) * $scope.itemsPerPage;
        var userImportQueryPath = "queries/Merit.Components.UserImport.Queries/UserImportQueries";

        $http.get(appConfig.urlRoot + userImportQueryPath + "/GetImportSummaries/results").then(
          function(response) {
            $scope.totalItems = response.data.results.length;
            angular.forEach(response.data.results, function(location) {
              if (skip-- <= 0 && $scope.locations.length < $scope.itemsPerPage) {
                $scope.locations.push(location);
              }
            });

            $scope.loading = false;
          }, function(err) {
            alert("GetImportList Error: " + err.status + " - " + err.statusText);
          });

        $http.get(appConfig.urlRoot + userImportQueryPath + "/GetAuditRecords/results").then(
          function(response) {
            $rootScope.importData = [];
            angular.forEach(response.data.results, function(record) {
              record["isInvited"] = false;
              record["selectedRole"] = $rootScope.defaultRole;
              record.errorText = JSON.parse(record.errorText);
              $rootScope.importData.push(record);
            });

            $scope.users = _.sortBy($rootScope.importData, 'employeeId');
          }, function(err) {
            alert("GetImportList Error: " + err.status + " - " + err.statusText);
          });
      };

      init();

      $scope.refresh = function() {
        init();
      };

      $scope.viewImports = function(location) {
        $rootScope.selectedLocation = location;
        $state.go('importUsers.inserted', {}, {
          reload: true
        });
      };

      $rootScope.closeView = function() {
        $rootScope.importing = false;

        if ($rootScope.savedLocation) {
          $rootScope.selectedLocation = $rootScope.savedLocation;
          $rootScope.savedLocation = null;
        } else {
          $rootScope.selectedLocation = null;
        }
      };

      $scope.inviteUsers = function() {
        angular.forEach($scope.users, function(userArray) {
          if (userArray) {
            angular.forEach(userArray, function(user) {
              if (user.isInvited) {
                // todo - send email to invited user
                console.log("Sending email to " + user.name);
                user.isInvited = false;
              }
            });
          }
        });
      };
    }
  ])
  .controller('iuInsertedController', [
    '$scope', '$state', '$log', '$rootScope',
    function($scope, $state, $log, $rootScope) {

      $rootScope.$broadcast("SET_BREADCRUMB", []);

      $scope.itemsPerPage = 10;
      $scope.selectedTab = "Inserted";
      $scope.currentPage = 1;
      $scope.inviteCount = 0;

      var init = function() {
        $scope.users = [];
        $scope.totalItems = 0;

        if (!$rootScope.selectedLocation) return;

        var skip = ($scope.currentPage - 1) * $scope.itemsPerPage;

        angular.forEach($rootScope.importData, function(user) {
          if (user.isInserted && !user.hasErrors && user.locationCode == $rootScope.selectedLocation.code && user.timestampUtc == $rootScope.selectedLocation.timeStampUtc) {
            if (skip-- <= 0 && $scope.users.length < $scope.itemsPerPage)
              $scope.users.push(user);

            $scope.totalItems++;
          }
        });
      };

      init();

      $scope.refresh = function() {
        init();
      };

      $scope.invite = function(user) {
        $scope.inviteCount = (user.isInvited ? $scope.inviteCount + 1 : $scope.inviteCount - 1);
      };

      $scope.inviteUsers = function() {
        alert("todo - send email to invited users");

        angular.forEach($scope.users, function(user) {
          if (user.isInvited) {
            // todo - send email to invited user
            console.log("Sending email to " + user.name);
            user.isInvited = false;
          }
        });
      };
    }
  ])
  .controller('iuInsertErrorController', [
    '$scope', '$state', '$log', '$rootScope',
    function($scope, $state, $log, $rootScope) {

      $rootScope.$broadcast("SET_BREADCRUMB", []);

      $scope.itemsPerPage = 10;
      $scope.currentPage = 1;
      $scope.selectedTab = "Inserted with Errors";

      var importData = $rootScope.importData;

      var init = function() {
        $scope.users = [];
        $scope.totalItems = 0;

        if (!$rootScope.selectedLocation) return;

        var skip = ($scope.currentPage - 1) * $scope.itemsPerPage;

        angular.forEach(importData, function(user) {
          if (user.isInserted && user.hasErrors && user.locationCode == $rootScope.selectedLocation.code && user.timestampUtc == $rootScope.selectedLocation.timeStampUtc) {
            if (skip-- <= 0 && $scope.users.length < $scope.itemsPerPage)
              $scope.users.push(user);

            $scope.totalItems++;
          }
        });
      };

      init();

      $scope.refresh = function() {
        init();
      };

      $scope.inviteUsers = function() {
        angular.forEach($scope.users, function(userArray) {
          if (userArray) {
            angular.forEach(userArray, function(user) {
              if (user.isInvited) {
                // todo - send email to invited user
                console.log("Sending email to " + user.name);
                user.isInvited = false;
              }
            });
          }
        });
      };
    }
  ])
  .controller('iuUpdatedController', [
    '$scope', '$state', '$log', '$rootScope',
    function($scope, $state, $log, $rootScope) {

      $rootScope.$broadcast("SET_BREADCRUMB", []);

      $scope.itemsPerPage = 10;
      $scope.currentPage = 1;
      $scope.selectedTab = "Updated";

      var init = function() {
        $scope.users = [];
        $scope.totalItems = 0;

        if (!$rootScope.selectedLocation) return;

        var skip = ($scope.currentPage - 1) * $scope.itemsPerPage;

        angular.forEach($rootScope.importData, function(user) {
          if (user.isUpdated && user.locationCode == $rootScope.selectedLocation.code && user.timestampUtc == $rootScope.selectedLocation.timeStampUtc) {
            if (skip-- <= 0 && $scope.users.length < $scope.itemsPerPage)
              $scope.users.push(user);

            $scope.totalItems++;
          }
        });
      };

      init();

      $scope.refresh = function() {
        init();
      };

      $scope.inviteUsers = function() {
        angular.forEach($scope.users, function(userArray) {
          if (userArray) {
            angular.forEach(userArray, function(user) {
              if (user.isInvited) {
                // todo - send email to invited user
                console.log("Sending email to " + user.name);
                user.isInvited = false;
              }
            });
          }
        });
      };
    }
  ])
  .controller('iuSkippedController', [
    '$scope', '$state', '$log', '$rootScope',
    function($scope, $state, $log, $rootScope) {

      $rootScope.$broadcast("SET_BREADCRUMB", []);

      $scope.itemsPerPage = 10;
      $scope.currentPage = 1;
      $scope.selectedTab = "Skipped";

      var init = function() {
        $scope.users = [];
        $scope.totalItems = 0;

        if (!$rootScope.selectedLocation) return;

        var skip = ($scope.currentPage - 1) * $scope.itemsPerPage;

        angular.forEach($rootScope.importData, function(user) {
          if (user.wasSkipped && user.locationCode == $rootScope.selectedLocation.code && user.timestampUtc == $rootScope.selectedLocation.timeStampUtc) {
            if (skip-- <= 0 && $scope.users.length < $scope.itemsPerPage)
              $scope.users.push(user);

            $scope.totalItems++;
          }
        });
      };

      init();

      $scope.refresh = function() {
        init();
      };

      $scope.inviteUsers = function() {
        angular.forEach($scope.users, function(userArray) {
          if (userArray) {
            angular.forEach(userArray, function(user) {
              if (user.isInvited) {
                // todo - send email to invited user
                console.log("Sending email to " + user.name);
                user.isInvited = false;
              }
            });
          }
        });
      };
    }
  ])
  .controller('iuFileController', [
    '$scope', '$state', '$log', '$rootScope', 'managedFileSvc', 'commandSvc', 'appConfig', '$http', 'modalMsgSvc',
    function($scope, $state, $log, $rootScope, managedFileSvc, commandSvc, appConfig, $http, modalMsgSvc) {

      $rootScope.$broadcast("SET_BREADCRUMB", []);

      $scope.locations = mockExtraLocations;
      $scope.importLocation = undefined;
      $scope.importFile = undefined;
      $scope.buttonText = "Browse...";
      var locationsQueryPath = "queries/Merit.Components.Locations.Queries/LocationQueries";

      $http.get(appConfig.urlRoot + locationsQueryPath + "/FindAllActive/results?skip=" + 0 + "&pageSize=" + 20).then(
        function(response) {
          $scope.locations = response.data.results;
        }, function(err) {
          console.log("LocationQueries.FindAllActive Error: " + err.status + " - " + err.statusText);
        });

      $scope.doImport = function() {
        managedFileSvc.uploadFile($scope.importFile, function(evt) {
          console.log(evt);
          $scope.uploadProgress = evt;
        }, function(response) {
          console.log('upload response :: File ID: ', response[0].QueryReferences[0].QueryParameter);

          var command = {
            ImportFileId: response[0].QueryReferences[0].QueryParameter,
            LocationCode: $scope.importLocation.code,
            LocationName: $scope.importLocation.name
          };

          modalMsgSvc.showLoading("Importing...");

          commandSvc.publish('Merit.Components.User.Commands', 'ImportHumanicUsers', command)
            .then(function(record) {
              modalMsgSvc.hideLoading();
              console.log("ImportHumanicUsers Published: " + record.status);
              $scope.importFile = undefined;
              $rootScope.closeView();
              $state.go("importUsers", {}, {
                reload: true
              });
            }, function(err) {
              modalMsgSvc.hideLoading();
              if (err.errors)
                console.log("ImportHumanicUsers ERROR: " + err.errors[0]);
              else if (err.errorHeadline)
                console.log("ImportHumanicUsers ERROR: " + err.errorHeadline);
              else
                console.log("ImportHumanicUsers ERROR: unknown");
              // TODO?
            });
        });
      };

      $scope.fileSelected = function(files) {
        // even though ng-multiple is false, we still get an array
        if (files != null) {
          $scope.importFile = files[0];
        }
      };

      $scope.cancelImport = function() {
        $scope.importFile = undefined;
        $rootScope.closeView();

        if ($rootScope.selectedLocation)
          $state.go("importUsers.inserted");
      };
    }
  ])
  .directive('uiSrefIf', function($compile) {
    return {
      scope: {
        val: '@uiSrefVal',
        if : '=uiSrefIf'
      },
      link: function($scope, $element, $attrs) {
        $element.removeAttr('ui-sref-if');
        $compile($element)($scope);

        $scope.$watch('if', function(bool) {
          if (bool) {
            $element.attr('ui-sref', $scope.val);
          } else {
            $element.removeAttr('ui-sref');
            $element.removeAttr('href');
          }
          $compile($element)($scope);
        });
      }
    };
  });
      

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller="importUsersController">

  <div class="row">
    <div class="col-md-5">
      <div class="table importLocationsCol">
        <div class="row headerRow titleBar">
          <div class="col-md-9"><i class="icon-users"></i>Import Users</div>
          <div class="col-md-3 importNewBtnDiv">
            <div class="btn btn-cancel importNewButton" ng-click="newImport()" ui-sref-opts="{reload: true}">
              <a ui-sref="importUsers.file" style="text-decoration: none !important; color: #585858;">New</a>
            </div>
          </div>
        </div>

        <div class="row">
          <h4>Past Imports</h4>
        </div>

        <div class="row">
          <table class="table table-striped deptTable importTable">
            <thead>
              <tr>
                <th class="importNameCol">Location</th>
                <th class="importCountsCol">Update / New / Skip</th>
                <th class="importDateCol">Imported</th>
              </tr>
            </thead>
            <tbody>
              <tr ng-show="loading && locations && locations.length == 0">
                <td colspan="3">
                  <br />
                  <br />
                  <img class="spinner" src="content/images/ajax-loader.gif" alt="Loading..." />
                </td>
              </tr>
              <tr ng-hide="true">
                <td>&nbsp;
                  <!-- trick angular into making the next row white -->
                </td>
              </tr>
              <tr ng-repeat=" location in locations" ng-class="{selected: location.code == selectedLocation.code}" ng-show="locations && locations.length > 0" ng-click="viewImports(location)">
                <td class="importNameCol">{{location.name}} - {{location.city}}, {{location.state}}</td>
                <td class="importCountsCol">{{location.updated}} / {{location.inserted}} / {{location.skipped}}</td>
                <td class="importDateCol">{{location.timeStampUtc | date:'MM/dd/yyyy'}}</td>
              </tr>
              <tr ng-hide="loading || (locations && locations.length > 0)">
                <td colspan="3" class="text-center">No Imports Found.</td>
              </tr>
            </tbody>
          </table>
        </div>

        <div class="row">
          <div class="col-md-12 paginationRow">
            <pagination total-items="totalItems" items-per-page="itemsPerPage" ng-model="currentPage" max-size="5" ng-show="totalItems > itemsPerPage || true" direction-links="false" boundary-links="false" rotate="false" ng-change="refresh()" class="pagination-sm pull-right"></pagination>
          </div>
        </div>
      </div>
    </div>

    <div class="col-md-7">
      <div class="table importViewCol" ng-show="selectedLocation != null || importing">
        <div class="row headerRow titleBar">
          <div class="col-md-9">
            <i class="icon-users"></i><span ng-show="importing">New</span><span ng-hide="importing">View</span> Import
          </div>
          <div class="col-md-3 text-right" ng-click="closeView()"><i class="icon-close"></i>
          </div>
        </div>

        <div class="editWorkOrder" style="padding-bottom: 0; border: 0;">
          <div class="tabBox">
            <div class="tabStrip">
              <div class="tab" ng-show="importing" ui-sref-opts="{reload: true}" ui-sref-active="thisTab">
                <a ui-sref="importUsers.file">File</a>
              </div>
              <div class="tab" ui-sref-opts="{reload: true}" ui-sref-active="thisTab" ng-disabled="importing">
                <a ui-sref-if="!importing" ui-sref-val="importUsers.inserted">Inserted</a>
              </div>
              <div class="tab" ui-sref-opts="{reload: true}" ui-sref-active="thisTab" ng-disabled="importing">
                <a ui-sref-if="!importing" ui-sref="importUsers.insertError">Inserted with Errors</a>
              </div>
              <div class="tab" ui-sref-opts="{reload: true}" ui-sref-active="thisTab" ng-disabled="importing">
                <a ui-sref-if="!importing" ui-sref="importUsers.updated">Updated</a>
              </div>
              <div class="tab" ui-sref-opts="{reload: true}" ui-sref-active="thisTab" ng-disabled="importing">
                <a ui-sref-if="!importing" ui-sref="importUsers.skipped">Skipped</a>
              </div>
            </div>
          </div>
        </div>

        <div class="importUsersDisplay" ui-view></div>
      </div>
    </div>

  </div>
</div>
      

Run codeHide result




Since we are using so many service calls, I have not tried to get this to work in Plunker. This is not a show stopper as the page behaves as expected. This is an annoyance that I would like to understand and eliminate. Also, my QA staff can stop complaining to me about this :)

Thanks Mike

+3


source to share





All Articles