Search code examples
angularjsfactoryangular-bootstrap

angular 1.6: can't get to pass text to modal as component


I have a factory:

  ngapp.factory('ErrorService',["$uibModal", function ErrorService($uibModal) {
    return {
      showError: function(errorText) {
        var modalInstance = $uibModal.open({
          animation: true,
          component: 'errorDialog',
          resolve: {
                errorText: function () {
                    return errorText;
                }
            } 
        });
      }
    }
  }]);

a dialog, using the angular bootstrap Modal component:

angular.module('myapp').component('errorDialog', {
  templateUrl: 'errorDialog.html',
  controller: function ($scope) {

    $scope.title = "Something went wrong!";
    $scope.error = $scope.errorText || "Generic error";

    $scope.cancel = function () {
      this.dismiss({$value: 'cancel'});
    };
  }
});

...being this the template:

<script type="text/ng-template" id="errorDialog.html">
  <div class="modal-header">
    <h3>{{$title}}</h3>
  </div>
  <div class="modal-body" id="dialogBody">
    <div class="errorText">{{error}}
      <button class="btn btn-primary yes" type="submit" ng-click="cancel()">Ok</button>
    </div>
  </div>
</script>

I can't get errorto display my errorText when using it like this:

ErrorService.showError("Failed to connect.");

Solution

  • You should add bindings to your modal component, and bind it to $scope again with this.$onInit, to make sure the binding happens after resolve has been.. resolved.

    angular.module('myapp').component('errorDialog', {
      bindings: {
        resolve: '<',
        close: '&',
        dismiss: '&'
      },
      templateUrl: 'errorDialog.html',
      controller: function ($scope) {
    
        this.$onInit = function() {
          $scope.error = this.resolve.errorText;
        }
      }
    });
    

    Small recommendation, in your component you can add:

    controllerAs: '$something',
    controller: function() {
    
        // Bind it to this
        var $something = this;
    
        // Use $something instead of $scope from now on
        $something.text = 'Hello';
    
        // Or with resolve
        $something.$onInit = function() {
            $something.error = $something.resolve.errorText;
        }
    }
    

    And then, in your template, you can use:

    <span>{{$something.error}}</span>
    

    Which removes the need for $scope entirely, and makes debugging a lot easier, since everything is contained in it's own scope (in this case, your modal). You binded to errorText to $scope, but still, it was not available in your component. This can be very confusing.

    It's really worth trying to use $scope as less as possible, before you end up with so called Scope soup.