Ng-model input type=file [duplicate] - html
I tried to use ng-model on input tag with type file:
<input type="file" ng-model="vm.uploadme" />
But after selecting a file, in controller, $scope.vm.uploadme is still undefined.
How do I get the selected file in my controller?
I created a workaround with directive:
.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]);
});
}
}
}]);
And the input tag becomes:
<input type="file" fileread="vm.uploadme" />
Or if just the file definition is needed:
.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;
});
});
}
}
}]);
I use this directive:
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
});
and invoke it like this:
<input type="file" ng-model="editItem._attachments_uri.image" accept="image/*" app-filereader />
The property (editItem.editItem._attachments_uri.image) will be populated with the contents of the file you select as a data-uri (!).
Please do note that this script will not upload anything. It will only populate your model with the contents of your file encoded ad a data-uri (base64).
Check out a working demo here:
http://plnkr.co/CMiHKv2BEidM9SShm9Vv
How to enable <input type="file"> to work with ng-model
Working Demo of Directive that Works with ng-model
The core ng-model directive does not work with <input type="file"> out of the box.
This custom directive enables ng-model and has the added benefit of enabling the ng-change, ng-required, and ng-form directives to work with <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>
This is an addendum to #endy-tjahjono's solution.
I ended up not being able to get the value of uploadme from the scope. Even though uploadme in the HTML was visibly updated by the directive, I could still not access its value by $scope.uploadme. I was able to set its value from the scope, though. Mysterious, right..?
As it turned out, a child scope was created by the directive, and the child scope had its own uploadme.
The solution was to use an object rather than a primitive to hold the value of uploadme.
In the controller I have:
$scope.uploadme = {};
$scope.uploadme.src = "";
and in the HTML:
<input type="file" fileread="uploadme.src"/>
<input type="text" ng-model="uploadme.src"/>
There are no changes to the directive.
Now, it all works like expected. I can grab the value of uploadme.src from my controller using $scope.uploadme.
I create a directive and registered on bower.
This lib will help you modeling input file, not only return file data but also file dataurl or base 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/
This is a slightly modified version that lets you specify the name of the attribute in the scope, just as you would do with ng-model, usage:
<myUpload key="file"></myUpload>
Directive:
.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'
};
});
For multiple files input using lodash or underscore:
.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);
});
});
}
}
}]);
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);
I had to do same on multiple input, so i updated #Endy Tjahjono method.
It returns an array containing all readed files.
.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] );
}
});
}
}
});
I had to modify Endy's directive so that I can get Last Modified, lastModifiedDate, name, size, type, and data as well as be able to get an array of files. For those of you that needed these extra features, here you go.
UPDATE:
I found a bug where if you select the file(s) and then go to select again but cancel instead, the files are never deselected like it appears. So I updated my code to fix that.
.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] );
}
});
}
}
});
If you want something a little more elegant/integrated, you can use a decorator to extend the input directive with support for type=file. The main caveat to keep in mind is that this method will not work in IE9 since IE9 didn't implement the File API. Using JavaScript to upload binary data regardless of type via XHR is simply not possible natively in IE9 or earlier (use of ActiveXObject to access the local filesystem doesn't count as using ActiveX is just asking for security troubles).
This exact method also requires AngularJS 1.4.x or later, but you may be able to adapt this to use $provide.decorator rather than angular.Module.decorator - I wrote this gist to demonstrate how to do it while conforming to John Papa's AngularJS style guide:
(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;
};
}
})();
Why wasn't this done in the first place? AngularJS support is intended to reach only as far back as IE9. If you disagree with this decision and think they should have just put this in anyway, then jump the wagon to Angular 2+ because better modern support is literally why Angular 2 exists.
The issue is (as was mentioned before) that without the file api
support doing this properly is unfeasible for the core given our
baseline being IE9 and polyfilling this stuff is out of the question
for core.
Additionally trying to handle this input in a way that is not
cross-browser compatible only makes it harder for 3rd party solutions,
which now have to fight/disable/workaround the core solution.
...
I'm going to close this just as we closed #1236. Angular 2 is being
build to support modern browsers and with that file support will
easily available.
Alternatively you could get the input and set the onchange function:
<input type="file" id="myFileInput" />
document.getElementById("myFileInput").onchange = function (event) {
console.log(event.target.files);
};
Try this,this is working for me in angular JS
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);
Related
Separating ViewValue and ModelValue in Angular for a text input
I need to convert a custom element that takes account numbers as input like ABCD1234 and make it display as ****1234. While saving this value/submitting the form I want the value to go as ABCD1234 ie the true value. I have tried making use of $formatters but it seems to be changing the model value as well and saving the * in the value. How do I separate the view value and model value so that the users see only last 4 digits but form saves true value entered. The regex looks for all but last four digit and replaces with * 'use strict'; // #ngdoc directive // #description // directive for masking NPI(Non-public Information) inputs with asterisks. angular.module('MyAPP').directive('npiMask', function() { return { require: 'ngModel', link: function($scope, element, attrs, modelCtrl) { modelCtrl.$formatters.push(function(inputValue) { var transformedInput = inputValue.toString().replace(/.(?=.{4,}$)/g, '*'); if (transformedInput !== inputValue) { modelCtrl.$setViewValue(transformedInput); modelCtrl.$render(); } return transformedInput; }); } }; }); <input-text name="accountNum" label="{{'LOAN_REPAY.ADD_LOAN.ACCOUNT_NUM_LABEL' | translate}}" ng-model="vm.model.formData.loanDetails.accountNum" is-required="true" maxlength="35" locked="vm.isSummaryView" size="4" ui-mask npi-mask > Change view value without change model value ^ this did not work for me.
Ended up using this: 'use strict'; // #ngdoc directive // #description // directive for masking NPI(Non-public Information) inputs with asterisks. angular.module('myApp').directive('npiMask', 'constants', function(constants) { return { require: 'ngModel', link: function($scope, element, attrs, modelCtrl) { var temp = ''; element.on('focusin', function() { element[0].querySelector('#' + modelCtrl.$name).value = temp; }); element.on('focusout', function() { temp = modelCtrl.$modelValue; var transformedInput = temp.toString().replace(constants.patterns.spacesInTheEnd, '*'); element[0].querySelector('#' + modelCtrl.$name).value = transformedInput; }); } }; });
Unable to invoke a custom directive inside another custom directive in AngularJs
I want to invoke a custom directive inside another custom directive's template. Please find below code snippets - Scenario 1 (Not working) angular.module('myApp') .directive('customOnChange', function () { return { restrict: 'A', link: function (scope, element, attrs) { var onChangeFunc = scope.$eval(attrs.customOnChange); element.bind('change', function (event) { var files = event.target.files; onChangeFunc(files); }); element.bind('click', function () { element.val(''); }); } }; }) .directive('writePost', function () { return { restrict: 'E', link: function (scope) { scope.changeUserProfileImage = function (files) { console.log(files); // I should receive uploaded files here. }; }, templateUrl: function () { return 'writePost.html'; } }; }); index.html <write-post></write-post> writePost.html <input type="file" ng-model="file" name="file" id="photo-upload1" custom-on-change="changeUserProfileImage" value="Change Image" title="Change Image"/> The error I am receiving when I upload a file - Uncaught TypeError: onChangeFunc is not a function Scenario 2 (Working) Although independently I am able to call customOnChange directive from index.html. Working code snippet - index.html <input type="file" ng-model="file" name="file" id="photo-upload1" custom-on-change="changeUserProfileImage" value="Change Image" title="Change Image"/> myCtrl.js angular.module('myApp') .controller('myCtrl', ['$scope', function ($scope) { $scope.changeUserProfileImage = function (files) { console.log(files); // I am receiving uploaded files here. }; }]); Can someone help me identifying, where I am going wrong in first scenario ?
link in directive definition defaults to postLink - it executes after template with its directives is parsed. (read more here https://docs.angularjs.org/api/ng/service/$compile#pre-linking-function) As a solution you can move $eval inside callback: element.bind('change', function (event) { var onChangeFunc = scope.$eval(attrs.customOnChange); var files = event.target.files; onChangeFunc(files); }); Correct way: If you want run function - let it be function in html: custom-on-change="changeUserProfileImage(files)" Now run it as function: element.bind('change', function (event) { var files = event.target.files; scope.$eval(attrs.customOnChange, {files: event.target.files}); });
type='file' is not working in angularjs [duplicate]
I tried to use ng-model on input tag with type file: <input type="file" ng-model="vm.uploadme" /> But after selecting a file, in controller, $scope.vm.uploadme is still undefined. How do I get the selected file in my controller?
I created a workaround with directive: .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]); }); } } }]); And the input tag becomes: <input type="file" fileread="vm.uploadme" /> Or if just the file definition is needed: .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; }); }); } } }]);
I use this directive: 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 }); and invoke it like this: <input type="file" ng-model="editItem._attachments_uri.image" accept="image/*" app-filereader /> The property (editItem.editItem._attachments_uri.image) will be populated with the contents of the file you select as a data-uri (!). Please do note that this script will not upload anything. It will only populate your model with the contents of your file encoded ad a data-uri (base64). Check out a working demo here: http://plnkr.co/CMiHKv2BEidM9SShm9Vv
How to enable <input type="file"> to work with ng-model Working Demo of Directive that Works with ng-model The core ng-model directive does not work with <input type="file"> out of the box. This custom directive enables ng-model and has the added benefit of enabling the ng-change, ng-required, and ng-form directives to work with <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>
This is an addendum to #endy-tjahjono's solution. I ended up not being able to get the value of uploadme from the scope. Even though uploadme in the HTML was visibly updated by the directive, I could still not access its value by $scope.uploadme. I was able to set its value from the scope, though. Mysterious, right..? As it turned out, a child scope was created by the directive, and the child scope had its own uploadme. The solution was to use an object rather than a primitive to hold the value of uploadme. In the controller I have: $scope.uploadme = {}; $scope.uploadme.src = ""; and in the HTML: <input type="file" fileread="uploadme.src"/> <input type="text" ng-model="uploadme.src"/> There are no changes to the directive. Now, it all works like expected. I can grab the value of uploadme.src from my controller using $scope.uploadme.
I create a directive and registered on bower. This lib will help you modeling input file, not only return file data but also file dataurl or base 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/
This is a slightly modified version that lets you specify the name of the attribute in the scope, just as you would do with ng-model, usage: <myUpload key="file"></myUpload> Directive: .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' }; });
For multiple files input using lodash or underscore: .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); }); }); } } }]);
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);
I had to do same on multiple input, so i updated #Endy Tjahjono method. It returns an array containing all readed files. .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] ); } }); } } });
I had to modify Endy's directive so that I can get Last Modified, lastModifiedDate, name, size, type, and data as well as be able to get an array of files. For those of you that needed these extra features, here you go. UPDATE: I found a bug where if you select the file(s) and then go to select again but cancel instead, the files are never deselected like it appears. So I updated my code to fix that. .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] ); } }); } } });
If you want something a little more elegant/integrated, you can use a decorator to extend the input directive with support for type=file. The main caveat to keep in mind is that this method will not work in IE9 since IE9 didn't implement the File API. Using JavaScript to upload binary data regardless of type via XHR is simply not possible natively in IE9 or earlier (use of ActiveXObject to access the local filesystem doesn't count as using ActiveX is just asking for security troubles). This exact method also requires AngularJS 1.4.x or later, but you may be able to adapt this to use $provide.decorator rather than angular.Module.decorator - I wrote this gist to demonstrate how to do it while conforming to John Papa's AngularJS style guide: (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; }; } })(); Why wasn't this done in the first place? AngularJS support is intended to reach only as far back as IE9. If you disagree with this decision and think they should have just put this in anyway, then jump the wagon to Angular 2+ because better modern support is literally why Angular 2 exists. The issue is (as was mentioned before) that without the file api support doing this properly is unfeasible for the core given our baseline being IE9 and polyfilling this stuff is out of the question for core. Additionally trying to handle this input in a way that is not cross-browser compatible only makes it harder for 3rd party solutions, which now have to fight/disable/workaround the core solution. ... I'm going to close this just as we closed #1236. Angular 2 is being build to support modern browsers and with that file support will easily available.
Alternatively you could get the input and set the onchange function: <input type="file" id="myFileInput" /> document.getElementById("myFileInput").onchange = function (event) { console.log(event.target.files); };
Try this,this is working for me in angular JS 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);
Is there a way to add a behavior(s) dynamically?
I want to add a behavior after a component/behavior already loaded or a certain function that will add a behevaior to its components. Something like this: <script> // samplebehavior.html file // this is the behavior file samplebehavior = { testAlert: function(){ alert('test'); } }; </script> // my-component.html <script> Polymer({ is: "my-component", test: function() { url = "samplebehavior.html"; var importHTML = new Promise(function(resolve, reject) { Polymer.Base.importHref(url, function(e) { resolve(e.target); }, reject); }); importHTML.then(function(element) { // add a behavior here // I know this script does not work this.push('behaviors', samplebehavior); }); } }); </script> So that I can access the testAlert() function. How to I add a behavior dynamically?
To the best of knowledge it's not possible. Behaviors are mixed in with the element definition when the prototype is built. What you could do is generate the behaviors array dynamically var behavior = { properties: { smth: { value: 'initial value' } } } Polymer({ is: 'my-elem', behaviors: getBehaviors() }); function getBehaviors() { return [ behavior ]; } Remember though that getBehaviors will only ever be called once. After that you won't be able to change behaviors of your elements.
It's ugly but you can copy all members of your behavior in your object <script> // samplebehavior.html file // this is the behavior file samplebehavior = { testAlert: function(){ alert('test'); } }; </script> // my-component.html <script> Polymer({ is: "my-component", test: function() { url = "samplebehavior.html"; var importHTML = new Promise(function(resolve, reject) { Polymer.Base.importHref(url, function(e) { resolve(e.target); }, reject); }); importHTML.then(function(element) { for (let member in samplebehavior) { this[member] = samplebehavior[member]; } }); } }); </script> Or maybe you can call the internal method _prepBehavior() https://github.com/Polymer/polymer/blob/ff6e884ef4f309d41491333860a8bc9c2f178696/src/micro/behaviors.html#L111 But i don't know if that can do side effects <script> // samplebehavior.html file // this is the behavior file samplebehavior = { testAlert: function(){ alert('test'); } }; </script> // my-component.html <script> Polymer({ is: "my-component", test: function() { url = "samplebehavior.html"; var importHTML = new Promise(function(resolve, reject) { Polymer.Base.importHref(url, function(e) { resolve(e.target); }, reject); }); importHTML.then(function(element) { this.push('behaviors', samplebehavior); this._prepBehaviors(); }); } }); </script>
With introduction to new value for lazyRegister setting this has become partially possible. By partially i mean you can only do this if you can edit the code of the element in which you want to add the behavior. Three changes you'll require to add behavior dynamically are Set lazyRegister setting to max window.Polymer = { lazyRegister:"max" }; Just like #tomasz suggested, in the element you want to add behavior dynamically add behaviors using a function. This is so, because we won't be able to access the element from where we'll try to add new behavior dynamically(atleast i was not able to). Polymer({ . . behaviors: getBehavior(), . . }); function getBehavior(){ var myArr = [myBehavior]; document.addEventListener('add-behavior',function(e){ debugger; myArr.push(e.detail); }); return myArr; } From the element where you want to dynamically add the behavior use beforeRegister callback to dispatch an event adding the new behavior object beforeRegister: function(){ var event = new CustomEvent('add-behavior',{ detail:{ func: function(){console.log(this.myProp,"!")} } }); document.dispatchEvent(event); } Here's a plunkr for working example.
Backbone using external js
Hi all I have a site developed in cakephp and I would to integrate backbone on it. For my scope I would to use external js for backbone to reuse the code. I have write some lines but I can't append results on my element. I have tried to print the "el" in this modes: console.log($(this.el)); console.log(this.el); console.log(this.$el); But nothing I can't enter into el to make a simple append! The container #search-results already exist This is my main view: <script type="text/javascript"> var search = {}; search.product = {}; search.product.template = "#results-product-template"; search.product.container = "#search-results"; search.product.defaults = { id:0, type:"product", }; $(function(){ var ProductList = new Search.Collections.Products(); var ProductView = new Search.Views.Product({ // new Search.Collections.Products(); collection:ProductList ,el:$("#search-results") }); function parseResults () { var json = { //my data } for (var i = json.products.length - 1; i >= 0; i--) { ProductList.add([new Search.Models.Product(json.products[i])]); }; updateResults(); } function updateResults () { console.log('updateResults: Ritorno il risultato quando hunter riceve una risposta dal server'); if ($('#search-results').length == 0) { $('div.main > section:first-child').before('<section id="search-results"> <ul id="product-results"> <li>Contenuto</li> </ul> </section>'); } ProductView.render(); } // search $('#search-results .close').on('click', function () { $('#search-results').animate({height:0}, 500, function () { $(this).remove(); }) }); }); </script> And this is my external js with backbone var Search = { Models: {}, Collections: {}, Views: {}, Templates:{} } Search.Models.Product = Backbone.Model.extend({ defaults: search.product.defaults || {}, toUrl:function (url) { return url.replace(" ", "-").toLowerCase(); }, initialize:function () { console.log("initialize Search.Models.Product"); this.on("change", function (){ console.log("chiamato evento change del Model Search.Models.Product"); }); this.on("change:text", function () { console.log("chiamato evento change:text del Model Search.Models.Product"); }); } }); Search.Collections.Products = Backbone.Collection.extend({ model: Search.Models.Product, initialize:function () { console.log("initialize Search.Collections.Products"); console.log(this); console.log(this.length); console.log(this.models); } }); Search.Views.Product = Backbone.View.extend({ initialize:function () { console.log("initialize Search.Views.Product"); console.log($(search.product.template).html()); }, template:function (data) { if (data == null) { data = this.collection.toJSON(); } var template = Handlebars.compile($(search.product.template).html()); template(data); }, render:function () { console.log($(this.el)); $(this.el.append("TEST")); //HERE IS THE PROBLEM // I have tried this.$el.append("TEST"); return this; } });
Does this change anything? var ProductView = new Search.Views.Product({ // new Search.Collections.Products(); collection:ProductList, el:$("#search-results")[0] }); I think backbone can accept both jQuery wrapped or not wrapped object and be fine, but I don't know what Backbone version you are using, see if this works EDIT: From backbone 1.0 sources, it seems backbone can indeed take either a jQuery wrapped object or a regular dom element, it should still work this.$el = element instanceof Backbone.$ ? element : Backbone.$(element); Do you have something online (JSFiddle?) I will be happy to take a look, but this.$el should work and be equal to $("#search-results") from your code in a quick glance. Have you tried using ProductView.setElement($("#search-results")) instead? it should be the same, but worth a try as well.