In my application I have a global search field which is used to filter the data in the list , list will have multiple column. From other component setting the filter value (setting to input value) it's happening but I have to trigger the manual keyboard event (enter key) action on the input.
I tried with viewChild decorator.
component.html
<input #gb type="text" placeholder="Global search.." class="changeListComponent_inputSearch" [(ngModel)]="jiraRef" />
component.ts
#ViewChild('gb') gb:ElementRef;
this.jiraRef = jiraRef;
const event = new KeyboardEvent("keypress",{ "which ": "13"});
this.gb.nativeElement.focus();
this.gb.nativeElement.dispatchEvent(event);
using this I could set the value and make a focus but keyboard event is not triggering.
Triggered the key up event using the native js code.
Placed the id attribute to the input element along with element reference.
Created the new function from inside my component.
triggerEvent(el, type) {
if ('createEvent' in document) {
var e = document.createEvent('HTMLEvents');
e.initEvent(type, false, true);
el.dispatchEvent(e);
} else {
var e = document.createEventObject();
e.eventType = type;
el.fireEvent('on'+e.eventType, e);
}
}
var el=document.getElementById('gb-search');
this.triggerEvent(el,'keyup');
I'm not sure why you are making it complicated. why not just add a keypress event to the input field, and then also be able trigger that event through a viewChild?
<input id="text1" type=text (keyup)="enterPressed()">
and then also be able to access this same method through a view child:
this.viewChild.enterPressed();
In general your inputs can be like:
<input (keydown.enter)="doEnter()">
From other component simply call to the function doEnter. How?
If the search component is in the parent it's easy because you can use ViewChild
<search-component></search-component>
#ViewChild(SearchComponent) searchComponent
..whe we need..
this.searchComponent.doEnter()
Else, typical use a Subject to subscribe. You has a service,
#Injectable({
providedIn: 'root',
})
export class KeyBoardService {
keyboard:Subject<any>=new Subject<any>()
constructor() { }
}
In your component Search, inject the service in constructor and in ngOnInit
constructor(private keyboardService:KeyBoardService){}
ngOnInit(){
this.keyboardService.keyboard.subscribe(_=>{
this.doEnter()
})
}
And in the other component
constructor(private keyboardService:KeyBoardService){}
..in anywhere..
this.keyboardService.next(null);
Exist another option that is when the two components are at time in the screen -and in the same <router-outlet></router-outlet> that is use a template reference variable and a Input. So we has
<search-component #search></search-component>
<other-component [search]=search><other-component>
in other component
#Input('search') searchComponent
..in anywhere
this.searchComponent.doEnter
You can simply use
<input type=text (keypress)="eventHandler($event)">
eventHandler(event) {
console.log(event, event.keyCode, event.keyIdentifier);
}
Solution is based on this answer
Get which key pressed from (keypress) angular2
Related
I have the following StimulusJS controller that aims to track certain required elements (flatckpickr date selectors).
Once all those date pickers' dates have been selected, I have to enable the submit button in a form.
I'm having problems tracking that all the required inputs have been inputed by the user and since this should be a reutilizable controller, I can't hardcode a value and compare against it.
What would be an effective way to check that all requiredTargets have received input from the user?
import { Controller } from 'stimulus';
export default class extends Controller {
static targets = ["required", "deactivable"];
toggle(){
this.deactivableTargets.toggleAttribute("disabled");
}
connect() {
this.requiredTargets.forEach((element) => {
element.addEventListener('input', (event) => {
console.log(`${element} changed`);
//if (this.requiredTargets.inputed?) {
toggle();
}
})
})
}
}
Technically inputs fields will have a value of an empty string ("") by default
You can use the JavaScript Array.every method to check that all required fields have been filled and are not equal to an empty string.
This will return true or false, and depending on what the case is, you can call the toggle() method
My Approach
On each flatpickr HTML input element, connect a Stimulus action method that fires on input event.
Let's call it handleInput in this case, so our HTML should look like this
<input
data-<controller-name>-target="required"
data-action="<controller-name>#handleInput"
type="text"
placeholder="Select Date.."
data-input
>
<!-- replace <controller-name> with the actual stimulus controller name -->
Next, define the handleInput method in your controller like this
handleInput() {
const isRequiredFilled = this.requiredTargets.every(el => el.value !== '');
if (isRequiredFilled) {
this.toggle();
}
}
NB: input element has a default input event so you don't have to handle that yourself. See the documentation
Have a look at the following example. I have enhanced the official example here with some Mousetrap functionality. So whenever somebody presses alt+1, the first input field will focus, whenever somebody presses alt+2 the second input field will be focused. It works.
Problem:
However, the input field then also takes the value of whatever was pressed as the hotkey (alt+1 then renders to ¡, alt+2 renders to € in the input). But I just want this to be a hotkey, I don't want it's actual value in the input field. How do I do this?
I could clear / delete the input field completely. This would work in the example here, but I don't want to do it since in my final app the state of the input field will need to be preserved, so I cannot just delete it.
Any advice?
import React from "react"
import Mousetrap from "mousetrap"
export default class CustomTextInput extends React.Component {
constructor(props) {
super(props)
// create a ref to store the textInput DOM element
this.textInput = React.createRef()
this.textInput2 = React.createRef()
this.focusTextInput = this.focusTextInput.bind(this)
}
componentDidMount() {
Mousetrap.bind("alt+1", () => {
this.focusTextInput(1)
})
Mousetrap.bind("alt+2", () => {
this.focusTextInput(2)
})
}
focusTextInput(id) {
// Explicitly focus the text input using the raw DOM API
// Note: we're accessing "current" to get the DOM node
if (id === 1) {
this.textInput.current.focus()
}
if (id === 2) {
this.textInput2.current.focus()
}
}
render() {
// tell React that we want to associate the <input> ref
// with the `textInput` that we created in the constructor
return (
<div>
<input type="text" ref={this.textInput} className="mousetrap" />
<input type="text" ref={this.textInput2} className="mousetrap" />
</div>
)
}
}
I
have you tried event.preventDefault() ?
Mousetrap.bind("alt+1", (e) => {
e.preventDefault();
this.focusTextInput(1);
})
Mousetrap.bind("alt+2", () => {
e.preventDefault();
this.focusTextInput(2)
})
#Dhananjai Pai solution didn't worked for me in the same case.
I know this is very old question, so I will just leave the solution, that worked for me, just if someone needs it.
<input
type="text"
placeholder="Put focus here"
name="something"
id="order-code"
class="form-control barcodeScanner"
>
if (typeof Mousetrap !== 'undefined') {
Mousetrap.bind(['`', '('], function (e) {
e.preventDefault();
$('.barcodeScanner').focus()
}, 'keyup');
}
Adding as third option the 'keyup' event solved the issue with typing inside the input.
Source:
https://craig.is/killing/mice#api.bind
There is a third argument you can use to specify the type of event to listen for. It can be keypress, keydown or keyup.
It is recommended that you leave this argument out if you are unsure. Mousetrap will look at the keys you are binding and determine whether it should default to keypress or keydown.
A button is only doing the function once, the video shows the problem:
https://vimeo.com/342491616
The logic goes like this:
When 'home' (inside footer.component) is clicked, sends function to parent(app.component), and then this one sends it to 'hamburguer' button (inside header.component).
I've read it may be something related to ngOnChanges but I tried it several times with it and it didn't work, but maybe I was using it wrong(?)
Code here:
footer.component.html
<a (click)="sendMessage()" routerLink="/page"><img src="assets/images/homebutton.png" class="footer_buttons" id="home_button"></a>
footer.component.ts (inside export class...)
message: string = "hamburguer";
#Output() messageEvent = new EventEmitter<string>();
sendMessage() {
this.messageEvent.emit(this.message);
//console.log(this.message);
}
/////////////////////////////////////////////////////////////
app.component.ts (inside export class...)
message = "";
receiveMessage($event){
this.message = $event;
//console.log($event);
}
/////////////////////////////////////////////////////////////
header.component.html
<div class="hamburguer">
<a (click)="getUrl();">
<img src="assets/images/{{ message }}.png" id="hamburguer">
</a>
</div>
header.component.ts (inside export class...)
#Input() message;
ngOnInit() {
if(window.location.pathname != "/settings"){
this.message = 'hamburguer';
}else{
this.message = 'cross';
}
}
This is happening because you fire the event only once.You need to remove the ngOninit Function and use your own function instead. Take a look at the following StackBlitz example
By the way,You rather work with variables instead working with the URL.URL is dynamic and could change, Pass a variable into your function and populate this.message according to that.
ngOnInit is a function that active on the first time, and invoked once.
you should add another "if" statement in the footer button, Home Button, in order to change the "message" variable.
I have been trying to get how to update an input based on keypress in window with ReactJS. I'm building a basic calculator, and it has only one input. I want the input to always be targeted once a key is pressed and also update the values. I also have another function for validation, so the keypress function will still pass through the validation function. Thank you!
I have something like this:
window.onkeypress = function(e){
this.inputFocus(); //my custom function to position the cursor in the input
this.setState({display: this.state.display + e.key});
}
I just don't know the proper location to fit it in cos it says syntax error: unexpected token, pointing at the "." between "window" and "onkeypress"
If you have focus on the input field, by pressing a button, it should enter the value into that input field, and so all you should need to do is make your input a controlled component by doing the following
export default class YourClass extends Component {
constructor(props) {
super(props);
this.state = {
numberValue: 0
}
}
updateValue = (numberValue) => {
this.setState({numberValue})
}
render() {
return (
<input
type='number'
value={this.state.numberValue} // this is initialized in this.state inside the constructor
onChange={(input) => this.updateValue(input.target.value)}
/>
)
}
}
So whenever you change the value, it will automatically update the state, which then sets the value of your input. This is all predicated on you having focus on that input though, so make sure it has focus.
You can assign focus by referencing the element through javascript like...
document.getElementById('yourElement').focus()
OK here it is... I called the window.onkeydown function outside the class as it doesn't update any state. And I later realised that window.onkeydown targets and add the values in the input
import ... from ...;
window.onkeydown = function(e) {
input.focus()
}
class App extends ... {
...
}
export default App;
A Component's EventEmitter does fire in some cases but does not in other cases. Why?
I have a custom Date picker. You can change the date manually (<input>) or use ng2-bootstrap <datepicker> to select conveniently.
I have this template:
<input [(ngModel)]="dateString"
class="form-control"
(focus)="showPopup()"
(change)="inputChange()">
<datepicker class="popup" [ngClass]="{ 'popup-hidden': !showDatepicker }"
[(ngModel)]="dateModel"
[showWeeks]="true"
(ngModelChange)="hidePopup($event)">
</datepicker>
The component with the relevant parts:
export class CustomDatepickerComponent {
#Input()
dateModel: Date;
dateString: string;
showDatepicker = false;
#Output()
dateModelChange: EventEmitter<Date> = new EventEmitter();
showPopup() {
this.showDatepicker = true;
}
// Called when the date is changed manually
// It DOES fire the event
inputChange() {
let result = moment(this.dateString, 'YYYY-MM-DD');
this.update(result.toDate());
}
// Called through the DatePicker of ng-bootstrap
// It DOESN'T fire the event
// However, the showDatepicker binding takes effect (see template)
hidePopup(event: Date) {
showDatepicker = false;
this.update(event);
}
update(date: Date) {
this.dateModel = date;
this.dateString = moment(date).format('YYYY-MM-DD');
// This SHOULD fire the event
// It is called in BOTH cases!
// But it fires only when the date is changed through the <input>
this.dateModelChange.emit(this.dateModel);
}
I use this datepicker this way:
<custom-datepicker [dateModel]="testDate" (change)="change($event)"></custom-datepicker>
Here's the change() handler function.
testDate = new Date();
change($event) { console.info('CHANGE', $event); }
Any ideas?
Ok... Now this is somewhat embarassing.
This was the usage of this component:
custom-datepicker [dateModel]="testDate" (change)="change($event)"></custom-datepicker>
Which should be changed to:
custom-datepicker [dateModel]="testDate" (dateModelChange)="change($event)"></custom-datepicker>
The interesting part is that it seems that the changed event of the <input> element bubbled up from CustomDatepickerComponent. It was handled inside the component, it fired dateModelChange which was not handled, then it bubbled up to the outer component as change event -- and this way it was handled.
If I passed the event to inputChange() and called event.stopPropagation() then it was cancelled and did not propagate.
Once again: it was nothing to do with EventEmitter.
I had a similiar issue. I solved it by decoupling the #ainput date model (datemodel) from the date model used inside the datepicker ( localdate: Date;)
Then I used ngOnChange to keep these both variables synced. This way, the EventEmitter is working fine and sending a value for each date selection or update.
export class myDatepicker implements OnChanges{
localdate: Date;
#Input() datemodel: Date;
#Input() isOpen: boolean = false;
#Input() label: string;
#Input() disabled: boolean = false;
#Output() datemodelChange: EventEmitter<Date> = new EventEmitter<Date>();
// Date has been selected in Datepicker, emit event
public selectedDate(value: any) {
this.datemodelChange.emit(value);
}
// React on changes in the connected input property
ngOnChanges(changes: { [propName: string]: SimpleChange }) {
try {
if (changes["datemodel"] && changes["datemodel"].currentValue) {
this.localdate = moment(changes["datemodel"].currentValue).toDate(); // raw Input value
}
} catch (ex) {
console.log('myDatePicker: ' + ex);
}
}
}
Another solution for anyone stumbling across this page.
I had a similar issue which was also not the fault of EventEmitter. :-)
Initially it appeared my EventEmitter was intermittently not firing, however the culprit turned out to be the click handler responsible for firing the EventEmitter.
I had this in my html template:
<button mat-mini-fab color="primary">
<i class="fas fa-pencil-alt" (click)="onClickEdit()"></i>
</button>
and the click handler:
export class MyClass {
#Output() edit = new EventEmitter();
constructor() { }
onClickEdit() {
console.log('onClickEdit(), this.edit=' + this.edit);
this.edit.emit();
}
}
I added the console.log() to debug the problem, and I noticed onClickEdit() was not fired every time I clicked the fancy edit icon, which explains why the EventEmitter wasn't firing. :-)
I fixed this by moving the (click) attribute to the enclosing button (in hindsight it seems kind of obvious.)
<button mat-mini-fab color="primary" (click)="onClickEdit()">
<i class="fas fa-pencil-alt"></i>
</button>
Problem solved.