0

I have many input files, and for each input file I have a preview div.

The input file accepts only images, so when I select some image I can preview it in the preview div.

I want also when I select some image to create a scope for it dynamically, what I mean is for example if I have this input file :

<input id="identityDocument" name="identityDocument"
                 ui-jq="filestyle" type="file" class="filestyle input-lg" ui-options="{
                    buttonText: '{{'ACTIONS.UPLOAD' | translate}}',
                    iconName: 'fa fa-inbox'
                  }"
                 accept="image/*">

I want to create a variable for it which called : $scope.candidature.identityDocument

This scope variable will contain only the name of the image I've selected, to save it in the database.

I want to use another scope variable which will contain the image data so I can preview it after onchange event.

Another thing I want is to get required work on the input file since there is no support for input type="file" with the ng-model service.

I've done all this and it works as following :

this is my html code for one input :

<div class="row">
        <div class="form-group col-md-4">
          <label for="identityDocument">{{'FME_CANDIDATURE.PERSONAL_INFORMATIONS.IDENTITY_DOCUMENT' |
            translate}}: </label>
          <input id="identityDocument" name="identityDocument"
                 ui-jq="filestyle" type="file" class="filestyle input-lg" ui-options="{
                    buttonText: '{{'ACTIONS.UPLOAD' | translate}}',
                    iconName: 'fa fa-inbox'
                  }"
                 onchange="angular.element(this).scope().setFile(this,'identityDoc')"
                 accept="image/*">
          <p class="help-block" style="color: #ff635d; !important;">
            <span ng-show="step1.identityDocument.$invalid && !step1.identityDocument.$pristine">{{'FME_CANDIDATURE.PERSONAL_INFORMATIONS.IDENTITY_DOCUMENT_ERROR' | translate}}</span>
          </p>
        </div>
        <div class="form-group col-md-4 mb-0">
          <h4 class="custom-font text-greensea"><strong>{{'ACTIONS.PREVIEW' | translate}}</strong></h4>
          <div><img ng-src="{{identityDoc}}" class="md-card-image img-responsive img-thumbnail"
                    alt="{{'ERRORS.NO_PHOTO_SELECTED' | translate}}"></div>

        </div>
      </div>

in this code I have the input file which only accepts images and when I select some image I call the function setFile(), this function will take two arguments, the current input and the name I want to give to it's scope (since ng-model doesn't work). and I have another div which will contain an img to preview the selected image.

And this is the code of setFile() function :

$scope.setFile = function(element, name) {
      $scope.currentFile = element.files[0];
      let reader = new FileReader();

      reader.onload = function(event) {
        //Converts Angular expression into a function, in our case we convert
        //the string we passed as a variable
        //and then assign a value to the parsed string
        $parse(name).assign($scope, event.target.result);
        //the same as above but instead we passing the name of selected image to the candidature object
        $parse('candidature.'+name).assign($scope, element.value.replace(/^.*[\\\/]/, ''));
        $scope.$apply();

      };
      // when the file is read it triggers the onload event above.
      reader.readAsDataURL(element.files[0]);
    };

so that function takes the input and a string, in that html example I pass the second param as "identityDocument", this function will then create a variable called : $scope.identityDocument which will contain the image data to preview, and $scope.candidature.identityDocument which will contain the name of the image.

to make required works for this input file I used this directive :

app.directive('validFile',function(){
  return {
    require:'ngModel',
    link:function(scope,el,attrs,ngModel){
      //change event is fired when file is selected
      el.bind('change',function(){
        scope.$apply(function(){
          ngModel.$setViewValue(el.val());
          ngModel.$render();
        });
      });
    }
  }
});

as following :

<input ... accept="image/*" valid-file required>

Currently this code works perfectly, but I have to use a directive and a function in the controller, my question is can't I only use one directive which can do all that without using that controller function ?

Edit :

I've added this line $parse(name+'Upload').assign($scope, element.files[0]); to the setFile function, which I use to upload the file as following :

let file = $scope.identityDocUpload;
      let fd = new FormData();
      fd.append('file', file);
      $http.post('http://localhost:8080/fileUpload', fd, {
          transformRequest: angular.identity,
          headers: {'Content-Type': undefined}
        })
        .success(function () {
        })
        .error(function () {
        });
4
  • Why don't you use a directive controller? Commented Apr 19, 2016 at 13:23
  • @TobiasTimm what do you mean ? Commented Apr 19, 2016 at 13:24
  • You could wrap the whole input in a directive and specifiy a controller within the directive.weblogs.asp.net/dwahlin/… there you could set the value and directily verify it Commented Apr 19, 2016 at 13:27
  • @TobiasTimm but how can I create those dynamic scope variables in that case ? Commented Apr 19, 2016 at 13:48

1 Answer 1

0

I would suggest something like

(function () {
    'use strict';

    angular
        .module('myApp')
        .directive('validFile', validFile);

    validFile.$inject = [];

    function validFile() {
        var directive = {
        link: link,
        controller: ValidFileController,
        scope: {
                fileRead: '=',
            }
        };
        ValidFileController.$inject = ['$scope'];
        return directive;

        function link(scope, element, attrs, controller) {
            // do UI-things
        }

         function ValidFileController($scope){
            /* jshint validthis: true */
            var vm = this;
            // do controller things
         }
    }

})();

This is a directive, with a controller. In that case you can execute your whole logic within one directive and don't need a ng-controller.

That ng-model doesnt work for input type="file" is right, but your accomplished code isn't really great, because you are accessing the $scope in the html via it's html attribute.

So your upload technique can be replaced with

 element.bind('change', function (changeEvent) {
            var reader = new FileReader();
            reader.onload = function (loadEvent) {
               scope.$apply(function () {
                    scope.fileRead = loadEvent.target.result;
                 });
            }
            reader.readAsDataURL(changeEvent.target.files[0]);
        });

For your dynamic model values, you can choose between different approaches, f.e.

$scope['yourAttributeName'] = value;

or

var the_string = 'my.test';
var model = $parse(the_string);
model.assign($scope, 'test');
Sign up to request clarification or add additional context in comments.

1 Comment

Updated the answer

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.