Reputation: 962
Edit 2: Improved upon the partial solution and now it's a complete solution. Yay!
Edit: Found an almost complete solution. It works, but not quite the way I want it.
The solution is posted after the text below.
I'm a new student to Angular and I'm making a simple CRUD app to understand how the language works. Currently, I'm learning how to do form validations.
As mentioned in the title, I want to have a validation method that checks if the input is empty, and if it is then to enter a default value. I'm looking for a clean and simple way to do this. Currently here is my code.
partial post.component.html
(sorry about the multiple lines for the <input>
element; I find it easier to read this way)
<modal #modal>
<form novalidate (ngSubmit)="onSubmit(postForm)" [formGroup]="postForm">
<modal-header [show-close]="true">
<h4 class="modal-title">{{modalTitle}}</h4>
</modal-header>
<modal-body>
<div class="form-group">
<div>
<span>Post Title</span>
<input [(ngModel)]="TitleText"
type="text" class="form-control"
placeholder="Post Title"
formControlName="PostTitle">
<div class="error"
*ngIf="postForm.get('PostTitle').hasError('required') && postForm.get('PostTitle').touched">
Title is required
</div>
<div class="error"
*ngIf="postForm.get('PostTitle').hasError('minlength') && postForm.get('PostTitle').touched">
Minimum title length is three (3) characters
</div>
</div>
<div>
<span>Post Slug</span>
<input [ngModel]="TitleText | slugify"
type="text"
class="form-control"
placeholder="Post Slug"
formControlName="PostSlug">
<div class="error"
*ngIf="postForm.get('PostSlug').hasError('pattern') && postForm.get('PostSlug').dirty">
URL slug must not contain spaces, special characters or capitalization
</div>
</div>
<div>
<span>Post Content</span>
<textarea class="form-control"
placeholder="Post Content"
formControlName="PostContent">
</textarea>
</div>
<div>
<span>Post Author</span>
<input type="text"
class="form-control"
placeholder="Post Author"
formControlName="PostAuthor">
</div>
</div>
</modal-body>
<modal-footer>
<div>
<a class="btn btn-default" (click)="modal.dismiss()">Cancel</a>
<button type="submit"
[disabled]="postForm.invalid"
class="btn btn-primary">
{{modalBtnTitle}}
</button>
</div>
</modal-footer>
</form>
</modal>
partial post.component.ts
ngOnInit(): void {
this.postForm = this.fb.group({
PostId: [''],
PostTitle: [
'',
[
Validators.required,
Validators.minLength(3)
]
],
PostSlug: [
'',
[
Validators.required,
Validators.pattern('^[a-z0-9-]+$')
]
],
PostContent: [''],
PostAuthor: ['']
});
this.LoadPosts();
}
I would like a default value to be passed if the PostContent
and PostAuthor
fields are left empty. One way that I thought would have worked was to have a default value in the template like so:
<textarea class="form-control"
placeholder="Post Content"
formControlName="PostContent"
value="Not Set">
However this doesn't work and debugging it shows that null is still returned:
Just as well, because that solution would have caused a problem anyway if someone decided to type something then delete it.
For reference, here's the whole post.component.ts
:
import { Component, OnInit, ViewChild, Pipe } from '@angular/core';
import { PostService } from '../Services/post.service';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ModalComponent } from 'ng2-bs3-modal/ng2-bs3-modal';
import { IPost } from '../Models/post';
import { DBOperation } from '../Shared/enum';
import { Observable } from 'rxjs/Rx';
import { Global } from '../Shared/global';
@Component({
templateUrl: '/app/Components/post.component.html'
})
export class PostComponent implements OnInit {
@ViewChild('modal') modal: ModalComponent;
posts: IPost[];
post: IPost;
msg: string;
indLoading: boolean = false;
postForm: FormGroup;
dbops: DBOperation;
modalTitle: string;
modalBtnTitle: string;
constructor(private fb: FormBuilder, private _postService: PostService) { }
ngOnInit(): void {
this.postForm = this.fb.group({
PostId: [''],
PostTitle: [
'',
[
Validators.required,
Validators.minLength(3)
]
],
PostSlug: [
'',
[
Validators.required,
Validators.pattern('^[a-z0-9-]+$')
]
],
PostContent: [''],
PostAuthor: ['']
});
this.LoadPosts();
}
LoadPosts(): void {
this.indLoading = true;
this._postService.get(Global.BASE_POST_ENDPOINT)
.subscribe(posts => { this.posts = posts; this.indLoading = false; },
error => this.msg = <any>error);
}
addPost() {
this.dbops = DBOperation.create;
this.SetControlsState(true);
this.modalTitle = "Add New Post";
this.modalBtnTitle = "Add";
this.postForm.reset();
this.modal.open();
}
editPost(id: number) {
this.dbops = DBOperation.update;
this.SetControlsState(true);
this.modalTitle = "Edit Post";
this.modalBtnTitle = "Update";
this.post = this.posts.filter(x => x.PostId == id)[0];
this.postForm.setValue(this.post);
this.modal.open();
}
deletePost(id: number) {
this.dbops = DBOperation.delete;
this.SetControlsState(false);
this.modalTitle = "Confirm Post Deletion?";
this.modalBtnTitle = "Delete";
this.post = this.posts.filter(x => x.PostId == id)[0];
this.postForm.setValue(this.post);
this.modal.open();
}
SetControlsState(isEnable: boolean) {
isEnable ? this.postForm.enable() : this.postForm.disable();
}
onSubmit(formData: any) {
this.msg = "";
switch (this.dbops) {
case DBOperation.create:
this._postService.post(Global.BASE_POST_ENDPOINT, formData._value).subscribe(
data => {
if (data == 1) //Success
{
this.msg = "Post successfully added.";
this.LoadPosts();
}
else {
this.msg = "There is an issue with creating the post, please contact the system administrator!"
}
this.modal.dismiss();
},
error => {
this.msg = error;
}
);
break;
case DBOperation.update:
this._postService.put(Global.BASE_POST_ENDPOINT, formData._value.PostId, formData._value).subscribe(
data => {
if (data == 1) //Success
{
this.msg = "Post successfully updated.";
this.LoadPosts();
}
else {
this.msg = "There is an issue with updating the post, please contact the system administrator!"
}
this.modal.dismiss();
},
error => {
this.msg = error;
}
);
break;
case DBOperation.delete:
this._postService.delete(Global.BASE_POST_ENDPOINT, formData._value.PostId).subscribe(
data => {
if (data == 1) //Success
{
this.msg = "Post successfully deleted.";
this.LoadPosts();
}
else {
this.msg = "There is an issue with deleting the post, please contact the system administrator!"
}
this.modal.dismiss();
},
error => {
this.msg = error;
}
);
break;
}
}
}
Should I look into adding the Null check in the onSubmit
method (or is it function?) instead?
Potential solution, needs improvement
So in the addPost()
part of the post.component.ts
above, I added two lines:
addPost() {
this.dbops = DBOperation.create;
this.SetControlsState(true);
this.modalTitle = "Add New Post";
this.modalBtnTitle = "Add";
this.postForm.reset();
this.modal.open();
this.postForm.controls['PostContent'].setValue('not set'); //this here is the magic
this.postForm.controls['PostAuthor'].setValue('not credited'); // and this too
}
This basically sets the value from the moment the modal box to enter a new entry is loaded. The value can be deleted from the input and when that happens it's replaced with an empty string. I don't know where this empty string value is coming from.
Upvotes: 6
Views: 103913
Reputation: 962
Alright, so I've found an elegant solution for this. Basically, I need to set the initial value when the form is loaded. To do this, I add use the setValue method for the form controls.
addPost() {
this.dbops = DBOperation.create;
this.SetControlsState(true);
this.modalTitle = "Add New Post";
this.modalBtnTitle = "Add";
this.postForm.reset();
this.modal.open();
this.postForm.controls['PostContent'].setValue('not set'); //this here is the magic
this.postForm.controls['PostAuthor'].setValue('not credited'); // and this too
}
Doing this seemed to have solved it in a clunky way. The value is pre-populated in the relevant input fields and if it is deleted, then the value becomes an empty string. This is not ideal.
So I flipped it around and did it this way:
this.postForm.controls['PostContent'].setValue('');
this.postForm.controls['PostAuthor'].setValue('');
Here I set the value to an empty string. It's not null, but the input fields are not pre-populated and my placeholder
value is still visible. Excellent. But what about the empty string?
We handle that before the data gets passed to the service that does the HttpPost.
onSubmit(formData: any) {
this.msg = "";
if (formData._value.PostContent == "")
{
this.postForm.controls['PostContent'].setValue('not set');
}
if (formData._value.PostAuthor == "")
{
this.postForm.controls['PostAuthor'].setValue('not credited');
}
. . .
What happens is if the value being passed for those specific keys is an empty string, then set the value to the default.
I think this is a great way to handle this. It's clean and though I am not sure if it's possible to do this, in theory it should not be difficult to put all the default values in a key-value-pair array and access it that way.
Upvotes: 1
Reputation: 3170
Regardless of what 'form' (reactive or template driven) you are using, if all you want is to have a default value whenever the input becomes empty, you can make use of the ngModelChange
event. Have to import FormsModule
though to your module (where you use this component having this template as its view) for it to work. Use ngModel
to pass the initial value, if you have any.
component
// define the type
postAuthorInitValue: string;
postAuthorDefaultValue = 'DEFAULT VALUE';
// or initialize it with a value, assign the right type (here I used string type)
// postAuthorInitValue = 'Abc Xyz';
postAuthorChanged(newVal: string): void {
if (newVal) {
this.postAuthorInitValue = newVal;
} else if (newVal === '') {
// here is where we put the default value when the 'newVal' is empty string
this.postAuthorInitValue = this.postAuthorDefaultValue;
} else {
this.postAuthorInitValue = newVal;
}
}
template
<input type="text"
[ngModel]="postAuthorInitValue"
(ngModelChange)="postAuthorChanged($event)"
class="form-control"
placeholder="Post Author"
formControlName="PostAuthor">
Another option would be to use a custom directive that can be placed on every <input type="text">
element that reads its value and a default value and sets the default value to the element whenever the field becomes empty.
Hope it helps.
Upvotes: 3
Reputation: 2213
EDIT: Ok thought this would be easier, but I was wrong.
You'd probably need something like a function checkme()
in your TS file that is called when your submit button is called, <button (click)="checkme()">Submit</button>
that checks:
checkme() {
if (TitleText===null) {
TitleText = 'default value';
}
}
or something.
Irrelevant below but might be useful
If you have:
<input [(ngModel)]="TitleText">
Then running
{{TitleText || 'placeholder'}}
will give you the string 'placeholder' if the input box is empty (TitleText is null).
It would be the same principle with number inputs where you'd put:
<input [(ngModel)]="mynumber">
and writing
{{mynumber || 0}}
to get 0 as a placeholder instead of NaN
Upvotes: 1