Angular - if form block in focus, display inputs - javascript

I've got a form in angular that looks something like this (shortened version with its context removed):
<form [formGroup]="myForm">
Name: <input type="text" formControlName="myName">
<div>
Gender: <input type="text" formControlName="myGender">
</div>
</form>
what I want to accomplish is, by clicking on the gender input field, 2 radio buttons will be displayed and when clicking somewhere outside the gender formblock, the buttons disappear. for that I added a boolean variable to my components ts:
export class MyFormComponent implements OnInit {
genderFocused: boolean = false;
constructor(
) { }
ngOnInit() {
}
}
and tried something like this:
<form [formGroup]="myForm">
Name: <input type="text" formControlName="myName">
<div tabindex="-1" (focusout)="genderFocused = false">
Gender: <input type="text" formControlName="myGender" (focus)="genderFocused = true">
<label>Yes<input type="radio"></label>
<label>No<input type="radio"></label>
</div>
</form>
with that however, as soon as I click on one of the radio buttons, they immediately disappear. I looked around and have not found anything that helped me solve this issue.
I replaced the radio buttons with a text input field, and it behaved the exact same way. as soon as the input field within the div is focused, it disappears, which is not intended

You can use #HostListener to check if element is clicked outside and set a flag for the *ngIf condition:
#HostListener('document:click', ['$event'])
clickout(event) {
if(this.eRef.nativeElement.contains(event.target)) {
this.clickedOutside = false;
} else {
this.clickedOutside = true;
}
}
As this answer suggested:
Detect click outside Angular component

Related

How can I conditionally render LitElement with TypeScript?

So I am mapping through an array of objects and looking to display on the page when a radio button is selected. For example, if there are two objects in the array, there will be two radio buttons. If you press radio1, it should render form1. If you press radio2, it should hide form1 and show form2.
I created a property called formIndex to keep track of which button is being pressed so that I know which form to call but I'm having trouble implementing.
Current behavior: on page load, both radios appear with no data rendered yet. When I press radio1, it displays both form1 and form2. When I press radio2, it also displays both form1 and form2.
I'm using LitElement in a TypeScript file.
Here is the property I created. Since I'm mapping through an array starting at 0, I initialized this property to -1:
#state()
formIndex: number = -1;
Here is where I am rendering the forms:
protected renderMultipleForms(formConfig: any): TemplateResult {
return html`
${formConfig?.formHeading ? html`<h3>${formConfig.formHeading}</h3>` : ''}
${formConfig.forms?.map((data: any) => html`
<!-- <adc-radio-group>
<adc-radio-button id="radioBtn" label=${data.label} #click="${this.handleClick}"></adc-radio-button>
</adc-radio-group> -->
<!-- RADIOS -->
<input type="radio" id=${data.label} name="paymentRadios" #click="${this.handleClick}">${data.label} <br />
<!-- RENDERING FORMS -->
<p id=${this.formIndex}>${this.formIndex > -1 ? this.renderForm(data.form, data.paymentIcons) : ''}</p>
`)}
`;
}
Finally, here is the method to handle the clicking of the radios:
protected handleClick(e: any){
if(e.target.id == this._data.forms[0].label){
this.formIndex = 0
} else if(e.target.id == this._data.forms[1].label) {
this.formIndex = 1
}
console.log(this.formIndex);
}
How can I make it to where only the first form is displayed when the first radio is clicked and only the second form is displayed when the second radio is clicked? Thanks!
I would just use conditional rendering as described on the lit.dev docs
A minimal example looks like this:
#customElement('test-cond-render')
export class CondRender extends LitElement {
#state()
selectedForm = -1;
formData = [
{id: 0, title: 'form one'},
{id: 1, title: 'form two'}
];
renderForm() {
if(this.selectedForm === -1) {
return html`
<span>Please select a form</span>
`;
} else if (this.selectedForm === 0) {
return html`
<form action="#" target="_blank">
<p>
Select your interests:<br>
<input type="checkbox" name="movies"> Movies<br>
<input type="checkbox" name="sports"> Sports<br>
<input type="checkbox" name="videogames"> Videogames
</p>
<input type="submit" value="Send data">
</form>
`;
} else if (this.selectedForm === 1) {
return html`
<form action="#" target="_blank">
<p>
Enter your full name: <input type="text" name="fullname">
<input type="submit" value="Send data">
</p>
</form>
`;
} else {
return html`
<span>Something went wrong..</span>
`;
}
}
handleClick(form_id:number) {
console.log('handle click:', form_id);
this.selectedForm = form_id;
}
render() {
return html `
<h2>Cond render</h2>
${this.formData.map((data) =>
html`
<input type="radio" name="form-group" value="${data.id}" id="${data.id}" #click="${this.handleClick.bind(this, data.id)}">
<label for="${data.id}">${data.title}</label>
`
)}
<div>${this.renderForm()}</div>
`;
}
}
Your forms might be more complicated but the principle should be the same.
You can also use the cache directive for the render so it won't re-create the DOM for the selected form every time it is switched.
<div>${cache(this.renderForm())}</div>

Angular Validation: Validate/Highlight child input on button click

Using Angular 6 here:
I have a parent component and within that I have a child component. The child component has a text fields. The parent has a submit button.
On the submit click button, I want to validate all the inputs of the child as they are required and throw error accordingly.
I am using reactive forms and was successfully able to pass form info from parent to child. But I am not sure how to highlight my text input when the submit button is clicked.
I have used $touched property on my child, which works and shows the required error. But I want the error to also show in case user just clicked the button.
Here is some relevant code.
--Parent--
<form class="form-horizontal" [formGroup]="myForm" (ngSubmit)="onSubmit()">
<child [myForm]="myForm"></child>
<button type="submit">Submit</button>
</form>
<br>
Form Valid: <pre>{{myForm.valid}}</pre>
export class AppComponent {
myForm: FormGroup
constructor(private fb: FormBuilder) {
this.myForm = this.fb.group({
uname: ['', Validators.required]
});
}
onSubmit() {
console.log('value: ', this.myForm.value);
console.log('the whole form and its controls: ', this.myForm)
}
}
--Child--
<div class="form-group" [formGroup]="myForm">
<label for="myForm" class="col-sm-3 control-label">Name:</label>
<div class="col-sm-8" [ngClass]="{ 'has-error': myForm.controls?.uname.errors }">
<input type="text" formControlName="uname" placeholder="Enter Name...">
<em *ngIf="myForm.hasError('required', 'uname') && myForm.controls?.uname.touched">*Required</em>
</div>
</div>
export class ChildComponent {
#Input() myForm: FormGroup;
ngOnInit() {
}
}
I have also created a demo to show this at:
https://stackblitz.com/edit/angular-dbevnj
FYI, this is just a sample I created to show my issue. I would be having 2-3 child components and few form controls on each.
Any inputs how to get this resolved?
We resolved this by calling markAsTouched on all form controls when submitting the form.
In your case you can add this.myForm.get('uname').markAsTouched(); to your onSubmit() method.

Checked radio button

I need to know if a radio button has been selected to hide / show other options.
<input #r4 type="radio" name="x">
<span>Group</span>
<div class="subOption" *ngIf="r4.checked"></div>
div.subOption must be displayed when it has been selected.
it's correct?¿
I would just create a new boolean variable in the component, that you set as true when radio button has been checked. With that boolean you then display the div. So for example:
isChecked: boolean = false; // our new variable
<input type="radio" name="x" (click)="isChecked = true">
<div *ngIf="isChecked">
<!-- Your code here -->
</div>
EDIT: As to multiple radiobuttons... as mentioned in a comment, radio buttons seems at this point be kind of buggy with Angular. I have found the easiest way to deal with radio buttons seems to be using a form. So wrap your radio buttons in a form, like so:
<form #radioForm="ngForm">
<div *ngFor="let val of values">
<input (change)="changeValue(radioForm.value)" type="radio" [value]="val.id" name="name" ngModel />{{val.name}}
</div>
</form>
whereas on radio button would look like the following in this example:
{
id: 1,
name: 'value1'
}
In the form there is a change event, from which we pick up the chosen radio button value.
(change)="changeValue(radioForm.value)"
Then I would just play with boolean and the value chosen:
changeValue(val) {
this.valueChosen = true; // shows div
this.chosenVal = val.name; // here we have the value chosen
}
A plunker to play with: here

Javascript onclick fails to send value while onfocus of text input

If a user clicks the save button as the next action after typing street data the onblur action intercepts the onclick and does not trigger the save. However, if you add some padding (30px) and click above the word save it works but below the word Save it does not work, the same as with no padding. I'm certain users will go right from typing text in the input field then click Save which will fail unless they first click somewhere else and then click Save. I’ve provide html and javascript example below. Is there a way using javascript to solve this issue?
<html>
<script>
function showstreet() {
var x = document.getElementById('street').value;
alert(x);
}
function focused() {
document.getElementById('title').style.display='';
document.getElementById('street').value='';
}
function blured() {
document.getElementById('title').style.display='none';
if (document.getElementById('street').value == '') {
document.getElementById('street').value='street';
}
}
</script>
<style>
.pad5 { padding:5px; }
.pad30 { padding:30px; }
</style>
<body>
<div id="title" class="pad5" style="display:none;">STREET NAME</div>
<div>
<input id="street" type="text" name="street" value="street" class="pad5"
onfocus="focused()" onblur="blured()">
</div>
<br>
<div>
<input type="button" value="Save" class="pad30" onclick="showstreet()">
</div>
</body>
</html>
I converted this to jsfiddle but I'm not doing something right (newbie) https://jsfiddle.net/eyo63mav/26/
use onMouseDown instead of onClick in your save button. Then onMouseDown will be fired before onBlur
below is working code
function showstreet() {
var x = document.getElementById('street').value;
alert(x);
}
function focused() {
document.getElementById('title').style.display = '';
document.getElementById('street').value = '';
}
function blured() {
document.getElementById('title').style.display = 'none';
if (document.getElementById('street').value == '') {
document.getElementById('street').value = 'street';
}
}
<div id="title" class="pad5" style="display:none;">STREET NAME</div>
<div>
<input id="street" type="text" value="street" class="pad5" onfocus="focused()" onblur="blured()">
</div>
<br>
<div>
<input type="button" value="Save" class="pad30" onclick="showstreet()">
</div>
Styling rarely makes a difference with events -- now, while that's a blanket statement and in lots of cases we find the styling of an inline element such as a link or a paragraph becoming problematic with inline events such as OnClick and OnFocus, in your case, adding thirty pixels to the size of a button is not your problem.
The problem with your code is that the variable you're assigning your #title's value to is local (it's inside the scope of showstreet(), of which can only be accessed by aforementioned function) -- nevermind that, it's never used again. You save a value to it, it alerts the user, and that's it -- it's never reassigned nor reused, so while it'll forever stay as the street name they entered, you'll never see it unless you apply it to something.
It took me a while to figure out what exactly you're trying to save, but I think I've managed it.
Here's the code I've created:
var streetValue = "Your street will appear here.";
function clickedField() {
// Init title
document.getElementById('title').innerHTML = streetValue;
// Reset field
document.getElementById('street').value = '';
}
function saveValue() {
// Reassign streetValue
streetValue = document.getElementById('street').value;
// Checking if value was left empty
if (streetValue === '') {
document.getElementById('title').innerHTML = "Error: No Street Entered!";
} else {
document.getElementById('title').innerHTML = streetValue;
}
}
(I'm not entirely sure what you had onblur for, but it should be very easy to insert back. If you need some help with that, comment on my reply, I'll be happy to.)
Now if we update the HTML with the approprate functions:
<div id="title" class="pad5" style="">STREET NAME</div>
<div>
<input id="street" type="text" name="street" value="street" class="pad5"
onfocus="clickedField()">
</div>
<br>
<div>
<input type="button" value="Save" class="pad30" onclick="saveValue()">
</div>

toggle checkbox values

I am trying to toggle the value of a checkbox using the following code:
<div class="control-group">
<label class="control-label checkbox" for="IsViewAsWebpage">
{{#if this.IsViewAsWebpage}}
<input type="hidden" id="IsViewAsWebpage" name="IsViewAsWebpage" value="true"/>
<input type="checkbox" class="enable-checkbox" checked />
{{else}}
<input type="hidden" id="IsViewAsWebpage" name="IsViewAsWebpage" value="false"/>
<input type="checkbox" class="enable-checkbox" />
{{/if}}
<span>View as Webpage</span>
</label>
</div>
'click .enable-checkbox': function (e) {
if (e.currentTarget.parentElement.htmlFor == "IsViewAsWebpage") {
this.$('#IsViewAsWebpage').is(':checked');
}
}
I know I am misssing something in the click function. I would basically want to toggle the checkbox value when the user clicks on it. Can someone point me to the right directions pls. Thank you.
When your checkbox gets clicked, it's going to toggle. That's the way checkboxes work.
If you want the hidden input to change value when the checkbox is toggled, I think what you want is this:
$(".enable-checkbox").change(function (e) {
if($(this).parent().attr("for") == "IsViewAsWebpage") {
var checked = $(this).is(":checked"); // Returns true/false.
$('#IsViewAsWebpage').attr("value", checked); // Sets true/false.
}
});
If you want to use handlebars.js to switch content when you click an check box, you will need to replace your content by calling handlebars each time you make a modification to your checkbox
$(".enable-checkbox").change(function (e) {
if($(this).parent().attr("for") == "IsViewAsWebpage") {
var checked = $(this).is(":checked");
IsViewAsWebpage = checked;
$("#yourcontainer").html(Handlebars.compile(yourtemplatesource));
}
}
then your IsViewAsWebpage variable should be global and your mustache condition should only be :
{{#if IsViewAsWebpage}}
But this is complicated for nothing... just use Aesthete solution, it will save you a lot of time.

Categories