I want to call a function from controller whenever the input file selection changed or a user selected a file. but it's not working.
<input id="selected-images" accept="image/*" type="file" (change)="upload.selectFile($event)" multiple />
<button id="select-image" class="btn btn-primary-alt " style="border-radius: 8px; padding: 15px 25px;" type="button" onclick="$('#selected-images').click();">Upload from Storage</button>
This is the function selectFile from controller:
$scope.selectFile = function (event) {
console.log('awdawdawdawdaw');
vm.selectedFiles = event.target.files;
console.log('====================================');
console.log('this is the selected file from selectFile function');
console.log(vm.selectedFiles);
console.log('====================================');
}
Not sure why you nest the selectFile(event) function in an object named upload, but adding the following code in the typescript code of the component definitely gets called and the file list gets logged:
upload = {
selectFile: (event) => {
const element = event.currentTarget as HTMLInputElement;
let fileList: FileList | null = element.files;
if (fileList) {
console.log(fileList);
}
}
}
Please also take a look here: file Input Event type in Angular
Related
im pretty new at coding, currently studing front end dev. I´m on my 5:th week learning JS and got a challange to create a toDo list with Typescript.
When i create a task in my app it has a checkbox and a "trash"-button. The idéa of the trash button is pretty clear and the checkbox is going put the task last in the list when its "checked".
I noticed some repetative code while creating my event listeners. So I found a way to add the event listener to multiple elements but I can't wrap my head around how to call the different functions that corresponds to what was clicked.
this is how I found a way to add the event listener to each element. But from here, how can I call a function based on what was clicked?
document.querySelectorAll('.add-click-listener').forEach(item => {
item.addEventListener('click', event => {
})
})
this was the code from the beginning
let checkbox = Array.from(document.querySelectorAll('.hidden-checkbox'));
checkbox.forEach((item) => {
item.addEventListener('click', checkboxStatusShift);
});
let trashBtn = Array.from(document.querySelectorAll('.trash-btn'));
trashBtn.forEach((item) => {
item.addEventListener('click', deleteTask);
});
this will be the function to "trash" a task:
function deleteTask(event: any) {
const index = (event.currentTarget);
const buttonId = index.id?.replace('remove-', '');
const currentTask = todoDatabase.filter((object) => object.id === buttonId)[0];
const currentTaskId = currentTask.id;
console.log(buttonId);
console.log(currentTaskId);
if (buttonId == currentTaskId) {
todoDatabase.splice(0, 1);
printTodoList();
}
}
I haven't started the code for the checkbox function yet.
Very grateful for any tips I can get.
Thanks for all the replies, this pointed me in the right direction and I got it working as intended, the following code was the result:
document.querySelectorAll('.add-click-listener').forEach(item => {
item.addEventListener('click', (event: any) => {
const thisWasClicked = event.currentTarget;
let trashBtn = Array.from(document.querySelectorAll('.trash-btn'));
const filterTrashBtn: any = trashBtn.find((btn) => btn.id === thisWasClicked.id);
const findTask = trashBtn.indexOf(filterTrashBtn);
if (filterTrashBtn?.id == thisWasClicked.id) {
todoDatabase.splice(findTask, 1);
printTodoList();
}
});
});
Now I can just write the code for my checkboxes with another if and some variables etc.
From my experience, you can avoid repeating code by using an event delegation pattern. You have to attach an event listener to the parent element, and then inside the handler to check if it matches your child element. The event by clicking on child will bubble so you will catch it. In code it will look like this:
document.querySelector('#parent-element').addEventListener('click', function(e) {
if (e.target && e.target.matches('.child-element')) {
// do your stuff here
}
});
You can bind to the root element where the checkboxes are located and handle the target element that fired the event.
If the HTML looks like this:
<div id="app">
<div class="checkboxes">
<input id="1" type="checkbox" /><label for="1">Checkbox 1</label>
<input id="2" type="checkbox" /><label for="2">Checkbox 2</label>
<input id="3" type="checkbox" /><label for="3">Checkbox 3</label>
</div>
</div>
An example JS code could be as shown below:
const root = document.getElementsByClassName('checkboxes')[0];
root.addEventListener('click', function (event) {
const target = event.target;
if (target.tagName == 'INPUT') {
console.log(target.id);
console.log(target.checked);
}
});
Bind a click event to the root div with the class name "checkboxes" and handle who exactly fires the event.
If you can use Jquery then the same can be done with on function
https://api.jquery.com/on
$(div.checkboxes).on("click", ".checkbox", function(event){})
I want to display a file selector and a submit button.
The file is only to be submitted once the button is clicked.
The submit/action target is on another server though.
I looked at react examples, but I can't figure out the exact method on how to
realise this with KotlinJS and React.
The duplicates in formMethod and onSubmitFunction are just me trying and seeing what sticks.
I also tried adding method = FormMethod.post, encType = FormEncType.multipartFormData directly to the form, but it didn't help. It doesn't even output the debug print.
EDIT: I do not need to do something with the file otherwise and want to leverage the default form functionality for the upload. The other server is also mine and has a simple html/http upload where this suffices:
<form method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="Upload">
</form>
ENDEDIT
EDIT2: I changed the code a little and now it refers to the other server, but does not append any files.
private fun RBuilder.render() {
form(
method = FormMethod.post,
encType = FormEncType.multipartFormData,
action = "https://otherserver.com/upload"
) {
styledInput {
attrs {
type = InputType.file
accept = ".zip"
multiple = false
}
}
styledInput {
attrs {
type = InputType.submit
value = "Test"
}
}
}
}
It seems like in this case the name parameter is strictly required.
The following works:
private fun RBuilder.render() {
form(
method = FormMethod.post,
encType = FormEncType.multipartFormData,
action = "https://otherserver.com/upload"
) {
styledInput {
attrs {
name = "upload"
type = InputType.file
accept = ".zip"
multiple = false
}
}
styledInput {
attrs {
type = InputType.submit
value = "Test"
}
}
}
}
In this HTML file,
<fieldset class="row">
<status-message status="housePlanEditCtrl.message"></status-message>
<legend class="fieldset-legend">HVAC Design Report</legend>
<file-manager
upload-only="true"
accept=".pdf, application/pdf"
label="HVAC Design Report"
input-id="HvacDesignReport"
files="housePlanEditCtrl.housePlan.HvacDesignReport">
</file-manager>
</fieldset>
I have a custom file manager directive that is called upon for input
const FILE_TYPE_ERROR = {
type : 'error',
text : 'File type not allowed.',
dismissable : false
};
function fileManager ($timeout) {
return {
scope : {
LocalFiles : '='
},
restrict : 'A',
link : (scope, element, attrs, fileManagerCtrl) => {
element.bind('change', function onChange (event) {
let parentScope = scope.$parent.$parent;
$timeout(()=>{
if (this.accept === 'application/pdf') {
var ext = this.value.match(/\.(.+)$/)[1];
switch (ext) {
case 'pdf':
break;
default:
alert('File type error.');
this.message = Object.assign({}, FILE_TYPE_ERROR);
this.value = '';
}
}
parentScope.fileManagerCtrl.files.push.apply(parentScope.fileManagerCtrl.files, _values(event.target.files));
parentScope.fileManagerCtrl.localSelectedCallback();
}, 0);
});
}
};
}
and a status message component for errors that occur within the controller.
<div class="status-message" data-ng-class="[statusMessageCtrl.getTypeClass(), {'dismissable' : statusMessageCtrl.status.dismissable}]" data-ng-if="statusMessageCtrl.isVisible">
<p class="status-text">{{statusMessageCtrl.status.text}}</p>
<button
data-ng-if="statusMessageCtrl.status.dismissable"
data-ng-click="statusMessageCtrl.onDismiss()"
type="button"
class="btn btn-dismiss btn-link btn-no-label"
aria-label="Dismiss Message">
<i class="fa fa-close" aria-hidden="true"></i>
</button>
</div>
I want to be able to display that message component whenever the wrong type file is uploaded through the file manager directive. How could I pass the message from the file manager to the status message directive whenever such an error occurs?
To communicate between two child components you need to setup #output from one component and feed it as input to other.
in your first child component , Create an output property which emits an event to parent component.
{
#Output() voted = new EventEmitter<boolean>();
didVote = false;
vote(agreed: boolean) {
this.voted.emit(agreed);
this.didVote = true;
}
}
Now , setup a method to handle that event in parent component.
here app-voter is your child component, which emits #output : (voted) which will be send to a method in parent compenent named : "onVoted($event)" .
{
<app-voter *ngFor="let voter of voters"
[name]="voter"
(voted)="onVoted($event)">
</app-voter>
}
Once you are in that method, you can assign that value to a local variable and then pass it to other child component as #input
{
<second-child-comp (input_name)="your_variable_Value"></second-child-comp>
}
for more go here, https://angular.io/guide/component-interaction
I have build a file upload component with drag-n-drop using react-dropzone.
I would like to inspect the files, that the user dragges onto the dropzone and do this while they are dragged (before they are dropped onto the dropzone).
I tried this:
<Dropzone
accept="application/pdf"
onDrop={this.handleDragDrop}
>
{({ draggedFiles, isDragActive, isDragReject, acceptedFiles, rejectedFiles }) => {
if (isDragActive) {
... inspect files here (before dropping)
}
}}
</Dropzone>
But non of draggedFiles, acceptedFiles and rejectedFiles has any value.
Am I missing something here... ?
EDIT:
To make my question more clear:
I want to do a validation of the files, before the user drops them to the dragzone. The validation by passing mimetypes to the accept property is not enougth in my scenario:
user is dragging files over the dragzone and onDragOver is fired
now the files are inspected and the dragzone is updated to show a message files are ok or files not ok, so the user gets this information BEFORE he drops the files
user is dropping the files and onDrop is triggered
Short answer
AFAIK You can't
Long answer
Let's see the following code:
<Dropzone
accept="image/*,application/pdf"
maxSize={5000000}
onDrop={this.onDrop}
onDragEnter={this.onDragEnter}
>
{({getRootProps, getInputProps, isDragAccept, isDragReject }) => {
let classes = 'form-control'
let placeholder = <p>Drag files here</p>;
if (isDragAccept) {
classes = `${classes} border-success bg-light`;
placeholder = <p className="text-success">Drop files now</p>;
}
if (isDragReject) {
classes = `${classes} border-danger bg-light`;
placeholder = <p className="text-danger">Some files are not allowed</p>
}
return (
<div {...getRootProps()} style={{ height: '100px' }} className={classes}>
<input {...getInputProps()} />
{placeholder}
</div>
);
}}
</Dropzone>
This snippet changes the style of the dropzone if the file to be dropped meets some requirements (any image type or pdf file and that is less than 5MB of size). If we drag any file over the dropzone the values of acceptedFiles and rejectedFiles are both an empty array. The value of draggedFiles is an array of DataTransferItems which give us the kind and type of the file being dragged, for example {kind: "file", type: "application/pdf"}.
At this point the values of isDragAccept and isDragReject are setted based on the type of the file. So, if we drag an image or a pdf file the value isDragAccept is set to true, the text Drop files now will be shown in the dropzone its border will be colored according the class border-success. But the same will occur if the file is greater than 5MB! Why? This is because we cannot read the file before the drop event.
Let see the event handlers:
onDrop = (acceptedFiles, rejectedFiles, event) => {
console.log(event.dataTransfer.items)
console.log(event.dataTransfer.files)
}
onDragEnter = (event) => {
console.log(event.dataTransfer.items)
console.log(event.dataTransfer.files)
}
The drag events give us a dataTransfer property which is a DataTransfer object. Here we have two properties that are important to us:
DataTransfer.items
This is a DataTransferItemList object which give us a list of DataTransferItems. It's the same object in the draggedFiles array and we can access this in both handlers: onDragEnter and onDrop.
DataTransfer.files
This holds a FileList object of the drag operation, but it's empty in the Drag events so we can access it only after the drop. For example, in the previous code if we drag and drop some file we get this output for the console log for the events showed:
FileList {length: 0} // dragEnter event
FileList {0: File(30299626), length: 1} // drop event
If we access the element 0 of the File list after the drop we get:
File(30299626) {name: "python_101.pdf", lastModified: 1549327543709, lastModifiedDate: Mon Feb 04 2019 21:45:43 GMT-0300 (Chile Summer Time), webkitRelativePath: "", size: 30299626, …}
Thus, we can only access data of the file After the drop event.
do this (React):
onDragOverHandle(event) {
event.stopPropagation();
event.preventDefault();
console.log(event.dataTransfer.items[0])
}
you can get drag target type, like :
{kind: "file", type: "image/png"} or {kind: "file", type: "video/mp4"}
then you can do something you want to do.
hope to help you ~
as #Jay lu sayied, you can do using something like this:
const onDragEnter = (event) => {
const fileType = event.dataTransfer.items[0].type;
const validTypes = ['image/png', 'image/jpeg', 'image/jpg'];
const valitate = validTypes.some(types => types === fileType);
!valitate && setDanger(true);
}
const onDragLeave = (event) => {
setDanger(false);
}
<Dropzone
onDrop={ onDrop }
accept='image/jpeg, image/png, image/jpg'
maxSize={500000}
onDragEnter={onDragEnter}
onDragLeave={onDragLeave}
>
You need to have init your state with : this.state = { files: [] } and handle drop files in function : onDrop(files) { // do stuff with files... }
This is an example extract from the doc https://react-dropzone.js.org/:
class Basic extends React.Component {
constructor() {
super()
this.state = { files: [] }
}
onDrop(files) {
this.setState({
files
});
}
render() {
return (
<section>
<div className="dropzone">
<Dropzone onDrop={this.onDrop.bind(this)}>
<p>Try dropping some files here, or click to select files to upload.</p>
</Dropzone>
</div>
<aside>
<h2>Dropped files</h2>
<ul>
{
this.state.files.map(f => <li key={f.name}>{f.name} - {f.size} bytes</li>)
}
</ul>
</aside>
</section>
);
}}
I have input file (without submit - typicall input file). I'd like to call some function when chose file.
Example:
I click on "Choose file" -> choose file -> now system detects change and call some function which prints all these file information (for example image name).
I can't use ngModel on input file, right? How to do that?
Use the following in your template:
<div class="modal-body">
<input type="file" (change)="fileChangeEvent($event)" placeholder="Upload file..." />
<img id="preview" src="" alt="Preview">
</div>
Then your Component fileChangeEvent() as
public fileChangeEvent(fileInput: any){
if (fileInput.target.files && fileInput.target.files[0]) {
var reader = new FileReader();
reader.onload = function (e : any) {
$('#preview').attr('src', e.target.result);
}
reader.readAsDataURL(fileInput.target.files[0]);
}
}
All Your File related info will console....
Here's my amendments to Double H's answer to not rely on jQuery and stop the webpack erroring out on e.target.result.
<img [src]="imageSrc" alt="" />
<input type="file" capture="camera" accept="image/*" (change)="displayPhoto($event)">
Typescript Code
displayPhoto(fileInput) {
if (fileInput.target.files && fileInput.target.files[0]) {
const reader = new FileReader();
reader.onload = ((e) => {
this.imageSrc = e.target['result'];
});
reader.readAsDataURL(fileInput.target.files[0]);
}
}
Double H's function didn't work for me till I added onclick="this.value = null" as found here: https://stackoverflow.com/a/42357862/634650