Angular 2+: Input decorator not reflecting checkbox - html

I have a component
timeBoxSelector
HTML:
<input type="checkbox" [(ngModel)]="selected">
TS:
#Component({
...
})
export class TimeboxComponent implements OnInit {
#Input() selected: boolean;
constructor() {}
ngOnInit() {
console.log('Selected: ', this.selected);
}
}
Now, when I create
<app-timebox selected="false"><app-timebox/>
<app-timebox selected="true"><app-timebox/>
In both cases, the checkbox initially appears as selected. Why is this the case, and how can I fix it?

In both cases, you are binding non empty strings, which are truthy values. Use the brackets notation to tell Angular that the bound value is to be evaluated as a Javascript expression:
<app-timebox [selected]="false"><app-timebox/>
<app-timebox [selected]="true"><app-timebox/>

Related

How to pass Angular directive by reference?

In an existing component template I have this (simplified) element:
<input type="button" #refreshPrice />
This is picked up (I don't know the correct term) by this property so we can subscribe to it's click event and call a function when the input element is clicked.
I want to replace this input element with a component I've developed, which would make it look (simplified) like this:
<spinner-button #refreshPrice></spinner-button>
This child component has this as its (simplified) template:
<button>
<mat-spinner></mat-spinner>
</button>
So now the button element, in the child component template, needs to have the #refreshPrice hash attribute (?) attached.
To do this, perhaps the spinner-button element should take the name of the hash attribute as an attribute value. Here is the complete spinner component class file:
import { Component, Input, OnInit } from "#angular/core";
#Component({
selector: "spinner-button",
templateUrl: "./spinner-button.component.html",
styleUrls: ["./spinner-button.component.css"]
})
export class SpinnerButtonComponent implements OnInit {
constructor() {}
#Input() targetElement: string;
ngOnInit() {
}
}
In theory, the targetElement property can then be attached to the button element as a hash attribute - but how is this done?
the #Input() attribute here allows you to bind a value to a variable on your component, if you want to have the parent do something based on your components data, you might want to use #Output() and emit a custom event. If the requirement is just listen to a click event then adding a (click)=functionToBeCalled() should help your cause here.
You can refer to the official docs as well:
https://angular.io/guide/inputs-outputs

how to disable dropdown selection?

I have a dropdown with 2 options:
I want the user to be able to see both options (Cips, Projects); however, I do not want to allow the user to choose a different item.
How do we allow the user to click on the dropdown and see all the options, but disable choosing any other option?
This dropdown is defined like so:
<p-dropdown [(ngModel)]="editRagColumns.BaseObjectType"
name="RagBasicType"
type="text"
class="form-control"
id="RagBasicType"
[options]="baseObjectTypes"
optionLabel="BaseObjectTypeName"
[style]="{'width':'100%', 'border-color':'transparent'}"
>
</p-dropdown>
As per the documentation:
https://www.primefaces.org/primeng/showcase/#/dropdown
You can use this optionDisabled
You have to add the following property to your "p-dropdown" component.
optionDisabled="BaseObjectTypeName"
This is my ts and html file that I have done to confirm that it works.
ts file
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-question1',
templateUrl: './question1.component.html',
styleUrls: ['./question1.component.sass']
})
export class Question1Component implements OnInit {
BaseObjectTypeName: string = "BaseObjectTypeName";
baseObjectTypes: any[] = [
{ BaseObjectTypeName: "tab1" },
{ BaseObjectTypeName: "tab2" },
];
constructor() { }
ngOnInit(): void {
}
}
html file
<p-dropdown
name="RagBasicType"
type="text"
class="form-control"
id="RagBasicType"
[options]="baseObjectTypes"
optionLabel="BaseObjectTypeName"
[style]="{'width':'100%', 'border-color':'transparent'}"
optionDisabled="BaseObjectTypeName"
>
</p-dropdown>
I hope I've helped you.
If you are using primeng 11.0.0 and onwards, then optionDisabled property is the way to go, as others have already mentioned.
In case if you are using previous versions, then you have to ensure following points:
The value passed to options must be of SelectItem[ ] type or at least have label, value and disabled properties from SelectItem type.
label would be the dropdown option text
value can be of any type i.e an object or simply a string
disabled would be of boolean type, setting it to true will disable the dropdown option
p-dropdown optionLabel should not be used
In your case baseObjectTypes should be something as below:
baseObjectTypes = [
{ label: 'Projects', value: 'Projects', disabled: true }, // <--- value can be object too
{ label: 'Cips', value: 'Cips', disabled: true
]

angular : passing the input value to the component

I want to affect the value of this input to a variable 'numToAdd' which is in my component and then add 1 to the variable 'numToAdd', but I can't pass the value of the html input to my component variable 'numToAdd'. How to bind my html input to a variable in the component ?
my html code
<input type="number" id=num class=num>
my component
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-afficher-projets',
templateUrl: './afficher-projets.component.html',
styleUrls: ['./afficher-projets.component.css']
})
export class AfficherProjetsComponent implements OnInit {
ngOnInit(): void {}
numToAdd: number;
constructor() { }
add: void {
this.numToAdd++;
}
}
Two-way binding a primary functionality of angular. Use the ngModel directive to bind input to variables.
<input type="number" id="num" class="num" [(ngModel)]="numToAdd">
The above code binds the variable numToAdd with the input. Find more details here

Angular interpolate inside a component like mat-checkbox

So I want to have a mat-checkbox component with a HTML string inside the label.
I tried the following:
<mat-checkbox class="check">
{{ someHtml }}
</mat-checkbox>
But it prints the HTML string as a string and doesn't render it.
Using the following doesn't work either:
<mat-checkbox class="check" [innerHtml]="someHtml">
</mat-checkbox>
This just replaces the whole content, including the checkbox that gets generated at runtime. Is there any way to inject the html into the label?
You could use Angular Directives
The idea here is to fetch the element from the HTML, then append some raw HTML dynamically.
Supose this scenario
app.component.html
<mat-checkbox class="check" [appendHtml]="innerHtml"></mat-checkbox>
app.component.ts
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
innerHtml = `<div style="border: 1px solid red;"> Text inside </div>`;
constructor() {}
}
As you can see, I added a appendHtml attribute to the mat-checkbox element. This is a custom directive that expects a string as "raw" HTML.
append-html.directive.ts
#Directive({
selector: '[appendHtml]'
})
export class AppendHtmlDirective implements AfterViewInit {
#Input('appendHtml') html: string
constructor(private element: ElementRef) {
}
ngAfterViewInit() {
const d = this.element.nativeElement.querySelector('label');
d.insertAdjacentHTML('beforeend', this.html);
}
}
The AppendHtmlDirective expects an html property of type string and implements AfterViewInit interface (from Angular) to fetch the element once it is rendered. By injection, Angular provides us the element which is being applied; so, the ElementRef from the constructor is our MatCheckbox element, in that case.
We can use the insertAdjacentHTML function to append childs to the element. I just fetched the label element from the MatCheckbox to fit inside of it. In every case, you should see where to append the HTML.
I mean, label here works, bc MatCheckbox has a tag whitin matching that. If you want to reuse this Directive for other elements, you should be passing the literal to find inside.
i.e.:
append-hmtl.directive.ts
// ...
#Input() innerSelector: string
// ...
ngAfterViewInit() {
const d = this.element.nativeElement.querySelector(this.innerSelector);
d.insertAdjacentHTML('beforeend', this.html);
}
app.component.hmtl
<mat-checkbox class="check" [appendHtml]="innerHtml" innerSelector="label"></mat-checkbox>
Moreover, you can pass as many inputs as you need to customize the styling or behavior of your directive.
Cheers
I think you should just wrap everything in a div and put it on the outside.
<div>
<mat-checkbox class="check"> </mat-checkbox>
{{ someHtml }}
</div>

Angular2 dynamically generating Reactive forms

I have a concept question and would like some advice.
So I have a component, myFormArray, which is a reactive form. It takes in an input array, and creates a number of FormControls accordingly.
#Component({
selector: 'myFormArray',
templateUrl: 'formarray.component.html'
})
export class FormArrayComponent{
#Input() classFields: any[];
userForm = new FormGroup();
ngOnInit(){
// psuedocode here, but I know how to implement
for (# of entries in classFields)
userForm.FormArray.push(new FormControl());
}
Now, in my parent html, I will be dynamically generating multiple myFormArrays. If that is confusing, assume I'm doing this:
<myFormArray [classFields] = "element.subArray"/>
<myFormArray [classFields] = "element2.subArray"/>
<button (click) = "save()"> //I don't know how to implement this!
And at the very end of the page, I want a save button, that can somehow grab all the values the user inputs in to all the forms, and pushes all this data to an array in a Service component. I'm not sure exactly how to do this part. Note that I don't want individual submit buttons for each dynamically generated form component.
How would I implement this functionality? Thanks!
Your start is good, but you have to write your source code differently.
Instead of this example app.components.ts is main component and my-array.component.ts is child component.
Our test data
classFields1: any[] = ['firstname', 'lastname', 'email', 'password'];
classFields2: any[] = ['country', 'city', 'street', 'zipcode'];
Step 1. Use FormBuilder for form creation (app.component.ts)
You must import FormBuilder and FormGroup from #angular/forms like this:
import { FormBuilder, FormGroup } from '#angular/forms';
and then define in constructor:
constructor(private formBuilder: FormBuilder) { }
Step 2. Define new empty FormGrooup
Now you can define new empty FormGroup in ngOnInit like this:
ngOnInit() {
this.myForm = this.formBuilder.group({});
}
Step 3. Create FormControls dynamically (app.component.ts)
Now you can start with dynamically creation of your FormControls by iteration of classFields. For this I would recommend to create own function. This function gets two parameter: arrayName and classFields. With arrayName we can set custom name of our FormArray-control. classFields-Array we will use for iteration. We create constant variable for empty FormArray, which we called arrayControls. After this we iterate over classFields and create for each element FormControl, which we called control, and push this control into arrayControls. At the end of this function we add our arrayControls to our Form with custom name by using arrayName. Here is an example:
createDynamicArrayControls(arrayName: string, classFields: any[]) {
const defaultValue = null;
const arrayControls: FormArray = this.formBuilder.array([]);
classFields.forEach(classField => {
const control = this.formBuilder.control(defaultValue, Validators.required);
arrayControls.push(control);
})
this.myForm.addControl(arrayName, arrayControls);
}
Import FormControl and FormArray from #angular/forms. Your import line should be like this:
import { FormBuilder, FormGroup, FormArray, FormControl } from '#angular/forms';
Now call createDynamicFormControls-Function in ngOnInit.
Step 4. HTML Template for this dynamic Form (app.component.html)
For this example I create following template:
<h1>My Form</h1>
<form [formGroup]="myForm">
<div formGroupName="test1">
<app-my-array [classFields]="classFields1" [arrayFormName]="myForm.controls.test1"></app-my-array>
</div>
<div formGroupName="test2">
<app-my-array [classFields]="classFields2" [arrayFormName]="myForm.controls.test2"></app-my-array>
</div>
<button type="button" (click)="saveForm()">Submit</button>
</form>
Here we have new div element with formGroupName. This group name is our arrayName in our form. We give our form arrays via #Input to my-array.component.
Step 5. MyArrayComponent
Now this component is very simnple:
import { Component, OnInit, Input } from '#angular/core';
import { FormGroup } from '#angular/forms';
#Component({
selector: 'app-my-array',
templateUrl: './my-array.component.html',
styleUrls: ['./my-array.component.css']
})
export class MyArrayComponent implements OnInit {
#Input() classFields: any[];
#Input() arrayFormName: FormGroup;
constructor() { }
ngOnInit() { }
}
Here we have only two #Input varibales. (I know, this variable can have a better names :-) ).
Step 6. HTML for MyArrayComponent
<div [formGroup]="arrayFormName">
<div *ngFor="let class of arrayFormName.controls; let index = index;">
<label [for]="classFields[index]">{{ classFields[index] }}</label>
<input type="text" [id]="classFields[index]" [formControlName]="index" />
</div>
</div>
<br>
And here is working example on Stackblitz: https://stackblitz.com/edit/angular-wawsja
If you have some question ask me in comments or read the Angular documentation abour Reactive Forms here.