Fill value of input with the time selected on Clockpicker in Angular 14 - angular14

I'm using clockpicker to make the user select the time he wants, however, I'm trying since a couple of days to figure out how to retrieve the value of time chosen and assign it to the corresponding input value but in vain.
So here is the html code. Down there I'm using also ngModel to send the value to .ts file.
<div>
<mat-form-field appearance="outline">
<mat-label>Select the start time</mat-label>
<input
appClockPicker
#clockpicker="clockpicker"
matInput
placeholder="..."
type="text"
[(ngModel)]="selectedHours[0]"
/>
</mat-form-field>
</div>
Then this is the directive to display the clockpicker, but I'm not really understanding the way it workes since I'm beginner, and I was copying it from somewhere in internet. So I think the error is from there.
import {
AfterViewInit,
Directive,
ElementRef,
NgZone,
} from "#angular/core";
declare var $: any;
declare var jQuery: any;
#Directive({
selector: "[appClockPicker]",
exportAs: "clockpicker",
})
export class ClockPickerDirective implements AfterViewInit {
myDate: any;
constructor(private el: ElementRef, private ngZone: NgZone) {}
ngAfterViewInit(): void {
this.ngZone.runOutsideAngular(() => {
$(this.el.nativeElement).clockpicker({
placement: '',
align: "left",
donetext: "Valider",
onselect: (data:any) => {
console.log(`data: ${data}`);
this.ngZone.run((date) => {
this.setTime(date);
console.log(`date: ${date}`);
});
},
})});
;}
setTime(date) {
this.myDate = date;
}
}
Finally this how it looks : the selected time appear correctly in the input box though
enter image description here

Related

How to select text inside input when the input get mounted (Vue3 )

I have this form that I created, basically what its doing is creating a new input with a random value each time I click on ADD_INPUT button. the thing I want is each time I create a new INPUT with random value the value get selected.
Sorry for my very bad English.
I've tried creating a new customized directive like this :
directives: {
focusInput: {
// directive definition
mounted(el) {
el.select()
}
}
},
but it breaks idy
Hoy can use select()
new Vue({
el : '#app',
data : {
text: 'some text'
},
methods : {
select() {
this.$refs.input.select();
},
},
mounted() {
this.select()
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="select">ADD_INPUT</button>
<input type="text" v-model="text" ref="input">
</div>
Composition API
setup() {
const input = ref(null)
function select() {
input.value.select()
}
return {
input,
select
}
}

Passing data from child component to parent and then to another child not working on page load but works after minor potentially unrelated change

I am new to Vuejs and come across this bug which I have no idea what I have done wrong. I am not receiving any console errors. It doesn't work on initial page load but it seems to work after I comment something out (or make a minor change). It will still then continue to work if I reverse the changes I just made and put it back to the original code. But once again on a fresh page load it won't work.
The issue: I am making a to do list and on page load when I add new tasks through the input field, the list does not appear on the page like it should be. I also console log the data array for this and it shows it is getting added to the array but is not getting rendered to the page. No console errors. In my code I will comment out some other data property (there are 2 additional ones below todosList in the TodoList.vue file that are currently not being used yet) and save and then the tasks will automatically appear on the page. So I think oh ok that might be the issue so with this new minor change I decide to refresh the page to see if it works as expected. Nope it doesn't so I then uncomment out what I previously commented out and save and the list appears again. But once again if I refresh the page it doesn't work. It only seems to be if I make a change inside the data function in the TodoList.vue file.
Additional info: The data is stored in the parent todos[] (App.vue), updated/pushed to array in a child (TodoCreate.vue) and sent back to the parent using $emit. This data is then sent through to another child (TodoList.vue) using props so that it can be rendered on the page.
Wondering if there is something that is not quite right in my code which is causing this to bug out like that. I will include everything in case it is something that looks unrelated to me but could be causing it.
Here is also a link to a code sandbox where the issue can be replicated by following the instructions on the page https://codesandbox.io/s/adding-new-todo-not-working-properly-jwwex?file=/src/components/TodoList.vue
main.js
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
App.vue
<template>
<div :class="currentMode">
<the-header #modeToggled="updateMode($event)"></the-header>
<main>
<todo-create #addedTodos="updateTodos"></todo-create>
<todo-list :todos="todos"></todo-list>
</main>
</div>
</template>
<script>
import TheHeader from './components/TheHeader.vue';
import TodoCreate from './components/TodoCreate.vue';
import TodoList from './components/TodoList.vue';
export default {
name: 'App',
components: {
TheHeader,
TodoCreate,
TodoList,
},
data() {
return {
currentMode: {
dark_mode: true,
light_mode: false
},
todos: [],
}
},
methods: {
updateMode(mode) {
this.currentMode = mode;
},
updateTodos(data) {
this.todos = data;
console.log(this.todos);
},
toggleCompleted() {
}
},
// provide() {
// return {
// todos: this.todos,
// };
// }
}
</script>
TheHeader.vue
<template>
<h1>To-do App</h1>
<div>
<label for="toggle-mode" aria-label="Toggle light and dark mode"></label>
<input type="checkbox" id="toggle-mode" #change="toggleMode">
</div>
</template>
<script>
export default {
emits: ['modeToggled'],
data() {
return {
toggleState: false,
}
},
methods: {
toggleMode() {
this.toggleState = !this.toggleState;
this.$emit('modeToggled', this.modeClasses);
}
},
computed: {
modeClasses() {
return {
dark_mode: !this.toggleState,
light_mode: this.toggleState
}
}
}
}
</script>
TodoCreate.vue
<template>
<div>
<label for="newtodo" class="sr-only">Create new to do</label>
<input type="text" id="newtodo" placeholder="Create a new todo..." v-model="todoval" v-on:keyup.enter="addTodo" >
</div>
</template>
<script>
export default {
emits: ['addedTodos'],
data() {
return {
todoval: '',
taskNumber: 0,
todos: [],
};
},
methods: {
addTodo() {
const val = this.todoval;
const taskNumber = this.taskNumber;
this.todos.push({ taskID: taskNumber, value: val, complete : 'not-completed'});
this.todoval = '';
this.taskNumber++;
console.log(this.todos);
this.$emit('addedTodos', this.todos);
},
}
}
</script>
TodoList.vue
<template>
<ul class="todo-items" :class="filterClass">
<li class="drop-zone" v-for="(listItem, index) in todosList" :class="listItem.complete" :key="listItem.taskID"
#drop='onDrop($event, index)'
#dragover.prevent
#dragenter.prevent>
<div class="drag-el" draggable="true"
#dragstart='startDrag($event, index)'>
<label :for="'checkbox-'+index" :aria-label="'Mark task ' + listItem.value + ' as completed'"></label>
<input type="checkbox" :id="'checkbox-'+index" #change="toggleCompleted(index, listItem.value, listItem.complete, listItem.taskID)">
<input type="text" disabled :value="listItem.value">
<img src="../assets/icon-cross.svg" #click="removeTask(index)">
</div>
</li>
</ul>
</template>
<script>
export default {
props: {
todos: Object,
filterClass: String
},
// inject: ['todos'],
data() {
return {
todosList: this.todos,
// completedTodos: [],
// activeTodos: [],
};
},
// watch: {
// todosList(data) {
// data.filter(function(todo) {
// if(todo.completed == 'completed') {
// completedTodos.push(todos);
// }
// });
// }
// },
methods: {
startDrag: (evt, item) => {
evt.dataTransfer.dropEffect = 'move'
evt.dataTransfer.effectAllowed = 'move'
evt.dataTransfer.setData('itemID', item)
},
onDrop (evt, list) {
const itemID = evt.dataTransfer.getData('itemID');
const movedData = this.todosList[itemID];
this.todosList.splice(itemID,1);
this.todosList.splice(list,0, movedData);
},
toggleCompleted() {
// still need to write this method
},
removeTask() {
// still need to write this method
}
}
}
</script>

Adding ngbDatepicker in agGrid

I am trying to add ngbdatepicker in agGrid, but while adding the calender is coming inside the cell. I tried adding isPopUp as true but that is making the complete input oitside.
Here is the code I wrote:
{
headerName: 'Start Date',
field: 'paiStartDate',
width: 150,
editable: (params) => { return this.isEditiable(params); },
cellEditor: 'agDateInput',
},
this.components = { agDateInput: DatePickerComponent };
Here is my componenet html:
<div class="row">
<div class="col-md-12 col-lg-12">
<input data-input type="text" class="form-control" autocomplete="off"
[(ngModel)]="model" ngbDatepicker #d="ngbDatepicker" (click)="d.toggle()" placement="bottom-right"
(dateSelect) = "onDateSelect($event)"
size="13">
</div>
Here is the ts code:
export class DatePickerComponent implements OnInit, AgEditorComponent {
params: ICellEditorParams;
public selectedDate: Date = new Date();
model: NgbDateStruct;
#ViewChild('d') datePicker : ElementRef;
constructor() { }
ngOnInit() { }
getValue() {
return `${this.selectedDate.getDate()}/${this.selectedDate.getMonth() + 1}/${this.selectedDate.getFullYear()}`;
}
isCancelBeforeStart?(): boolean {
return false;
}
isCancelAfterEnd?(): boolean {
return false;
}
agInit(params: any): void {
this.params = params;
this.selectedDate = params.value;
}
onDateSelect(date:Date){
debugger;
this.selectedDate = date;
alert(this.selectedDate);
this.params.api.stopEditing();
}
isPopup(): boolean {
return false;
}
}
Please help as the calender here is opening inside of the input.
The issue here is that the popup element in rendering inside the cell. You will need to attach the popup element to the document body, to have it render as expected.
There is a blog post which addresses this which uses a different date picker component, but the concept remains the same for any date picker component: https://blog.ag-grid.com/using-third-party-datepickers-in-ag-grid/#appending-body
For your case, the easy way is to add [container]="'body'" to your input component. You can find this in the docs for component: https://ng-bootstrap.github.io/#/components/datepicker/api#NgbDate
<input data-input type="text" class="form-control" autocomplete="off"
[(ngModel)]="model" [container]="'body'" ngbDatepicker #d="ngbDatepicker" (click)="d.toggle()" placement="bottom-right"
(dateSelect) = "onDateSelect($event)"
size="13">

VueJS: how to trigger 'change' on <input> changed programmatically

I'm going to build a customized virtual keyboard, so that's the first problem I've encountered.
I have an input element, whose value is changed from outside, in my case by pressing a button. The problem is that there seems to be no way to trigger the normal 'change' event.
Neither clicking outside the input, nor pressing Enter gives any result. What might be the correct way of solving this problem?
<template>
<div class="app-input">
<input #change="onChange" type="text" v-model="value" ref="input">
<button #click="onClick">A</button>
</div>
</template>
<script>
export default {
name: "AppInput",
data() {
return {
inputDiv: null,
value: ""
};
},
props: {
msg: String
},
methods: {
onClick() {
this.value = this.value + "A";
this.inputDiv.focus();
},
onChange() {
alert("changed");
}
},
mounted() {
this.$nextTick(() => {
this.inputDiv = this.$refs.input;
});
}
};
</script>
The whole pen can be found here.
v-on:change would only trigger on a direct change on the input element from a user action.
What you are looking for is a wathcer for your data property, whenever your value changes, watcher will execute your desired function or task.
watch: {
value: function() {
this.onChange();
}
}
The watch syntax is elaborated on the provided official vuejs docs link. use your data property as the key and provide a function as a value.
Check the snippet.
export default {
name: "AppInput",
data() {
return {
inputDiv: null,
value: ""
};
},
props: {
msg: String
},
methods: {
onClick() {
this.value = this.value + "A";
this.inputDiv.focus();
},
onChange() {
alert("changed");
}
},
// this one:
watch: {
value: function() {
this.onChange();
}
},
// --- rest of your code;
mounted() {
this.$nextTick(() => {
this.inputDiv = this.$refs.input;
});
}
};
When I build any new vue application, I like to use these events for a search input or for other inputs where I don't want to fire any functions on #change
<div class="class">
<input v-model="searchText" #keyup.esc="clearAll()" #keyup.enter="getData()" autofocus type="text" placeholder="Start Typing ..."/>
<button #click="getData()"><i class="fas fa-search fa-lg"></i></button>
</div>
These will provide a better user experience in my opinion.

File upload from <input type="file">

Using angular 2 beta, I cannot seem to get an <input type="file"> to work.
Using diagnostic, I can see two-way binding for other types such as text.
<form>
{{diagnostic}}
<div class="form-group">
<label for="fileupload">Upload</label>
<input type="file" class="form-control" [(ngModel)]="model.fileupload">
</div>
</form>
In my TypeScript file, I have the following diagnostic line:
get diagnostic() { return JSON.stringify(this.model); }
Could it be that it is the issue of not being JSON? The value is null.
I cannot really verify the value of the input. Уven though the text next to "Choose file ..." updates, I cannot see differences in the DOM for some reason.
I think that it's not supported. If you have a look at this DefaultValueAccessor directive (see https://github.com/angular/angular/blob/master/modules/angular2/src/common/forms/directives/default_value_accessor.ts#L23). You will see that the value used to update the bound element is $event.target.value.
This doesn't apply in the case of inputs with type file since the file object can be reached $event.srcElement.files instead.
For more details, you can have a look at this plunkr: https://plnkr.co/edit/ozZqbxIorjQW15BrDFrg?p=info:
#Component({
selector: 'my-app',
template: `
<div>
<input type="file" (change)="onChange($event)"/>
</div>
`,
providers: [ UploadService ]
})
export class AppComponent {
onChange(event) {
var files = event.srcElement.files;
console.log(files);
}
}
#Component({
selector: 'my-app',
template: `
<div>
<input name="file" type="file" (change)="onChange($event)"/>
</div>
`,
providers: [ UploadService ]
})
export class AppComponent {
file: File;
onChange(event: EventTarget) {
let eventObj: MSInputMethodContext = <MSInputMethodContext> event;
let target: HTMLInputElement = <HTMLInputElement> eventObj.target;
let files: FileList = target.files;
this.file = files[0];
console.log(this.file);
}
doAnythingWithFile() {
}
}
There is a slightly better way to access attached files. You could use template reference variable to get an instance of the input element.
Here is an example based on the first answer:
#Component({
selector: 'my-app',
template: `
<div>
<input type="file" #file (change)="onChange(file.files)"/>
</div>
`,
providers: [ UploadService ]
})
export class AppComponent {
onChange(files) {
console.log(files);
}
}
Here is an example app to demonstrate this in action.
Template reference variables might be useful, e.g. you could access them via #ViewChild directly in the controller.
Another way using template reference variable and ViewChild, as proposed by Frelseren:
import { ViewChild } from '#angular/core';
#Component({
selector: 'my-app',
template: `
<div>
<input type="file" #fileInput/>
</div>
`
})
export class AppComponent {
#ViewChild("fileInput") fileInputVariable: any;
randomMethod() {
const files = this.fileInputVariable.nativeElement.files;
console.log(files);
}
}
Also see https://stackoverflow.com/a/40165524/4361955
Try this small lib, works with Angular 5.0.0
https://www.npmjs.com/package/ng2-file-upload
Quickstart example with ng2-file-upload 1.3.0:
User clicks custom button, which triggers upload dialog from hidden input type="file" , uploading started automatically after selecting single file.
app.module.ts:
import {FileUploadModule} from "ng2-file-upload";
your.component.html:
...
<button mat-button onclick="document.getElementById('myFileInputField').click()" >
Select and upload file
</button>
<input type="file" id="myFileInputField" ng2FileSelect [uploader]="uploader" style="display:none">
...
your.component.ts:
import {FileUploader} from 'ng2-file-upload';
...
uploader: FileUploader;
...
constructor() {
this.uploader = new FileUploader({url: "/your-api/some-endpoint"});
this.uploader.onErrorItem = item => {
console.error("Failed to upload");
this.clearUploadField();
};
this.uploader.onCompleteItem = (item, response) => {
console.info("Successfully uploaded");
this.clearUploadField();
// (Optional) Parsing of response
let responseObject = JSON.parse(response) as MyCustomClass;
};
// Asks uploader to start upload file automatically after selecting file
this.uploader.onAfterAddingFile = fileItem => this.uploader.uploadAll();
}
private clearUploadField(): void {
(<HTMLInputElement>window.document.getElementById('myFileInputField'))
.value = "";
}
Alternative lib, works in Angular 4.2.4, but requires some workarounds to adopt to Angular 5.0.0
https://www.npmjs.com/package/angular2-http-file-upload
If you have a complex form with multiple files and other inputs here is a solution that plays nice with ngModel.
It consists of a file input component that wraps a simple file input and implements the ControlValueAccessor interface to make it consumable by ngModel. The component exposes the FileList object to ngModel.
This solution is based on this article.
The component is used like this:
<file-input name="file" inputId="file" [(ngModel)]="user.photo"></file-input>
<label for="file"> Select file </label>
Here's the component code:
import { Component, Input, forwardRef } from '#angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '#angular/forms';
const noop = () => {
};
export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => FileInputComponent),
multi: true
};
#Component({
selector: 'file-input',
templateUrl: './file-input.component.html',
providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class FileInputComponent {
#Input()
public name:string;
#Input()
public inputId:string;
private innerValue:any;
constructor() { }
get value(): FileList {
return this.innerValue;
};
private onTouchedCallback: () => void = noop;
private onChangeCallback: (_: FileList) => void = noop;
set value(v: FileList) {
if (v !== this.innerValue) {
this.innerValue = v;
this.onChangeCallback(v);
}
}
onBlur() {
this.onTouchedCallback();
}
writeValue(value: FileList) {
if (value !== this.innerValue) {
this.innerValue = value;
}
}
registerOnChange(fn: any) {
this.onChangeCallback = fn;
}
registerOnTouched(fn: any) {
this.onTouchedCallback = fn;
}
changeFile(event) {
this.value = event.target.files;
}
}
And here's the component template:
<input type="file" name="{{ name }}" id="{{ inputId }}" multiple="multiple" (change)="changeFile($event)"/>
just try (onclick)="this.value = null"
in your html page add onclick method to remove previous value so user can select same file again.