Textarea to ignore enter but need to trigger Save button - javascript

A bit tricky situation. For the code below, I have added (keydown.enter)="false" to ignore the break line/enter button in textarea
This is causing a user issue and would like the existing behaviour where Pressing enter should automatically trigger the "Save button"
Any idea how to trigger the Save button when still focusing in textArea but ignore the breakline?
<textarea #textArea
style="overflow:hidden; height:auto; resize:none;"
rows="1"
class="form-control"
[attr.placeholder]="placeholder"
[attr.maxlength]="maxlength"
[attr.autofocus]="autofocus"
[name]="name"
[attr.readonly]="readonly ? true : null"
[attr.required]="required ? true : null"
(input)="onUpdated($event)"
[tabindex]="skipTab ? -1 : ''"
(keydown.enter)="false"
[(ngModel)]="value">
</textarea >

Extending the answer by #Pengyy
You can bind the bind the enter key to a pseudoSave function, and preventDefault inside of that, thus preventing both the Save function and the newline. Then you can either call the save function from there(assuming it is accessible such as a service) or you can emit an EventEmitter, and have that emit get caught to trigger the Save function.

you can bind the same function of Save button to keydown.enter of texterea, and call $event.preventDefault to avoid the newline.
sample plunker.

Assuming that your textarea is inside a form element.
{Plunker Demo}
You can achieve it by using a hidden submit input, like this
#Component({
selector: 'my-app',
template: `
<form (submit)="formSubmitted($event)">
<input #proxySubmitBtn type="submit" [hidden]="true"/>
<textarea #textArea (keydown.enter)="$event.preventDefault(); proxySubmitBtn.click()">
</textarea>
</form>
`,
})
export class App {
formSubmitted(e) {
e.preventDefault();
alert('Form is submitted!');
}
}

You can create a service which can send a notification to other components that will handle the command. The service could look like this:
import { Injectable } from "#angular/core";
import { Subject } from "rxjs/Subject";
#Injectable()
export class DataSavingService {
private dataSavingRequested = new Subject<void>();
public dataSavingRequested$ = this.dataSavingRequested.asObservable();
public requestDataSaving(): void {
this.dataSavingRequested.next();
}
}
... and should be registered in the providers section of the module. Note: if data must be passed in the notification, you can declare a non-void parameter type for the dataSavingRequested Subject (e.g. string).
The service would be injected in the component with the textarea element and called in the handler of the Enter keypress event:
import { DataSavingService } from "./services/data-saving.service";
...
#Component({
template: `
<textarea (keypress.enter)="handleEnterKeyPress($event)" ...></textarea>
`
})
export class ComponentWithTextarea {
constructor(private dataSavingService: DataSavingService, ...) {
...
}
public handleEnterKeyPress(event: KeyboardEvent): void {
event.preventDefault(); // Prevent the insertion of a new line
this.dataSavingService.requestDataSaving();
}
...
}
The component with the Save button would subscribe to the dataSavingRequested$ notification of the service and save the data when notified:
import { Component, OnDestroy, ... } from "#angular/core";
import { Subscription } from "rxjs/Subscription";
import { DataSavingService } from "../services/data-saving.service";
...
#Component({
...
})
export class ComponentWithSaveButton implements OnDestroy {
private subscription: Subscription;
constructor(private dataSavingService: DataSavingService, ...) {
this.subscription = this.dataSavingService.dataSavingRequested$.subscribe(() => {
this.saveData();
});
}
public ngOnDestroy(): void {
this.subscription.unsubscribe();
}
private saveData(): void {
// Perform data saving here
// Note: this method should also be called by the Save button
...
}
}
The code above assumes that the saving must be performed in the component with the Save button. An alternative would be to move that logic into the service, which would expose a saveData method that could be called by the components. The service would need to gather the data to save, however. It could be obtained with a Subject/Observable mechanism, or supplied directly by the components as a parameter to saveData or by calling another method of the service.

it could be 2 solutions:
Use javascript to handle enter event and trigger Save function in it
or
Use Same thing from Angular side as describe in this.
This may also help you

Related

Pass selected value to other component html

I am not much familiar with Angular . I have a code where I am inputting some values using dropdown select
html 1:-
<angular-material-filter-dropdown
[dropdownList]="['Solid', 'Liquid', 'Gas']"
(selected)="triggerStateSelect($event)">
</angular-material-filter-dropdown>
Component 1:-
triggerStateSelect(event) {
console.log('state select ', event);
}
Here event stores the value.
HTML 2:-
<div class="title-container">
<p class="title">
{{}} -Dashboard
</p>
</div>
Now I want to pass selected value to a different component and also to display the selected in HTML
Whenever a value is selected , the second component must get the value and shows the value. Also when nothing is selected the second component must not show any previous selected value. It must get cleared.
How can I achieve this ?
You could use a service which provides you the Information with Observables. You create a service.ts file in which you declare your observable and observer. Everytime a value is selected you simply .next(value) in your service and then subscribe to it in the .ts file where you .html file is. Here is a good explanation on how to use Observables. I hope it helps
https://medium.com/#luukgruijs/understanding-creating-and-subscribing-to-observables-in-angular-426dbf0b04a3
If they have parent-child relationship you can interact via input
https://angular.io/guide/component-interaction#pass-data-from-parent-to-child-with-input-binding
If not, you can achieve it via service:
Create Service:
import { Observable, Subject } from 'rxjs';
#Injectable({ providedIn: 'root' })
export class SelectStateService {
private subject = new Subject<any>();
triggerStateSelect() {
this.subject.next();
}
onStateSelect(): Observable<any> {
return this.subject.asObservable();
}
}
Component 1:
constractor(private selectStateService : SelectStateService ){}
triggerStateSelect(event){
this.myService.triggerStateSelect(event);
}
Component 2:
constractor(private selectStateService: SelectStateService){}
state;
this.$subscription = this.selectStateService.onStateSelect().subscribe((event) => {
this.state = event;
}
);
ngOnDestroy() {
this.$subscription.unsubscribe();
}
Html 2:
<div class="title-container">
<p class="title">
{{state}} -Dashboard
</p>
</div>
documentation:
https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service

How to capture anctions window:beforeunload Angular 8

I have a problem with window event beforeunload in Angular app.
i wants to check if the user navigate to another page, if so i want to clean sessionStorage. I use.
onBeforeUnload(event: BeforeUnloadEvent) {
event.returnValue = '';
}
Beforeunload event also workd as a page refresh. How to check if the user leaves the application and clear the sesion storage after confirm dialog.
You can try below code in constructor of your component class.
Option 1
window.addEventListener("beforeunload", function (e) {
exampleService.logout();
});
Option 2
#HostListener('window:beforeunload', ['$event'])
beforeUnloadHandler(event: any) {
event.preventDefault();
// any other code / dialog logic
}
To distinguish from 2 cases, you should check out this stackoverflow question or related questions, here's one:
Question
Hope, this helps.
The following code will make browser to ask user that whether he wants to lose the unsaved changes or not.
Also, I'm checking whether the form is dirty or not.
If the form is not dirty, browser won't bother the user and close it.
import { Component, ViewChild, HostListener } from '#angular/core';
import { NgForm } from '#angular/forms';
import { DetailedUser } from 'src/app/models/detailed-user';
#Component({
selector: 'app-member-edit',
templateUrl: './member-edit.component.html',
styleUrls: ['./member-edit.component.css']
})
export class MemberEditComponent {
user: DetailedUser = new DetailedUser();
#ViewChild('userForm') userForm: NgForm;
#HostListener('window:beforeunload', ['$event'])
WindowBeforeUnoad($event: any) {
if (this.userForm.dirty) {
$event.returnValue = 'Your data will be lost!';
}
}
saveChanges(){
....
this.userForm.reset(this.user);
}
}
the following is the html section
<form (ngSubmit)="saveChanges()" #userForm="ngForm">
<div class="form-group">
<label for="introduction">Introduction</label>
<textarea
name="introduction"
class="form-control"
[(ngModel)]="user.introduction"
rows="6"></textarea>
</div>
<div class="form-group">
<label for="lookingFor">Looking For</label>
<textarea
name="lookingFor"
class="form-control"
[(ngModel)]="user.lookingFor"
rows="6"></textarea>
</div>
</form>

button component with BUILT-IN disable / re-enable (PREVENT DOUBLE SUBMIT!)

Is it just a good rule of thumb to handle all 'disable' validation logic on the form component itself? Does that sound right??
I'm really just trying to find a way to share the 'disable' logic across the application without repeating it in every form component, but i guess that is the proper way to do things?? Can someone verify this??
I would like to create a reusable submit button component.
This submit button component should act like any other submit button component, except for one thing...
The submit button needs to "disable itself" after being clicked.
That should be easy enough right. However, the rub here is that the button also needs to "re-enable itself" after the "call" is 100% completed. (In case there is an error, or the application needs to allow another action after the first is completed, etc).
I would like 100% of "that" logic to exist inside of the component so I can easily reuse it everywhere in the application.
I thought it would be kinda easy, but I think I'm still missing something...
I guess the idea would be to use an #Input() preferably (or maybe Output) in order to pass in some "async callback type of thing" to the button control itself...
That way the button could react to the "async callback type of thing" completing and use the callback handler to re-enable itself.
Thanks for the help in advance!
I wrote an abstract class which I use all the time for this exact case:
import { ElementRef, EventEmitter, Input, Output, ViewChild } from '#angular/core';
import { FormGroup, FormGroupDirective } from '#angular/forms';
export abstract class FormBase<T = any> {
#Input() isSubmitting: boolean;
#Output('onSubmit') _submit = new EventEmitter<T>();
#ViewChild(FormGroupDirective, { static: true })
ngForm: FormGroupDirective;
#ViewChild('submitButton', { static: true }) button: ElementRef;
form: FormGroup;
onSubmit(): void {
if (this.isFormValid()) this._submit.emit(this.getFormValue());
}
submit(): void {
if (!this.button || !this.button.nativeElement) return;
this.button.nativeElement.click();
}
reset(value?: any) {
this.ngForm.resetForm(value);
}
isFormValid(): boolean {
return this.form.valid;
}
getFormValue(): T {
return this.form.value;
}
shouldDisable(): boolean {
return (
this.isSubmitting ||
((this.form.invalid || this.form.pending) &&
(this.ngForm ? this.ngForm.submitted : true))
);
}
}
component.ts
import { FormBase } from 'path';
#Component({
...
})
export class FormComponent extends FormBase {
constructor(formBuilder: FormBuilder) {
super();
this.form = formBuilder.group({
username: ['', Validators.required],
password: ['', Validators.required],
});
}
}
component.html
<form (ngSubmit)="onSubmit()" [formGroup]="form">
<mat-form-field>
<mat-label>Username</mat-label>
<input matInput type="text" formControlName="username" />
</mat-form-field>
<mat-form-field>
<mat-label>Password</mat-label>
<input matInput type="text" formControlName="password" />
</mat-form-field>
<button [disabled]="shouldDisable()" mat-flat-button color="primary">
Submit
</button>
</form>
Your form components basically extend from this class which should work 99% of the time. If your component needs some very specific functionality like changing when the button becomes disabled or something else, you can simply override the methods in the FormComponent.

Javascript Angular 4 Change ngClass from another Component

I currently have this code in my app.component.ts
app.component.html
<div [ngClass]="myclass">
...rest of the content here
</div>
This I have the this:
<button (click)="changeClass('myFavClass')">Change Class to myFavClass</div>
app.component.ts
export class AppComponent {
myclass: string;
changeClass(myclass) {
this.myclass = myclass;
}
}
Now, all this works fine BUT I now want to put the triggering button on another component.
If I put this on another component:
<button (click)="changeClass('myFavClass')">Change Class to myFavClass</div>
How can I get it to change the class?
There are two ways you can do this you can use output with an EventEmit
Or you can set up a service that monitors the changes to a variable and use that as the control point for the change.
Personally, I use services for this instance as its easier to manage the code and its flow.
This answer has all the code in you need to look at.
Changing a value in two different components at the same time Angular 2
Hope that helps
There are at least two options. Subject and Observable or if this another component is a parent you can use #Input.
Subject and Observable method:
angular guide Highly recommended to read whole page.
Some component
export class SomeComponent {
constructor(private ClassService: ClassService) { }
private changeClass(class) {
this.ClassService.changeClass(class);
}
}
Another Component
export class AnotherComponent implements OnInit, OnDestroy {
constructor(private ClassService: ClassService) { }
private class: string = "";
private subscribtion: Subscribtion;
ngOnInit(): void {
this.Subscribtion = this.ClassService.someClass$.subscribe(
(class) => { this.class = class; }
)
}
ngOnDestroy(): void {
this.Subscribtion.unsubscribe();
}
}
Service
#Injectable();
export class ClassService{
constructor() { }
private someClassSource= new Subject<string>();
someClass$= this.someClassSource.asObservable();
changeClass(class) {
this.someClassSource.next(class);
}
}
taken from my answer
#Input method:
angular guide
This is very simple, when you click button changeClass method will change elClass which will be passed to another component by #Input decorator, every change of #Input will cause a detect changes which will detect that value has changed so class will change to myClass.
Parent component
parent.component.html
<another-component [elementClass]="elClass"></another-component>
<button (click)="changeClass('myClass')">change class<button>
parent.component.ts
export class ParentComponnet {
private elClass: string = "";
changeClass(class: string) {
elClass = class;
}
}
Another component (must be child component)
another.component.html
<div [ngClass]="elementClass">
another.component.ts
export class AnotherComponent {
#Input() elementClass: string;
}
There is also Child to Parent interaction via #Output (emitting event) angular guide

how can I listen to changes in code in angular 2?

I'm using angular 2. I have a component with an input.
I want to be able to write some code when the input value changes.
The binding is working, and if the data is changed (from outside the component) I can see that there is change in the dom.
#Component({
selector: 'test'
})
#View({
template: `
<div>data.somevalue={{data.somevalue}}</div>`
})
export class MyComponent {
_data: Data;
#Input()
set data(value: Data) {
this.data = value;
}
get data() {
return this._data;
}
constructor() {
}
dataChagedListener(param) {
// listen to changes of _data object and do something...
}
}
You could use the lifecycle hook ngOnChanges:
export class MyComponent {
_data: Data;
#Input()
set data(value: Data) {
this.data = value;
}
get data() {
return this._data;
}
constructor() {
}
ngOnChanges([propName: string]: SimpleChange) {
// listen to changes of _data object and do something...
}
}
This hook is triggered when:
if any bindings have changed
See these links for more details:
https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html
https://angular.io/docs/ts/latest/api/core/OnChanges-interface.html
As mentioned in the comments of Thierry Templier's answer, ngOnChanges lifecycle hook can only detect changes to primitives. I found that by using ngDoCheck instead, you are able to check the state of the object manually to determine if the object's members have changed:
A full Plunker can be found here. But here's the important part:
import { Component, Input } from '#angular/core';
#Component({
selector: 'listener',
template: `
<div style="background-color:#f2f2f2">
<h3>Listener</h3>
<p>{{primitive}}</p>
<p>{{objectOne.foo}}</p>
<p>{{objectTwo.foo.bar}}</p>
<ul>
<li *ngFor="let item of log">{{item}}</li>
</ul>
</div>
`
})
export class ListenerComponent {
#Input() protected primitive;
#Input() protected objectOne;
#Input() protected objectTwo;
protected currentPrimitive;
protected currentObjectOne;
protected currentObjectTwo;
protected log = ['Started'];
ngOnInit() {
this.getCurrentObjectState();
}
getCurrentObjectState() {
this.currentPrimitive = this.primitive;
this.currentObjectOne = _.clone(this.objectOne);
this.currentObjectTwoJSON = JSON.stringify(this.objectTwo);
}
ngOnChanges() {
this.log.push('OnChages Fired.')
}
ngDoCheck() {
this.log.push('DoCheck Fired.');
if (!_.isEqual(this.currentPrimitive, this.primitive)){
this.log.push('A change in Primitive\'s state has occurred:');
this.log.push('Primitive\'s new value:' + this.primitive);
}
if(!_.isEqual(this.currentObjectOne, this.objectOne)){
this.log.push('A change in objectOne\'s state has occurred:');
this.log.push('objectOne.foo\'s new value:' + this.objectOne.foo);
}
if(this.currentObjectTwoJSON != JSON.stringify(this.objectTwo)){
this.log.push('A change in objectTwo\'s state has occurred:');
this.log.push('objectTwo.foo.bar\'s new value:' + this.objectTwo.foo.bar);
}
if(!_.isEqual(this.currentPrimitive, this.primitive) || !_.isEqual(this.currentObjectOne, this.objectOne) || this.currentObjectTwoJSON != JSON.stringify(this.objectTwo)) {
this.getCurrentObjectState();
}
}
It should be noted that the Angular documentation provides this caution about using ngDoCheck:
While the ngDoCheck hook can detect when the hero's name has changed,
it has a frightful cost. This hook is called with enormous frequency —
after every change detection cycle no matter where the change
occurred. It's called over twenty times in this example before the
user can do anything.
Most of these initial checks are triggered by Angular's first
rendering of unrelated data elsewhere on the page. Mere mousing into
another input box triggers a call. Relatively few calls reveal actual
changes to pertinent data. Clearly our implementation must be very
lightweight or the user experience will suffer.

Categories

Resources