Nested reactive forms in angular with required validator - angular-reactive-forms

How can I pass the required validator to a nested form? I made a project to explain:
https://stackblitz.com/edit/angular-nested-forms-cva-3b17dm?file=src%2Fapp%2Fbasic-info%2Fbasic-info.component.ts
This does not work as expected.
Any ideas?
Thank you

Here I consider a userProfile FormGroup as an example
You have to access as follows
f.address.controls.city.invalid
.ts file should be like this.
export class Home implements OnInit {
userProfileForm : FormGroup;
ngOnInit() {
this.userProfileForm = new FormGroup({
'userName': new FormControl('', [Validators.required]),
'address': new FormGroup({
'city': new FormControl('', [Validators.required])
})
});
}
}
In the HTML part, it should be edited as follows.
<form [formGroup]="userProfileForm " (ngSubmit)="onSubmit()">
<div formGroupName="address">
<input type="text" formControlName="city" />
<ng-container *ngIf="!userProfileForm.get('address.city').valid && userProfileForm.get('address.city').touched">
<span>This is required</span>
</ng-container>
</div>
</form>

Related

Vue.js/Laravel: pass category id to Vue.js component

I'm using Vue.js with Laravel and facing a problem. I want to pass category id from the blade file to the Vue.js component as a prop. But don't know what is good practice and the right way for this.
I've defined the route something like this:
Route::view('/categories/{category}/edit', 'edit')->name('categories.edit');
and my edit.blade.php file is:
#extends('master')
#section('vue')
<div id="app">
<categories-edit :id=""></categories-edit>
</div>
#endsection
The Vue.js component code is:
<template>
<div class="container py-5">
<div class="row">
<div class="col-lg-12">
<div class="mb-3">
<label for="name" class="form-label">Name:</label>
<input type="text" v-model="formState.name" name="name" class="form-control" id="name" placeholder="Category Name" autocomplete="off">
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'CategoriesEdit',
props: ['id'],
data: function () {
return {
formState: {
name: '',
photo: ''
}
}
},
mounted() {
},
methods: {
loadInitialData: function () {
const self = this;
axios.get(``).then(function (response) {
}).catch(function (err) {
});
}
}
}
</script>
When I'm entering the URL in the web browser. I'm getting this error.
http://example.test/categories/1/edit
Output:
Undefined variable $category
Since you are using Route::view() you do not have the traditional way of getting route parameters and pass them to the view. Luckily you can always get these on the request object and there is a request() helper that makes it easier for Blade views.
<categories-edit :id="{{ request()->route('category') }}"></categories-edit>

How to show only one validation at a time in angular 6 form control validators

I am using form control to apply validation in material angular 6 application.
Below is the code of validator :-
paymentOffBankName: new FormControl('', Validators.compose([
Validators.required,
Validators.maxLength(this.responseMap.get('ev_payment_t.bank_name').values.maxSize),
Validators.minLength(this.responseMap.get('ev_payment_t.bank_name').values.minSize),
Validators.pattern(this.responseMap.get('ev_payment_t.bank_name').values.validationExp)
])),
I have one method to display the message related validator :-
paymentOffBankName': [
{ type: 'required', message: 'Required') },
{ type: 'pattern', message: 'Invalid Name' },
{ type: 'minlength', message: 'Requires atleast 3 letters'
},
],
Here is my html which shows the error :-
<mat-form-field fxFlex="{{responseMap.get('ev_payment_t.bank_name').values.maxSize}}">
<input required matInput placeholder="{{responseMap.get('ev_payment_t.bank_name').values.label}}"
formControlName="paymentOffBankName" maxlength="{{responseMap.get('ev_payment_t.bank_name').values.maxSize}}">
<mat-error *ngFor="let validation of validationMessages.paymentOffBankName">
<mat-error class="error-message" *ngIf="offlinePaymentService.OfflinePayment_form.get('paymentOffBankName').hasError(validation.type) && (offlinePaymentService.OfflinePayment_form.get('paymentOffBankName').dirty || offlinePaymentService.OfflinePayment_form.get('paymentOffBankName').touched)">{{validation.message}}</mat-error>
</mat-error>
</mat-form-field>
My Problem is, If I put invalid name with 2 letters, then it shows two validation messages.
Expectation: It should show only one message, I will put one common message like 'Required, MinLengh-3, Alpha Numeric only'
To show only one validation message at a time, try to use messageKey in which you can pass composeMessageKey which contains all the validation messages of the field.
Declare the composeMessageKey in app component
export class AppComponent implements OnInit {
ngOnInit(){
ReactiveFormConfig.set({"validationMessage":{
"composeMessageKey":"please enter valid input"
}});
}
}
and in component ts:
ngOnInit() {
this.userFormGroup = this.formBuilder.group({
paymentOffBankName:['', RxwebValidators.compose(
{validators:[
RxwebValidators.alpha(),
RxwebValidators.maxLength({value:3}),
RxwebValidators.minLength({value:4}),
],messageKey:"composeMessageKey",
})
]
});
}
For that i have used validators of #rxweb validators(RxwebValidators) and passed the MessageKey
Html :
<div>
<form [formGroup]="userFormGroup">
<div class="form-group">
<label>Payment Off BankName</label>
<input type="text" formControlName="paymentOffBankName" class="form-control" />
</div>
<small class="form-text text-danger" *ngIf="userFormGroup.controls.paymentOffBankName.errors">{{userFormGroup.controls.paymentOffBankName.errors.composeMessageKey?.message}}<br/></small>
<button [disabled]="!userFormGroup.valid" class="btn btn-primary">Submit</button>
</form>
</div>
Here is the stackblitz example : Stackblitz

Vue/Laravel How to pass form from blade to vue to use checkValidity

Made following button made by vue(single component).
<template>
<div>
<input type="submit" class="button_orange button_big" value="Register" v-show="!isSubmitting" #click="startSubmit">
<div class="button_disable button_big" v-show="isSubmitting">Register</div>
</div>
</template>
<script>
export default {
data() {
return {
isSubmitting: false,
};
},
methods: {
startSubmit() {
if (form.checkValidity()) {
this.isSubmitting = true;
}
},
reset() {
this.isSubmitting = false;
},
},
};
</script>
Current problem is how to pass the form to vue to use checkValidity() method. laravel blade is the following type source code.
if there is idea how to pass form from blade to vue, please help.
<form>
<input type="text" name="test">
<submit-button></submit-button>
</form>
What's the problem with this?
document.querySelector("form").checkValidity()
(Of course this would require to have this for as the only form on the page, so if you can, you should have an ID on the form and go for something like document.querySelector("#myForm01").checkValidity() instead.)

Redirection from a vue component to a laravel route

I have a Vue component named searchbox and I want the users to get redirected to display the results once they type the name and click the search button. I am using axios to make the http request. Here's my template:
<form #submit.prevent="searchResult">
<div class="field has-addons searchbox">
<div class="control">
<input class="input" type="text" id="search" name="q" placeholder="Search a video..." #keyup.enter="searchResult" v-model="searchData">
</div>
<div class="control">
<button class="button is-primary"><i class="fa fa-search"></i></button>
</div>
</div>
</form>
Here's my script in the Vue file:
<script>
export default {
data() {
return {
searchData: null,
};
},
methods: {
searchResult() {
axios.get('/search?q=' + this.searchData);
}
}
}
</script>
Here's my search controller:
class SearchController extends Controller {
public function index(Request $request) {
return view('search.index');
}
}
However, I can not see the redirection. How do I redirect from vue component to another route in laravel??
Is vue-router necessary or we can follow any other method??
you can replace axios.get('/search?q=' + this.searchData); with window.location.href = '/search?q=' + this.searchData;

Using redux-form I'm losing focus after typing the first character

I'm using redux-form and on blur validation. After I type the first character into an input element, it loses focus and I have to click in it again to continue typing. It only does this with the first character. Subsequent characters types remains focuses. Here's my basic sign in form example:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import * as actions from '../actions/authActions';
require('../../styles/signin.scss');
class SignIn extends Component {
handleFormSubmit({ email, password }) {
this.props.signinUser({ email, password }, this.props.location);
}
renderAlert() {
if (this.props.errorMessage) {
return (
<div className="alert alert-danger">
{this.props.errorMessage}
</div>
);
} else if (this.props.location.query.error) {
return (
<div className="alert alert-danger">
Authorization required!
</div>
);
}
}
render() {
const { message, handleSubmit, prestine, reset, submitting } = this.props;
const renderField = ({ input, label, type, meta: { touched, invalid, error } }) => (
<div class={`form-group ${touched && invalid ? 'has-error' : ''}`}>
<label for={label} className="sr-only">{label}</label>
<input {...input} placeholder={label} type={type} className="form-control" />
<div class="text-danger">
{touched ? error: ''}
</div>
</div>
);
return (
<div className="row">
<div className="col-md-4 col-md-offset-4">
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))} className="form-signin">
<h2 className="form-signin-heading">
Please sign in
</h2>
{this.renderAlert()}
<Field name="email" type="text" component={renderField} label="Email Address" />
<Field name="password" type="password" component={renderField} label="Password" />
<button action="submit" className="btn btn-lg btn-primary btn-block">Sign In</button>
</form>
</div>
</div>
);
}
}
function validate(values) {
const errors = {};
if (!values.email) {
errors.email = 'Enter a username';
}
if (!values.password) {
errors.password = 'Enter a password'
}
return errors;
}
function mapStateToProps(state) {
return { errorMessage: state.auth.error }
}
SignIn = reduxForm({
form: 'signin',
validate: validate
})(SignIn);
export default connect(mapStateToProps, actions)(SignIn);
This happens because you're re-defining renderField as a new component every time you render which means it looks like a new component to React so it'll unmount the original one and re-mounts the new one.
You'll need to hoist it up:
const renderField = ({ input, label, type, meta: { touched, invalid, error } }) => (
<div class={`form-group ${touched && invalid ? 'has-error' : ''}`}>
<label for={label} className="sr-only">{label}</label>
<input {...input} placeholder={label} type={type} className="form-control" />
<div class="text-danger">
{touched ? error: ''}
</div>
</div>
);
class SignIn extends Component {
...
render() {
const { message, handleSubmit, prestine, reset, submitting } = this.props;
return (
<div className="row">
<div className="col-md-4 col-md-offset-4">
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))} className="form-signin">
<h2 className="form-signin-heading">
Please sign in
</h2>
{this.renderAlert()}
<Field name="email" type="text" component={renderField} label="Email Address" />
<Field name="password" type="password" component={renderField} label="Password" />
<button action="submit" className="btn btn-lg btn-primary btn-block">Sign In</button>
</form>
</div>
</div>
);
}
}
...
As #riscarrott mentioned, put renderField outside of component class .
But I am still losing focus .. And after testing, I concluded the re-rendering is done because of using curried function (return another function, and not return element . directly) .
const const renderField = (InputComponent = 'input') => ({ input, label, type, meta: { touched, invalid, error } }) => (
<div class={`form-group ${touched && invalid ? 'has-error' : ''}`}>
<label for={label} className="sr-only">{label}</label>
<InputComponent {...input} placeholder={label} type={type} className="form-control" />
<div class="text-danger">
{touched ? error: ''}
</div>
</div>
);
Then, if your renderField is a curried function :
then , don't do 😔😔😔😔:
//.....
<Field name="email" type="text" component={renderField('input')} label="Email Address" />
<Field name="desc" component={renderField('textarea')} label="Email Address" />
But , do the following 🙂🙂🙂🙂 :
// outside component class
const InputField = renderField('input');
const TextAreaField = renderField('textarea');
// inside component class
<Field name="email" type="text" component={InputField} label="Email Address" />
<Field name="desc" component={TextAreaField} label="Email Address" />
What worked for me was refactoring arrowFunction-based Component to class-based Component as the behavior of InputForm components was weird. Every time the value of each input was changed they all rerendered even after splitting each inputType to separated components. There was nothing else left to fix but changing main component to class-based. I guess it may be caused by redux-form itself.
This can also happen if you have defined styled-components inside your render function.
You should define them outside your class.
Like this:
const Row = styled.div`
justify-content:center;
`;
const Card = styled.div`
width:18rem;
padding:1rem;
`;
class Login extends Component{
i have the same problem. i resolved mine by changing the component to Class component and i removed all the css style config from render().
I had the same problem. I solved it when I added my react redux form to the store in the createForms():
export const ConfigureStore = () => {
const store = createStore(
combineReducers({
tasks: Tasks,
task: Task,
image: Image,
admin: Admin,
pageId: PageID,
fieldValues: FieldValues,
formValues: FormValues,
...createForms({
createTask: initialTask,
editTask: initialEditTask
})
}),
applyMiddleware(thunk, logger)
);
return store;
}
I had the same problem, and none of the answers worked for me.
But thanks to Advem's answer I got an idea of what could be wrong:
My form required accordion UI, and for that I had state variable in it:
const ConveyorNotificationSettingsForm = (props) => {
const {handleSubmit, formValues, dirty, reset, submitting} = props;
const [expandedSection, setExpandedSection] = useState(null);
...
with only one expanded section, that with its index equal to expandedSection .
After I extracted the accordion to a separate functional component and moved useState there, the problem was gone.
actually, this is a problem with the function component. I used a class-based component with redux form and my problem solved. I don't know the exact reason but redux form re-renders when we enter the first word and losses focus. use class-based components whenever you want to use redux form.
class StreamCreate extends React.Component{
rendorInput(formProps){
return <input {...formProps.input} />;
}
render(){
return (
<Container maxWidth="lg">
<form action="">
<Field name="title" component={this.rendorInput}/>
<Field name="description" component={this.rendorInput} />
</form>
</Container>
)
}
}
export default reduxForm({
form: 'createStream'
})( StreamCreate);

Resources