Search code examples
javascriptangularjsng-file-uploadng-img-crop

angular material modal and ng-file-upload


I am working on image upload using angular material, ng-file-upload, and ng-imgcrop-extended. I was previously using all of this on a normal page, and everything was working fine, but requirements have changed and I had to move this logic to a modal.

The way it works is I am using ng-imgcrop to crop the photos, and ng-file-upload does the uploading. So right now, I have an element listening to the file select, and that handles the cropping. Right now however, it is not listening to the file select, and I can only reason that is from the modal.

Here is my code

modal render

$scope.headshotModal = function(ev) {
  var useFullScreen;
  useFullScreen = ($mdMedia('sm') || $mdMedia('xs')) && $scope.customFullscreen;
  $mdDialog.show({
    locals: {
      p: $scope.persona
    },
    controller: 'ImagesController',
    templateUrl: 'application/views/images/image_modal.html',
    parent: angular.element(document.body),
    targetEvent: ev,
    clickOutsideToClose: true,
    fullscreen: useFullScreen
  }).then((function(answer) {
    $scope.status = 'You said the information was "' + answer + '".';
  }), function() {
    $scope.status = 'You cancelled the dialog.';
  });
  $scope.$watch((function() {
    return $mdMedia('xs') || $mdMedia('sm');
  }), function(wantsFullScreen) {
    $scope.customFullscreen = wantsFullScreen === true;
  });
};

images_controller

angular.module('App').controller('ImagesController', [
  '$scope', 'p', '$mdDialog', 'ImageService', '$routeParams', function($scope, p, $mdDialog, ImageService, $routeParams) {
    var handleFileSelect;
    $scope.persona = p;
    $scope.uploadedImg = false;
    $scope.myCroppedImage = '';
    $scope.myImage = '';
    $scope.blockingObject = {
      block: true
    };
    $scope.callTestFuntion = function() {
      $scope.blockingObject.render(function(dataURL) {
        $scope.showRender = true;
        console.log('via render');
        console.log(dataURL.length);
      });
    };
    $scope.blockingObject.callback = function(dataURL) {
      console.log('via function');
      console.log(dataURL.length);
    };
    handleFileSelect = function(evt) {
      var file, reader;
      file = evt.currentTarget.files[0];
      console.log(file);
      $scope.uploadedImg = true;
      reader = new FileReader;
      reader.onload = function(evt) {
        $scope.$apply(function($scope) {
          $scope.myImage = evt.target.result;
        });
      };
      reader.readAsDataURL(file);
    };
    angular.element(document.querySelector('#imgInput')).on('change', function() {
      console.log('handlefileselect');
      // this function runs the code needed. it is not being triggered
      handleFileSelect;
    });
    $scope.thenRedirect = function() {
      return window.location.href = "personas/" + $scope.persona.slug;
    };
    $scope.testCrop = function(file) {
      ImageService.uploadCroppedImg(file, 'headshot', $routeParams, $scope.cropAttributes);
      return $scope.thenRedirect();
    };
    $scope.cancel = function() {
      $scope.uploadedImg = false;
      return $scope.showRender = false;
    };
    $scope.hide = function() {
      $mdDialog.hide();
    };
    return $scope.cancelOut = function() {
      $mdDialog.cancel();
    };
  }
]);

modal.slim

md-dialog.fs [style="width: 100%; margin-left:25%; margin-right: 25%;" aria-label=("Image Edit") ng-cloak=""]
  /form
  md-toolbar.text-center
    .md-toolbar-tools
      h2 Image Edit
      span flex=""
      md-button.md-icon-button [ng-click="cancelOut()" aria-label="Cancel"]
        i.fa.fa-times
  md-dialog-content
    .md-dialog-content
      h2.text-center Edit Your Headshot

      div.input-div
        | Select an image file:
        input#imgInput [type="file" ngf-select accept="image/*" ng-model="headshotFile"] /
      / [ng-show='uploadedImg']
      div
        md-button.render-btn[ng-click="callTestFuntion()"]  Render
        .crop-area
          img-crop cropject='cropAttributes' area-type="rectangle" image="myImage" live-view="blockingObject" result-image="myCroppedImage"

      a.img-upload [href="#" ngf-select="uploadBanner($file)" ngf-dimensions="$width > 149 && $height > 149"]
        i.fa.fa-camera
        span Banner

      a.img-upload[style='cursor: pointer;'ng-click="testCrop(headshotFile)"]
        i.fa.fa-upload
        span Upload

      a.cancel-img.img-upload [href="#" ng-click="cancel()"]
        i.fa.fa-ban
        span Cancel

this code works on a normal html page. But the problem seems to be it cannot listen to the angular.element(document.querySelector('#imgInput')).on('change') part of the ImagesController. does anyone know how using a modal, I can handle these types of events? I have seen that I might have to wrap some logic in the $mdDialog.show().resolve() function, but i'm not sure what it's expecting.

Any help would be appreciated :)


Solution

  • Based on your results, I would approach this problem by wiring up the event in the onShowing or onComplete event for the dialog. I would create a callback function here:

    $mdDialog.show({
        locals: {
          p: $scope.persona
        },
        controller: 'ImagesController',
        templateUrl: 'application/views/images/image_modal.html',
        parent: angular.element(document.body),
        targetEvent: ev,
        clickOutsideToClose: true,
        fullscreen: useFullScreen,
        onComplete: wireUpChangeEvent,
        onRemoving: removeChangeEvent // so it doesn't get wired up multiple times
      })
    

    I'm not 100%, but I think the dialog stays on the DOM after you hide(close) it. If that's the case, you either need the onRemoving event function or a check to see if it's already been wired up to prevent multiple firings. The function would be called from the controller that launches the dialog. You may need to make the two of them share the same scope by using the scope option and telling it to preserveScope. I'm also not sure if the template will be loaded and on the DOM the first time onShowing is called, so it's probably safer to use onComplete.