I'm making a pop up in angular with primeng. The content of the pop up depends on two flags controlled by radio buttons. If one is true, a particular HTML has to be rendered and if other is true, other part has to be rendered. But if one is true, other part in not getting cleared form the pop up. My code is something like this:
In .ts file:
part1: boolean = false;
part2: boolean = false;
makeP1True() {
this.part1=true;
this.part2=false;
}
makeP2True() {
this.part2=true;
this.part1=false;
}
in HTML file:
<p-radioButton name="groupname" value="Part1" (onClick)="makeP1True()"></p-radioButton>
<p-radioButton name="groupname" value="Part2" (onClick)="makeP2True()"></p-radioButton>
<div *ngIf="part1">
Show Part 1
</div>
<div *ngIf="part2">
Show Part 2
</div>
You don't have part2 property as in you example
part1: boolean = false;
part1: boolean = false; // just name it to part2
template
<p-radioButton name="groupname" [value]="Part1" label="Part1" (onClick)="makeP1True()"></p-radioButton>
<p-radioButton name="groupname" [value]="Part2" label="Part2" (onClick)="makeP2True()"></p-radioButton>
<div *ngIf="part1">
Show Part 1
</div>
<div *ngIf="part2">
Show Part 2
</div>
component
export class AppComponent {
part1: boolean = false;
part2: boolean = false;
makeP1True() {
this.part1 = true;
this.part2 = false;
}
makeP2True() {
this.part2 = true;
this.part1 = false;
}
}
stackblitz example
As a general note you can solve it with single propert and relay on
p-radioButton to toggle the value
component
export class AppComponent {
part1: boolean ;
}
template
<p-radioButton name="groupname" label="part1" [value]="true" [(ngModel)]="part1"></p-radioButton>
<p-radioButton name="groupname" label="part2" [value]="false" [(ngModel)]="part1"></p-radioButton>
<div *ngIf="part1 === true">
Show Part 1
</div>
<div *ngIf="part1 === false">
Show Part 2
</div>
stackblitz example with single variable
By doing *ngIf="part1" you are actually just checking whether 'part1' is truthy, which means whether it exists or not. However, to see which item is selected, you would need to set a state variable such as selected.
I would suggest you simplify this by using a single function and ngSwitch. Here is a tutorial: https://www.tektutorialshub.com/angular-2-ngswitch-directive/
With the [ngSwitch] directive you can specify an expression to be evaluated. With *ngSwitchCase you can specify the matcher. The syntax would be the closest to your code.
It would look like this:
HTML
<p-radioButton name="groupname"
value="Part1"
(onClick)="setPart('part1')">
</p-radioButton>
<p-radioButton name="groupname"
value="Part2"
(onClick)="setPart('part2')">
</p-radioButton>
<ng-container [ngSwitch]="selected">
<div *ngSwitchCase="'part1'">
Show Part 1
</div>
<div *ngSwitchCase="'part2'">
Show Part 2
</div>
</ng-container>
TS
public selected: string;
public setPart(id: string): void {
this.selected = id;
}
In this case both divs do something like this: *ngIf = "selected === 'partX'.
BTW: <ng-container> lets you use directives such as *ngIf,*ngSwitch and so on without creating a new DOM elements. Great for building conditionals.
You can read up on all of this in the official docs
Related
I have a simple form with two input fields. I would like to take the text from one field and snip the first part and replace it with some other text and populate it into the second field dynamically. I was able to achieve the getting part from the field with ease but I am not sure how to snip the value on keyup. The following is the code:
Component HTML
<div class="labels">Real Name</div>
<div class="lbl">
<input #realName type="text" [(ngModel)]="actualName" (keyup)="trigger()">
</div>
<div class="labels">Cyber Name</div>
<div class="lbl">
<input #cybName type="text"[(ngModel)]="cyberName">
</div>
Component TS
#ViewChild('realName') realName: ElementRef;
#ViewChild('cybName') cybName: ElementRef;
trigger() {
this.cybName.nativeElement.value = this.realName.nativeElement.value;
}
On every keyup, I am setting the value of the cybName with the realName. But, I would like to snip the first 4 characters of the realName and replace it with droid and the remaining characters of the realName i.e., if the typed in real Name was "Alexys", I would like to make it "droidys".
I am sure I shouldn't be doing it with keyup but I am not sure what else to use for this situation. Could anyone help me with it. Thank you.
An easy solution that I think will solve your issue:
Changes in html:
<input #realName type="text" [ngModel]="actualName" (ngModelChange)="trigger($event)">
Changes in ts:
trigger(newActualName) {
this.actualName = newActualName;
this.cyberName = `Droid${event.substring(4)}`;
}
When you update the model (actualName and cyberName) it will also update the input with the new values.
Also: Angular warns against using the ElementRef and accessing the DOM directly. See here for more info
you can use the splice to cut up your input. I am also assuming the following use cases
"AAAAAA" -> "droidAA"
"AAAA" -> "droid"
"AA" -> "AA"
const strLength = this.realName.nativeElement.value && this.realName.nativeElement.value.toString().length;
if (strLength >= 4) {
this.cybName.nativeElement.value = 'droid' + this.realName.nativeElement.value.toString().splice(4, strLength);
} else {
this.cybName.nativeElement.value = this.realName.nativeElement.value;
}
Also, you shouldn't have to use nativeElement. You should be able to use the variables themselves.
if (this.actualName && this.acutualName.length >= 4) {
this.cyberName = 'droid' + this.actualName.splice(4, this.acutualName.length);
} else {
this.cyberName = this.actualName;
}
I think you just want to append the substr:
let cyberName = 'droid';
let realName = 'Alexys';
let cyberDroidName = `${cyberName}${realName.substring(4)}`;
console.log(cyberDroidName);
Try the following:
<div class="labels">Real Name</div>
<div class="lbl">
<input #realName type="text" [(ngModel)]="actualName">
</div>
<div class="labels">Cyber Name</div>
<div class="lbl">
<input #cybName type="text"[ngModel]="'droid' + actualName.substring(4)">
</div>
As you can see by all the answers there are many ways to skin this Cat. If it were me, I would use a pipe to achieve this. As shown in this working example.
You can create a pipe like this:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({name: 'robotNamePipe'})
export class RobotNamePipe implements PipeTransform {
transform(value: number, exponent: string): string {
// Run your logic in here
return `${value} - Anyhting you want!`;
}
}
... and then in your HTML use it like this:
<div class="labels">Real Name</div>
<div class="lbl">
<input #realName type="text" [(ngModel)]="actualName">
</div>
<div class="labels">Cyber Name</div>
<p>Robot name: {{realName.value | robotNamePipe}}</p>
Because really, you probably don't want people to be able to edit the generated droid name anyway - do you?
I am making angular application with angular form.
Here i have given a form with input fields first name and last name which will always showing..
After that i am having children which will be displayed upon clicking the add button and the children will get removed on click remove button.
As of now everything works fine.
Here i am making patching of data to the inputs on click option from select box.. The neccessary inputs gets patched..
HTML:
<div>
<form (ngSubmit)="onSubmit()" [formGroup]="form">
<div *ngFor="let question of questions" class="form-row">
<ng-container *ngIf="question.children">
<div [formArrayName]="question.key">
<div *ngFor="let item of form.get(question.key).controls; let i=index" [formGroupName]="i">
<div *ngFor="let item of question.children">
<app-question [question]="item" [form]="form.get(question.key).at(i)"></app-question>
</div>
</div>
<select multiple (change)="changeEvent($event)">
<option *ngFor="let opt of persons" [value]="opt.key">{{opt.value}}</option>
</select>
</div>
</ng-container>
<ng-container *ngIf="!question.children">
<app-question [question]="question" [form]="form"></app-question>
</ng-container>
</div>
<div class="form-row">
<!-- <button type="submit" [disabled]="!form.valid">Save</button> -->
</div>
</form> <br>
<!-- Need to have add and remove button.. <br><br> -->
<button (click)="addControls('myArray')"> Add </button>
<button (click)="removeControls('myArray')"> Remove </button><br/><br/>
<pre>
{{form?.value|json}}
</pre>
</div>
TS:
changeEvent(e) {
if (e.target.value == 1) {
let personOneChild = [
{ property_name : "Property one" },
{ property_name : "Property two" },
]
for (let i = 0; i < personOneChild.length; i++) {
this.addControls('myArray')
}
this.form.patchValue({
'myArray': personOneChild
});
}
if (e.target.value == 2) {
let personTwoChild = [
{ property_name : "Property three" },
{ property_name : "Property four" },
{ property_name : "Property five" },
]
for (let i = 0; i < personTwoChild.length; i++) {
this.addControls('myArray')
}
this.form.patchValue({
'myArray': personTwoChild
});
}
}
addControls(control: string) {
let question: any = this.questions.find(q => q.key == control);
let children = question ? question.children : null;
if (children)
(this.form.get(control) as FormArray).push(this.qcs.toFormGroup(children))
}
removeControls(control: string) {
let array = this.form.get(control) as FormArray;
array.removeAt(array.length - 1);
}
Clear working stackblitz: https://stackblitz.com/edit/angular-x4a5b6-fnclvf
You can work around in the above link that if you select the person one option then the value named property one and property two gets binded to the inputs and in select box the property one is highlighted as selected..
The thing i am in need is actually from here,
I am having a remove button, you can see in demo.. If i click the remove button, one at last will be got removed and again click the last gets removed..
Here i am having two property one and two, if i remove both the inputs with remove button, the the highlighted value person one in select box needs to get not highlighted.
This is actually my requirement.. If i remove either one property then it should be still in highlighted state.. Whereas completely removing the both properties it should not be highlighted..
Hope you got my point of explanation.. If any needed i am ready to provide.
Note: I use ng-select for it as i am unable implement that library, i am making it with html 5 select box.. In ng-select library it will be like adding and removing the option.. Any solution with ng-select library also appreciable..
Kindly help me to achieve the result please..
Real time i am having in application like this:
Selected three templates and each has one property with one,two,three respectively:
If choose a dropdown then the property values for the respective will get added as children.
Here you can see i have deleted the property name three for which the parent is template three and the template three still shows in select box even though i removed its children
Firstly, get a reference to the select, like so:
HTML:
<select multiple (change)="changeEvent($event)" #mySelect>
<option *ngFor="let opt of persons" [value]="opt.key">{{opt.value}}</option>
</select>
TS:
import { ViewChild } from '#angular/core';
// ...
#ViewChild('mySelect') select;
Then, in your remove function, check if all elements have been removed, and if they have, set the value of the select to null
if (array.length === 0) {
this.select.nativeElement.value = null;
}
Here is a fork of the StackBlitz
I have the below code:
<li *ngFor="let item of Array let i = index">
<span>
<label (dblclick)="editTag($event,i)">
{{item.tag}}
</label>
<input type="text" #tagInput />
</span>
</li>
The code is in a for loop. When I click on a label, all labels should be hidden and the input should be visible. Currently, when I click on each label, the other remain open. How do I hide the other span when clicking on any item?
I have below code in .ts
#ViewChild('tagInput') tagNameTextInput: ElementRef;
editTag(event: any,index: any) {
//console.info(event);
this.tagNameTextInput.nativeElement.hidden = true;
this.tagNameTextInput.nativeElement.previousElementSibling.hidden = false;
let initialValue = event.target.childNodes[0].nodeValue.trim();
event.target.hidden = true;
event.target.nextElementSibling.hidden = false;
event.target.nextElementSibling.value = initialValue;
console.log(index);
// this.checkListNameHidden = true;
// this.checkListNameTextInput.nativeElement.value = initialValue;
// this.checkListNameTextInput.nativeElement.focus();
event.stopPropagation();
}
How to solve this?
You have multiple children, So you need to use #ViewChildren instead of #ViewChild.
Also in your ngFor loop you do not have unique template reference #tagInput. Use QueryList with ElementRef.
Try : #ViewChildren('tagInput') tagNameTextInput: QueryList<ElementRef>;
instead of
#ViewChild('tagInput') tagNameTextInput: ElementRef;.
Import QueryList from #angular/core.
Like this import { Component, QueryList } from '#angular/core';
the best aproach is add a new property to "item", (e.g. called "editing") so
<li *ngFor="let item of Array let i = index">
<span>
<label *ngIf="!item.editing" (dblclick)="item.editing=true;">
{{item.tag}}
</label>
<input *ngIf="item.editing" [(ngModel)]="item.tag" type="text" (blur)="item.editing=false" />
</span>
</li>
See several things:
1.-in a click of label, the variable becomes true, so the inpĆ¹t is showed
2.-in blur of item, the variable becomes false, so the label is showed
3.-Use [(ngModel)] to relation between the input and the value
I am wanting to change the colour of my text with a service and subscription, however I cannot seem to get it to work properly?
I am using [ngClass] to dynamically set characters that are true for 'isActive', but I don't know why I cannot get this to work?
At present, only the first letter is being changed.
Also... I am hoping to change the colour of the highlighted letters changed by the [ngClass].
Stackblitz
html
<div class="container">
<div class="keys" *ngFor="let item of dataKeys">
<div #text class="chars" *ngFor="let char of data[item]" [ngClass]="{'active': char.isActive}">
{{char.name}}
</div>
</div>
</div>
component
#ViewChild('text') private text: ElementRef;
constructor(private service: SettingsService) {
}
ngOnInit() {
this.subscribeToColour();
}
subscribeToColour() {
this.service.getColour.subscribe(res => {
if (this.text) {
this.text.nativeElement.style.color = res;
}
});
}
get dataKeys() {
return Object.keys(this.data);
}
service
export class SettingsService {
default = 'green'
colour = new BehaviorSubject<any>(this.default);
setColour(colour) {
this.colour.next(colour);
}
get getColour(): Observable<any> {
return this.colour;
}
}
As Peter said the view child reference you captured to "text" is only a reference to the first of the divs that you repeated.
As an alternative, you could consider adding the color to the data object:
Eg:
{
"name": "a",
"isActive": true,
"color": "red",
}
And binding to this in your html file - you shouldn't really be manipulating the nativeElement property yourself
You would then update the property in your data object in your service subscriber as appropriate
The #text is only pointing at the div for a. Therefore, this.text.nativeElement.style.color = res; only updates the color for a.
I modified your code a little bit:
my-comp.component.html
<div class="container">
<div class="keys" *ngFor="let item of dataKeys">
<div class="chars" *ngFor="let char of data[item]" [style.color]="char.isActive ? color : 'white'">
{{char.name}}
</div>
</div>
</div>
my-comp.component.ts
color: string = 'white';
subscribeToColour() {
this.service.getColour.subscribe(res => {
this.color = res;
});
}
Note that I got rid of #text in your html file as well as the conditional block in your ts file.
You don't seem to be setting the .isActive variable of any character in your code. That #text is not working how you want it to.
I think you may be overcomplicating this. You can simply change colors by setting them in your service without subscribing to a value.
Try something like:
<div class="container">
<div class="keys" *ngFor="let item of dataKeys">
<div class="chars" *ngFor="let char of data[item]" [ngClass]="{'active': getColour(char.isActive)}">
{{char.name}}
</div>
</div>
</div>
export class SettingsService {
getColour(boolean): string {
if (boolean == true) {
return 'green'; // whatever colour you want.
} else {
return 'red' // whatever you want
}
}
I have a code:
document.getElementById('loginInput').value = '123';
But while compiling the code I receive following error:
Property value does not exist on type HTMLElement.
I have declared a var: value: string;.
How can I avoid this error?
Thank you.
if you want to set value than you can do the same in some function on click or on some event fire.
also you can get value using ViewChild using local variable like this
<input type='text' id='loginInput' #abc/>
and get value like this
this.abc.nativeElement.value
here is working example
Update
okay got it , you have to use ngAfterViewInit method of angualr2 for the same like this
ngAfterViewInit(){
document.getElementById('loginInput').value = '123344565';
}
ngAfterViewInit will not throw any error because it will render after template loading
(<HTMLInputElement>document.getElementById('loginInput')).value = '123';
Angular cannot take HTML elements directly thereby you need to specify the element type by binding the above generic to it.
UPDATE::
This can also be done using ViewChild with #localvariable as shown here, as mentioned in here
<textarea #someVar id="tasknote"
name="tasknote"
[(ngModel)]="taskNote"
placeholder="{{ notePlaceholder }}"
style="background-color: pink"
(blur)="updateNote() ; noteEditMode = false " (click)="noteEditMode = false"> {{ todo.note }}
</textarea>
import {ElementRef,Renderer2} from '#angular/core';
#ViewChild('someVar') el:ElementRef;
constructor(private rd: Renderer2) {}
ngAfterViewInit() {
console.log(this.rd);
this.el.nativeElement.focus(); //<<<=====same as oldest way
}
A different approach, i.e: You could just do it 'the Angular way' and use ngModel and skip document.getElementById('loginInput').value = '123'; altogether. Instead:
<input type="text" [(ngModel)]="username"/>
<input type="text" [(ngModel)]="password"/>
and in your component you give these values:
username: 'whatever'
password: 'whatever'
this will preset the username and password upon navigating to page.
Complate Angular Way ( Set/Get value by Id ):
// In Html tag
<button (click) ="setValue()">Set Value</button>
<input type="text" #userNameId />
// In component .ts File
export class testUserClass {
#ViewChild('userNameId') userNameId: ElementRef;
ngAfterViewInit(){
console.log(this.userNameId.nativeElement.value );
}
setValue(){
this.userNameId.nativeElement.value = "Sample user Name";
}
}