set focus in input element after submit and reset form - form-submit

I'm working with a template form component with angular2 and I cannot get to set the focus in my firstName input element after submit the form. The form is reseted fine but no way of setting focus.
This is my component code:
export class TemplateFormComponent {
#ViewChild('f') form: any;
onSubmit() {
if (this.form.valid) {
console.log("Form Submitted!");
this.form.reset();
this.form.controls.firstName.focus();
}
}
}
and my template code:
<form novalidate autocomplete="off" #f="ngForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<label>First Name</label>
<input type="text"
class="form-control"
name="firstName"
[(ngModel)]="model.firstName"
required
#firstName="ngModel">
</div>
</form>

On your view, set a reference to the input field you want to focus (you already have that with #firstName).
Then, on your component code, create an access to it with #ViewChild:
#ViewChild('firstName') firstNameRef: ElementRef;
And finally, right after reseting the form:
this.firstNameRef.nativeElement.focus()
ps.: I would expect the FormControl api to expose a focus method, but this issue on gitHub suggests it may never happen.

For a more generic solution see
Is it possible to get native element for formControl?
`
#Directive({
selector: '[ngModel]',
})
export class NativeElementInjectorDirective {
constructor(private el: ElementRef, private control : NgControl) {
(<any>control.control).nativeElement = el.nativeElement;
}
}
`
This will add the nativeElement to EACH form control once you add the directive to your module.
UPDATE: a much simpler solution for this usecase
To set focus on the first invalid element on a form create a directive like this:
import { Directive, HostListener, ElementRef} from '#angular/core';
#Directive({
selector: '[focusFirstInvalidField]'
})
export class FocusFirstInvalidFieldDirective {
constructor(private el: ElementRef) { }
#HostListener('submit')
onFormSubmit() {
const invalidElements = this.el.nativeElement.querySelectorAll('.ng-invalid');
if (invalidElements.length > 0) {
invalidElements[0].focus();
}
}
}
Then simply add it to your form element tag

Simple solution: add "autofocus" to your input element properties.
<input type="text" class="form-control" name="firstName" autofocus required>

Related

vuejs "TypeError: Cannot use 'in' operator to search for

I have a input box and user have to give their facebook user id on that so i make input box with default value https://www.facebook.com and then user give their user-profile-link then it will update data but i got this error when anything gonna type on input box
<div class="column">
<label class="label">Facebook Id::</label>
<input class="input is-medium" type="text" v-model="'https://www.facebook.com/'+data.fblink">
</div>
<script>
import contactInfo from './ContactInfo'
export default {
components: {contactInfo},
props: ['data'],
binding is for variables only, use this it works
<div class="column">
<label class="label">Facebook Id::</label>
<input class="input is-medium" type="text" v-model="facebook_link">
</div>
in your data variable add facebook_link as string. then if you want to update use this
'https://www.facebook.com/'+facebook_variable
v-model needs a variable to bind to, not a fixed string.
...
<input v-model="fbProfileLink"/>
...
export default {
components: {contactInfo},
props: ['data'],
data() {
return {
fbProfileLink: 'https://www.facebook.com/'
}
}
}
If you're making any requests to that link though, you probably shouldn't let the user manipulate it freely but put guards in place so you won't be making requests to a user provided URL blindly.

react-apollo mutation doesn't refetch

I have a component RecipeList and another AddRecipe.after adding a recipe it should be redirected to RecipeList and all items be displayed.
here is the code of AddRecipe Component.
import React ,{Component}from 'react'
import { Mutation } from "react-apollo";
import {ADD_RECIPE} from '../../mutations';
import Error from '../Error';
import {withRouter} from 'react-router-dom';
import {GET_ALL_RECIPIES} from '../../queries'
class AddRecipe extends Component {
...
onSubmit(event,addRecipe){
event.preventDefault();
addRecipe().then(
({data})=>
{
this.props.history.push("/")
}
)
}
render (){
const{name,category,description,instruction,username} = this.state;
return(<div className="App">
<h2>Add recipe</h2>
<Mutation
mutation={ADD_RECIPE}
variables={{name,category,description,instruction,username}}
update={(cache, {data:{addRecipe}}) => {
const {getAllRecipes} = cache.readQuery({ query: GET_ALL_RECIPIES });
cache.writeQuery({
query:GET_ALL_RECIPIES,
data:{getAllRecipes:getAllRecipes.concat[addRecipe]}
})
}} >
{(addRecipe, {data,loading,error})=>
(<form className="form" onSubmit={event=>this.onSubmit(event,addRecipe)}>
<input type="text" name="name" onChange={this.handleChange.bind(this)} placeholder="recipe name" value={name}/>
<select name="category" value="breakfast" id="" onChange={this.handleChange.bind(this)} value={category}>
<option value="breakfast">breakfast</option>
<option value="lunch">lunch</option>
<option value="dinner">dinner</option>
<option value="snack">snack</option>
</select>
<input type="text" name="description" onChange={this.handleChange.bind(this)} placeholder="recipe description" value={description}/>
<textarea name="instruction" id="instruction" cols="30" rows="10" onChange={this.handleChange.bind(this)} value={instruction}> </textarea>
<button type="submit" className="button-primary" disabled = {loading || this.validateForm()}>submit</button>
{error && <Error error={error}/>}
</form>)
}
</Mutation>
</div>)
}
my add recipe mutation is :
import gql from 'graphql-tag';
//RECIPE QUERY
export const GET_ALL_RECIPIES =gql`
query {
getAllRecipes{
name
_id
category
}
}
`
and finally get recipe list query is :
import gql from "graphql-tag";
export const GET_RECIPE_ITEM =gql`
query($id:ID!){
getRecipeItem(_id:$id){
_id
name
category
description
instruction
createdDate
likes
username
}
}`
after submitting form and adding recipe I expect that
first component is redirected to recipe list and
second component the list of recipes includes the newly added recipe .
but I see the old list that does not contain any of newly added recipes and for displaying new components I should refresh the page .
Apollo Client caches data for performance improvements and it does not refetch a list of your recipes because it already in cache.
In order to overcome this, you can either change fetchPolicy from default cache-first to cache-and-network or network-only but I don't recommend this in your case or refetch getAllRecipes query after addRecipe mutation like so:
<Mutation
mutation={ADD_RECIPE}
variables={{name,category,description,instruction,username}}
refetchQueries=['getAllRecipes']
>
...
</Mutation>

Set input[text] value according to radio button

I'm trying to set the value of an input corresponding to the selected radio button, but I just can't figure out how.
<input type="text" class="input" name="provider" v-model="selected_provider">
<b-radio-group v-model="provider_options">
<div class="field">
<template v-for="provider in providers">
<b-radio
name="select_provider[]" id="provider_#{{ $index }}"
model="selected_provider"
value="provider"
type="radio">
#{{ provider.provider_name }}
</b-radio>
</template>
</div>
</b-radio-group>
I've managed to get an array to display and that the input shows whatever is written in selected_provider, but I just don't know how to link those together.
var app = new Vue({
el: '#app',
data: {
selected_provider: '',
providers: [
{
provider_name: 'Twitch',
provider_url: 'https://www.twitch.tv/'
},
{
provider_name: 'Smashcast',
provider_url: 'https://www.smashcast.tv/'
},
{
provider_name: 'AzubuFrost',
provider_url: 'https://www.smashcast.tv/'
}
]
},
});
furthermore I'd like to attach the value of
<input type="text" class="input" name="name" id="name">
to the end of provider_url in selected_provider
Is someone so kind to help me?
If you set your for-loop to v-for="(provider, index) in providers"You can put a v-on:click="setSelectedProvider(index)" inside <b-radio></b-radio> and can create a method that looks like this:
setSelectedProvider: function(index){
this.$set(this, 'selected_provider', index);
}
Whenever a radio button is clicked this will set selected_provider to the index of the clicked radio button.
As for attaching the value of your other input to the end of provider_url, you can create a method that does this:
updateProviderURL: function(event){
this.$set(this.providers[this.selected_provider], 'provider_url',
'https://www.twitch.tv/' + event.currentTarget.value);
}
You can then add a listener to your input that looks like this:
v-on:input="updateProviderURL($event)"
It's good to set selected_provider by default to 0 inside the data part of your component so that the updateProviderURL function never tries to set a part of the array that doesn't exist ;)

Angular2 cannot enter text into input field

I am using Angular2 and when users go to my page using IE11 on Windows 7 (which appears to be the only combination that has issues) they cannot enter information into the textboxes/textareas. The drop-downs work just fine as do the checkboxes (which will show/hide additional fields as needed although you still cannot enter information into the new text fields).
If the user clicks the submit button all the required fields show as needing input and then the user can enter the information.
If it matters, the users are redirected to this page after enter RECAPTCHA information. If I take out the "canActivate" from my app routing module it works.
Here is the captcha code
import { Component } from '#angular/core';
import {
Router,
NavigationExtras
} from '#angular/router';
import { CaptchaService } from '../../utils/captchaguard/captcha.service';
import { ReCaptchaComponent } from 'angular2-recaptcha/lib/captcha.component';
#Component({
moduleId: module.id,
templateUrl: 'captcha.component.html'
})
export class CaptchaComponent {
message: string;
constructor(
public captchaService: CaptchaService,
public router: Router) {
//this.captchaService.isLoggedIn = true; //Uncomment out for testing
this.captchaService.isLoggedIn = false; //Uncomment out for live
}
handleCorrectCaptcha(userResponseKey: any) {
let redirect = this.captchaService.redirectUrl ? this.captchaService.redirectUrl : '/admin';
let redirectParts = redirect.split('/');
let appendID = redirect[redirect.length - 1];
//let appendID = 0;
this.captchaService.isLoggedIn = true;
//this.router.navigate(['/studyfd' + appendID]);
this.router.navigateByUrl('/studyfd' + appendID);
//this.router.navigate(['/studytest/0']);
}
}
Here is a portion of the HTML that generates the page in question:
<!-- First and Last Name -->
<div class="row">
<div class="col-sm-6">
First Name, Last Name
</div>
<div class="col-sm-3">
<input type="text" name="FirstName" id="firstname" required="required"
placeholder="First Name" class="required "
[(ngModel)]="user.FirstName" *ngIf="!readonly" />
<label *ngIf="readonly">{{user.FirstName}}</label>
</div>
<div class="col-sm-3">
<input type="text" name="LastName" id="lastName" required="required"
placeholder="Last Name" [(ngModel)]="user.LastName" *ngIf="!readonly"
class="required " />
<label *ngIf="readonly">{{user.LastName}}</label>
</div>
</div>
Turns out the issue was due to the RECAPTCHA window not closing (probably because of the redirect I was doing in handleCorrectCaptcha). Resolved by adding a hidden button and in the handleCorrectCaptcha method I made the button visible. Clicking on the button performed the redirect that was originally done in the handleCorrectCaptcha method.

Form validation is not working with Angular 2 FormBuilder in Ionic 2

I am building a Ionic 2 (typescript) application. Having problem ONLY in a simple model-driven form validation in checkbox (ion-checkbox tag).
I'm using FormBuilder to build the form module. I want the form to be validated / not validated based on the checked state of a checkbox input control. But its working one-way only. Here's my code :
reg.html
<ion-content padding>
<form class="client_profile" [formGroup]="regForm">
<div class="profile_pic" id="profile_pix">
<img src="build/images/home.svg" id="client_camera_pic" />
</div>
<ion-item>
<ion-label floating>What we would call you?</ion-label>
<ion-input type="text" formControlName="client_name"></ion-input>
</ion-item>
<ion-item>
<ion-label floating>Choose your username</ion-label>
<ion-input type="text" name="client_username" value="" #client_username formControlName="client_username"></ion-input>
</ion-item>
<ion-item>
<ion-label floating>Where we will drop your Email?</ion-label>
<ion-input type="email" name="client_email" value="" #client_email formControlName="client_email"></ion-input>
</ion-item>
<ion-item>
<ion-label floating>We also need your phone number</ion-label>
<ion-input type="number" name="client_phone" value="" #client_phone formControlName="client_phone"></ion-input>
</ion-item>
<ion-item class="reg_terms">
<ion-checkbox secondary #terms name="terms" value="" checked="false" formControlName="terms"></ion-checkbox>
<ion-label>I accept the Terms & Conditions</ion-label>
</ion-item>
</form>
<div>
<ion-buttons right class="client_reg_done">
<button danger class="reg_complete" (click)="process_client_reg()" [disabled]="!regForm.valid">NEXT</button>
</ion-buttons>
</div>
</ion-content>
reg.ts
import { Component, ViewChild, ElementRef } from '#angular/core';
import { FORM_PROVIDERS, FormBuilder, FormControl, FormGroup, AbstractControl, Validators, REACTIVE_FORM_DIRECTIVES } from '#angular/forms';
import { App, NavController, ViewController, ModalController, LoadingController, LoadingOptions } from 'ionic-angular';
#Component({
templateUrl: "build/pages/registration/reg.html"
})
export class Registration {
ngAfterViewInit(){
}
regForm: FormGroup;
constructor(public app: App, public nav: NavController, public viewCtrl: ViewController, public elem: ElementRef, public modalCtrl: ModalController, public loading: LoadingController, public fb: FormBuilder){
}
ngOnInit() {
this.regForm = this.fb.group({
client_name: ['', Validators.compose([Validators.required, Validators.maxLength(30), Validators.pattern('^[a-zA-Z\. ]+$')])],
client_username: ['', Validators.compose([Validators.required, Validators.maxLength(20), Validators.pattern('[a-zA-Z0-9-_\.]+')])],
client_email: ['', Validators.compose([Validators.required, Validators.pattern('^[a-zA-Z0-9-_]+#[a-zA-Z]+\.[a-zA-Z]{2,4}$')])],
client_phone: ['', Validators.compose([Validators.required, Validators.pattern('^[\+0-9]{10,12}$')])],
terms: [null, Validators.required]
});
}
}
Actual view:
Checking / unchecking the Terms & Condition checkbox doesn't updating the validation logic and the 'NEXT' button disabled state is not updating. Its because form validation isn't taking account this checkbox. Some help is appreciated.
Initialising your terms control with null seems to break the emission of change events. Try adding this subscription:
this.regForm.valueChanges.subscribe(v => {
console.log('Required validation failed: ' + this.form.controls['terms'].hasError('required'));
console.log('Required validation touched: ' + this.form.controls['terms'].touched);
console.log('Required validation status: ' + this.form.controls['terms'].status);
});
Toggling the terms checkbox doesn't result in any logging. This might be a bug.
Now change your control to be initialised as false and try again: terms: [false, Validators.required] This time the logging happens and you can see what's going on.
Unfortunately, now the control is in the valid state as soon as it loads. You can check that with similar logging after building your FormGroup. The best solution would be to create an is-true custom validator, but a quick and dirty solution would be to change your button:
[disabled]="!regForm.valid || !form.controls.terms.touched"

Resources