एनजी-मॉडल के लिए `<इनपुट प्रकार =" फ़ाइल "/>` (निर्देश DEMO के साथ)


281

मैंने टाइप फ़ाइल के साथ इनपुट टैग पर एनजी-मॉडल का उपयोग करने की कोशिश की:

<input type="file" ng-model="vm.uploadme" />

लेकिन नियंत्रक में एक फ़ाइल का चयन करने के बाद, $ गुंजाइश.vm.uploadme अभी भी अपरिभाषित है।

मैं अपने नियंत्रक में चयनित फ़ाइल कैसे प्राप्त करूं?


3
देखें stackoverflow.com/a/17923521/135114 , विशेष रूप से उद्धृत उदाहरण ऑनलाइन पर jsfiddle.net/danielzen/utp7j
Daryn

मेरा मानना ​​है कि आपको हमेशा ngModel का उपयोग करते समय html तत्व पर नाम संपत्ति निर्दिष्ट करने की आवश्यकता होती है।
सैम

जवाबों:


327

मैंने निर्देशन के साथ वर्कअराउंड बनाया:

.directive("fileread", [function () {
    return {
        scope: {
            fileread: "="
        },
        link: function (scope, element, attributes) {
            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]);
            });
        }
    }
}]);

और इनपुट टैग बन जाता है:

<input type="file" fileread="vm.uploadme" />

या अगर सिर्फ फ़ाइल परिभाषा की जरूरत है:

.directive("fileread", [function () {
    return {
        scope: {
            fileread: "="
        },
        link: function (scope, element, attributes) {
            element.bind("change", function (changeEvent) {
                scope.$apply(function () {
                    scope.fileread = changeEvent.target.files[0];
                    // or all selected files:
                    // scope.fileread = changeEvent.target.files;
                });
            });
        }
    }
}]);

4
मैं एक img टैग में src के रूप में uploadme का उपयोग करता हूं, इसलिए मैं देख सकता हूं कि यह निर्देशन द्वारा सेट हो रहा है। हालांकि, अगर मैं $ गुंजाइश का उपयोग करके नियंत्रक से इसे हथियाने की कोशिश करता हूं। लोडलोडे, यह "अपरिभाषित" है। मैं नियंत्रक से uploadme सेट कर सकते हैं, यद्यपि। उदाहरण के लिए, $ गुंजाइश.uploadme = "*" छवि को गायब कर देता है।
प्रति क्वेस्ट Aronsson

5
समस्या यह है कि निर्देश एक चाइल्डस्कोप बनाता है, और अपलोडमे को उस दायरे में सेट करता है। मूल (मूल) स्कोप में एक अपलोडमे भी है, जो चाइल्डस्कोप द्वारा अप्रभावित है। मैं या तो गुंजाइश से HTML में अपलोड को अपडेट कर सकता हूं। क्या चाइल्डस्कोप बनाने से बचने का एक तरीका है?
प्रति क्वेस्ट Aronsson

4
@AlexC में सवाल यह था कि एनजी-मॉडल काम नहीं कर रहा था, फाइल अपलोड करने के बारे में नहीं :) उस समय मुझे फाइल अपलोड करने की जरूरत नहीं थी। लेकिन हाल ही में मैंने सीखा कि इस egghead.io ट्यूटोरियल से फाइल कैसे अपलोड करें ।
एंडी तजहोनो

10
$ स्कोप को मत भूलिए। $ ('$ डेस्टोरी', फंक्शन () {
एलिमेंट

2
मेरे पास एक सवाल है .... क्या इस तरह से सादे जावास्क्रिप्ट और HTML की तुलना में बहुत जटिल नहीं है? सच में, आपको वास्तव में इस समाधान तक पहुंचने के लिए एंगुलरजेएस को समझने की जरूरत है ... और ऐसा लगता है कि मैं एक जावास्क्रिप्ट घटना के साथ भी ऐसा कर सकता था। यह कोणीय तरीके से क्यों नहीं और सादे जेएस तरीके से क्यों?
AFP_555

53

मैं इस निर्देश का उपयोग करता हूं:

angular.module('appFilereader', []).directive('appFilereader', function($q) {
    var slice = Array.prototype.slice;

    return {
        restrict: 'A',
        require: '?ngModel',
        link: function(scope, element, attrs, ngModel) {
                if (!ngModel) return;

                ngModel.$render = function() {};

                element.bind('change', function(e) {
                    var element = e.target;

                    $q.all(slice.call(element.files, 0).map(readFile))
                        .then(function(values) {
                            if (element.multiple) ngModel.$setViewValue(values);
                            else ngModel.$setViewValue(values.length ? values[0] : null);
                        });

                    function readFile(file) {
                        var deferred = $q.defer();

                        var reader = new FileReader();
                        reader.onload = function(e) {
                            deferred.resolve(e.target.result);
                        };
                        reader.onerror = function(e) {
                            deferred.reject(e);
                        };
                        reader.readAsDataURL(file);

                        return deferred.promise;
                    }

                }); //change

            } //link
    }; //return
});

और इसे इस तरह लागू करें:

<input type="file" ng-model="editItem._attachments_uri.image" accept="image/*" app-filereader />

संपत्ति (editItem.editItem._attachments_uri.image) आपके द्वारा डेटा-उरी (!) के रूप में चुनी गई फ़ाइल की सामग्री के साथ आबाद हो जाएगी।

कृपया ध्यान दें कि यह स्क्रिप्ट कुछ भी अपलोड नहीं करेगी। यह केवल आपके मॉडल को आपकी फ़ाइल एन्कोडेड विज्ञापन डेटा-यूआरआई (बेस 64) की सामग्री के साथ आबाद करेगा।

यहां एक वर्किंग डेमो देखें: http://plnkr.co/CMiHKv2BEidM9SShm9Vv


4
होनहार देखो, क्या आप कोड के पीछे के तर्क की व्याख्या कर सकते हैं, और ब्राउज़र संगतता के बारे में टिप्पणी कर सकते हैं (आईई और गैर-फाइलएपीआई ब्राउज़र ज्यादातर)?
ओलेग बेलौसोव

इसके अलावा, मेरी समझ में सबसे अच्छी तरह से, अगर मैं AJAX अनुरोध के सामग्री-प्रकार के हेडर को अपरिभाषित कर दूंगा, और इस तरह के क्षेत्र को सर्वर पर भेजने का प्रयास करूंगा, कोणीय इसे अपलोड करेगा, यह मानते हुए कि ब्राउज़र fileAPI का समर्थन करता है, am मैं सही?
ओलेग बेलौसोव

@OlegTikhonov आप सही नहीं हैं! यह स्क्रिप्ट कुछ भी नहीं भेजेगी। यह आपके द्वारा चुनी गई फ़ाइल को बेस 64 स्ट्रिंग के रूप में पढ़ेगा और उस स्ट्रिंग के साथ अपने मॉडल को अपडेट करेगा।
एल्मर

@ ईमर हां, मैं समझता हूं, मेरे कहने का मतलब यह है कि एक फाइल फील्ड (फाइलएपीआई ऑब्जेक्ट में उपयोगकर्ता की मशीन में फाइल के लिए एक रिश्तेदार पथ) को भेजने से आप कोणीय को एक्सएचआर अनुरोध द्वारा फाइल अपलोड कर सकते हैं अपरिभाषित करने के लिए सामग्री प्रकार हेडर सेट करके
ओलेग बेलौसोव

1
Overwritting का उद्देश्य क्या है ngModelके $renderसमारोह?
sp00m

39

कैसे <input type="file">काम करने के लिए सक्षम करने के लिएng-model

निर्देशन का कार्य डेमो जो साथ काम करता है ng-model

मुख्य ng-modelनिर्देश <input type="file">बॉक्स से बाहर काम नहीं करता है ।

यह कस्टम निर्देश सक्षम बनाता है ng-modelऔर सक्षम बनाने के अतिरिक्त लाभ है ng-change, ng-requiredऔर ng-formसाथ काम करने के लिए निर्देशों <input type="file">

angular.module("app",[]);

angular.module("app").directive("selectNgFiles", function() {
  return {
    require: "ngModel",
    link: function postLink(scope,elem,attrs,ngModel) {
      elem.on("change", function(e) {
        var files = elem[0].files;
        ngModel.$setViewValue(files);
      })
    }
  }
});
<script src="//unpkg.com/angular/angular.js"></script>
  <body ng-app="app">
    <h1>AngularJS Input `type=file` Demo</h1>
    
    <input type="file" select-ng-files ng-model="fileArray" multiple>

    <code><table ng-show="fileArray.length">
    <tr><td>Name</td><td>Date</td><td>Size</td><td>Type</td><tr>
    <tr ng-repeat="file in fileArray">
      <td>{{file.name}}</td>
      <td>{{file.lastModified | date  : 'MMMdd,yyyy'}}</td>
      <td>{{file.size}}</td>
      <td>{{file.type}}</td>
    </tr>
    </table></code>
    
  </body>


आप यह जाँचने के लिए शर्त का उपयोग कर सकते हैं कि क्या कोई चयनित फ़ाइल नहीं है, एनजी-मॉडल अपरिभाषित हो **** यदि (files.length> 0) {ngModel। $ SetViewValue (फाइलें); } और {ngModel। $ setViewValue (अपरिभाषित); }
फर्षद काजेमी

फाइल का डाटा कैसे प्राप्त करें? और हम अन्य विशेषताओं का क्या उपयोग कर सकते हैं जैसे {{file.name}}
आदर्श सिंह


26

यह @ endy-tjahjono के समाधान का एक परिशिष्ट है।

मैं समाप्त हो गया है कि स्कोप से अपलोडमे का मान नहीं पा रहा हूँ । भले ही HTML में Uploadme निर्देश द्वारा दृष्टि से अपडेट किया गया था, फिर भी मैं $ value.uploadme द्वारा इसके मूल्य तक नहीं पहुंच सका। मैं हालांकि इसके मूल्य को दायरे से निर्धारित करने में सक्षम था। रहस्यमय, सही ..?

जैसा कि यह पता चला है, निर्देश द्वारा एक बच्चा गुंजाइश बनाई गई थी, और बच्चे के दायरे का अपना अपलोडमेम था

समाधान यह था कि अपलोडमेम के मान को रखने के लिए किसी आदिम के बजाय किसी वस्तु का उपयोग किया जाए

नियंत्रक में मेरे पास है:

$scope.uploadme = {};
$scope.uploadme.src = "";

और HTML में:

 <input type="file" fileread="uploadme.src"/>
 <input type="text" ng-model="uploadme.src"/>

निर्देश में कोई बदलाव नहीं हैं।

अब, यह सब उम्मीद की तरह काम करता है। मैं $ दायरे का उपयोग करके अपने नियंत्रक से uploadme.src के मूल्य को हड़प सकता हूँ।


1
हाँ, यह ठीक है।
प्रति क्वेस्ट Aronsson

1
मैं उसी अनुभव की पुष्टि करता हूं; बहुत अच्छा डिबग और स्पष्टीकरण। मुझे यकीन नहीं है कि निर्देश अपना दायरा क्यों बना रहा है।
एडम केसी

वैकल्पिक रूप से, एक इनलाइन घोषणा:$scope.uploadme = { src: '' }
मैथुशंडंड

9

हाय दोस्तों मैं एक निर्देश और bower पर पंजीकृत बनाने के लिए।

यह दायित्व आपको इनपुट फ़ाइल मॉडलिंग में मदद करेगा, न केवल फ़ाइल डेटा लौटाएगा, बल्कि डेटाअरल या बेस 64 भी फ़ाइल कर सकता है।

{
    "lastModified": 1438583972000,
    "lastModifiedDate": "2015-08-03T06:39:32.000Z",
    "name": "gitignore_global.txt",
    "size": 236,
    "type": "text/plain",
    "data": "data:text/plain;base64,DQojaWdub3JlIHRodW1ibmFpbHMgY3JlYXRlZCBieSB3aW5kb3dz…xoDQoqLmJhaw0KKi5jYWNoZQ0KKi5pbGsNCioubG9nDQoqLmRsbA0KKi5saWINCiouc2JyDQo="
}

https://github.com/mistralworks/ng-file-model/

आशा है आप मदद करेंगे


$ स्कोप का उपयोग करके इसका उपयोग कैसे करें? मैंने इसका उपयोग करने की कोशिश की, लेकिन डिबगिंग के दौरान अपरिभाषित हो गया।
गुजरात संताना

1
अच्छा काम yozawiratama! यह अच्छा काम करता है। और @GujaratSantana, यदि <input type = "file" ng-file-model = "myDocument" /> तो बस $ गुंजाइश का उपयोग करें ।myDocument.name या सामान्य $ गुंजाइश में ।myDocument। <कोई भी संपत्ति जहाँ संपत्ति एक हो। ["lastModified", "lastModifiedDate", "name", "size", "type", "data"]
Jay Dharmendra Solanki

बोवर के माध्यम से स्थापित नहीं किया जा सकता
Pimgd

एकाधिक फ़ाइल अपलोड के लिए उपयोगकर्ता कैसे?
aldrien.h

reader.readAsDataURLविधि अप्रचलित है। आधुनिक कोड URL.create ObjectURL () का उपयोग करता है ।
georgeawg

4

यह थोड़ा संशोधित संस्करण है जो आपको कार्यक्षेत्र में विशेषता के नाम को निर्दिष्ट करने देता है, जैसा कि आप एनजी-मॉडल, उपयोग के साथ करते हैं:

    <myUpload key="file"></myUpload>

निर्देशक:

.directive('myUpload', function() {
    return {
        link: function postLink(scope, element, attrs) {
            element.find("input").bind("change", function(changeEvent) {                        
                var reader = new FileReader();
                reader.onload = function(loadEvent) {
                    scope.$apply(function() {
                        scope[attrs.key] = loadEvent.target.result;                                
                    });
                }
                if (typeof(changeEvent.target.files[0]) === 'object') {
                    reader.readAsDataURL(changeEvent.target.files[0]);
                };
            });

        },
        controller: 'FileUploadCtrl',
        template:
                '<span class="btn btn-success fileinput-button">' +
                '<i class="glyphicon glyphicon-plus"></i>' +
                '<span>Replace Image</span>' +
                '<input type="file" accept="image/*" name="files[]" multiple="">' +
                '</span>',
        restrict: 'E'

    };
});

3

कई फ़ाइलों के लिए लॉश या अंडरस्कोर का उपयोग कर इनपुट:

.directive("fileread", [function () {
    return {
        scope: {
            fileread: "="
        },
        link: function (scope, element, attributes) {
            element.bind("change", function (changeEvent) {
                return _.map(changeEvent.target.files, function(file){
                  scope.fileread = [];
                  var reader = new FileReader();
                  reader.onload = function (loadEvent) {
                      scope.$apply(function () {
                          scope.fileread.push(loadEvent.target.result);
                      });
                  }
                  reader.readAsDataURL(file);
                });
            });
        }
    }
}]);

3

function filesModelDirective(){
  return {
    controller: function($parse, $element, $attrs, $scope){
      var exp = $parse($attrs.filesModel);
      $element.on('change', function(){
        exp.assign($scope, this.files[0]);
        $scope.$apply();
      });
    }
  };
}
app.directive('filesModel', filesModelDirective);

फ़ाइल ऑब्जेक्ट को वापस करने के लिए यश । अन्य निर्देश जो इसे एक DataURL में परिवर्तित करते हैं, यह उन नियंत्रकों के लिए मुश्किल बनाता है जो फ़ाइल अपलोड करना चाहते हैं।
georgeawg

2

मुझे कई इनपुट पर समान करना था, इसलिए मैंने @Endy Tjahjono विधि को अपडेट किया। यह सभी पढ़ी गई फ़ाइलों वाली एक सरणी देता है।

  .directive("fileread", function () {
    return {
      scope: {
        fileread: "="
      },
      link: function (scope, element, attributes) {
        element.bind("change", function (changeEvent) {
          var readers = [] ,
              files = changeEvent.target.files ,
              datas = [] ;
          for ( var i = 0 ; i < files.length ; i++ ) {
            readers[ i ] = new FileReader();
            readers[ i ].onload = function (loadEvent) {
              datas.push( loadEvent.target.result );
              if ( datas.length === files.length ){
                scope.$apply(function () {
                  scope.fileread = datas;
                });
              }
            }
            readers[ i ].readAsDataURL( files[i] );
          }
        });

      }
    }
  });

1

मुझे एंडी के निर्देश को संशोधित करना था ताकि मैं लास्ट मोडिफाइड, लास्ट मॉडिफाईडडेट, नाम, साइज, टाइप, और डेटा प्राप्त कर सकूं और साथ ही फाइलों की एक सरणी प्राप्त कर सकूं। आप में से उन लोगों के लिए जिन्हें इन अतिरिक्त सुविधाओं की आवश्यकता थी, यहां आप जाएं।

अद्यतन करें: मुझे एक बग मिला जहां आप फ़ाइल का चयन करते हैं और फिर फिर से चयन करने के लिए जाते हैं, लेकिन इसके बजाय रद्द करें, फ़ाइलों को कभी भी ऐसा नहीं हटाया जाता है जैसे यह दिखाई देता है। इसलिए मैंने उसे ठीक करने के लिए अपना कोड अपडेट किया।

 .directive("fileread", function () {
        return {
            scope: {
                fileread: "="
            },
            link: function (scope, element, attributes) {
                element.bind("change", function (changeEvent) {
                    var readers = [] ,
                        files = changeEvent.target.files ,
                        datas = [] ;
                    if(!files.length){
                        scope.$apply(function () {
                            scope.fileread = [];
                        });
                        return;
                    }
                    for ( var i = 0 ; i < files.length ; i++ ) {
                        readers[ i ] = new FileReader();
                        readers[ i ].index = i;
                        readers[ i ].onload = function (loadEvent) {
                            var index = loadEvent.target.index;
                            datas.push({
                                lastModified: files[index].lastModified,
                                lastModifiedDate: files[index].lastModifiedDate,
                                name: files[index].name,
                                size: files[index].size,
                                type: files[index].type,
                                data: loadEvent.target.result
                            });
                            if ( datas.length === files.length ){
                                scope.$apply(function () {
                                    scope.fileread = datas;
                                });
                            }
                        };
                        readers[ i ].readAsDataURL( files[i] );
                    }
                });

            }
        }
    });

1

यदि आप कुछ अधिक सुरुचिपूर्ण / एकीकृत चाहते हैं, तो आप समर्थन के साथ निर्देश का विस्तार करने के लिए एक डेकोरेटर का उपयोग कर सकते हैं । ध्यान में रखने के लिए मुख्य चेतावनी यह है कि यह विधि IE9 में काम नहीं करेगी क्योंकि IE9 ने फ़ाइल एपीआई को लागू नहीं किया था । XHR के माध्यम से प्रकार की परवाह किए बिना द्विआधारी डेटा अपलोड करने के लिए जावास्क्रिप्ट का उपयोग करना IE9 या पूर्व में मूल रूप से संभव नहीं है ( स्थानीय फाइल सिस्टम तक पहुंचने के लिए उपयोग नहीं करता है क्योंकि ActiveX का उपयोग केवल सुरक्षा समस्याओं के लिए पूछ रहा है)।inputtype=fileActiveXObject

इस सटीक विधि के लिए भी AngularJS 1.4.x या बाद के संस्करण की आवश्यकता होती है, लेकिन आप इसके $provide.decoratorबजाय इसका उपयोग करने के लिए इसे अनुकूलित करने में सक्षम हो सकते हैं angular.Module.decorator- मैंने यह दिखाने के लिए जॉन पापा की AngularJS शैली मार्गदर्शिका के अनुरूप यह प्रदर्शित करने का तरीका लिखा है :

(function() {
    'use strict';

    /**
    * @ngdoc input
    * @name input[file]
    *
    * @description
    * Adds very basic support for ngModel to `input[type=file]` fields.
    *
    * Requires AngularJS 1.4.x or later. Does not support Internet Explorer 9 - the browser's
    * implementation of `HTMLInputElement` must have a `files` property for file inputs.
    *
    * @param {string} ngModel
    *  Assignable AngularJS expression to data-bind to. The data-bound object will be an instance
    *  of {@link https://developer.mozilla.org/en-US/docs/Web/API/FileList `FileList`}.
    * @param {string=} name Property name of the form under which the control is published.
    * @param {string=} ngChange
    *  AngularJS expression to be executed when input changes due to user interaction with the
    *  input element.
    */
    angular
        .module('yourModuleNameHere')
        .decorator('inputDirective', myInputFileDecorator);

    myInputFileDecorator.$inject = ['$delegate', '$browser', '$sniffer', '$filter', '$parse'];
    function myInputFileDecorator($delegate, $browser, $sniffer, $filter, $parse) {
        var inputDirective = $delegate[0],
            preLink = inputDirective.link.pre;

        inputDirective.link.pre = function (scope, element, attr, ctrl) {
            if (ctrl[0]) {
                if (angular.lowercase(attr.type) === 'file') {
                    fileInputType(
                        scope, element, attr, ctrl[0], $sniffer, $browser, $filter, $parse);
                } else {
                    preLink.apply(this, arguments);
                }
            }
        };

        return $delegate;
    }

    function fileInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
        element.on('change', function (ev) {
            if (angular.isDefined(element[0].files)) {
                ctrl.$setViewValue(element[0].files, ev && ev.type);
            }
        })

        ctrl.$isEmpty = function (value) {
            return !value || value.length === 0;
        };
    }
})();

यह पहली जगह में क्यों नहीं किया गया? AngularJS समर्थन IE9 के रूप में केवल वापस तक पहुँचने का इरादा है। यदि आप इस निर्णय से असहमत हैं और सोचते हैं कि उन्हें इसे वैसे भी लागू करना चाहिए था, तो वैगन को एंगुलर 2+ पर कूदें क्योंकि बेहतर आधुनिक समर्थन का शाब्दिक अर्थ है कि कोणीय 2 मौजूद है।

मुद्दा यह है (जैसा कि पहले उल्लेख किया गया था) कि फाइल एपीआई समर्थन के बिना यह ठीक से कर रहा है कोर के लिए हमारे बेसलाइन IE9 दिया जा रहा है और इस सामान को पोलीफ़िलिंग कोर के लिए सवाल से बाहर है।

इसके अतिरिक्त इस इनपुट को ऐसे तरीके से संभालने की कोशिश की जाती है जो क्रॉस-ब्राउज़र संगत न हो, केवल 3 पार्टी समाधान के लिए कठिन बना देता है, जिसे अब कोर समाधान से लड़ना / अक्षम करना / कार्य करना है।

...

मैं इसे बंद करने जा रहा हूँ जैसे हमने # 1236 को बंद किया। आधुनिक ब्राउज़रों का समर्थन करने के लिए कोणीय 2 का निर्माण किया जा रहा है और इसके साथ फ़ाइल समर्थन आसानी से उपलब्ध होगा।


-2

यह कोशिश करो, यह मेरे लिए कोणीय जेएस में काम कर रहा है

    let fileToUpload = `${documentLocation}/${documentType}.pdf`;
    let absoluteFilePath = path.resolve(__dirname, fileToUpload);
    console.log(`Uploading document ${absoluteFilePath}`);
    element.all(by.css("input[type='file']")).sendKeys(absoluteFilePath);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.