I have a misunderstood how my FormGroup is displayed.
Here is my FormGroup (not with the fields I wanted to be displayed in this particular order).
I want it in order I added control in FormGroup. If I display each key on addControl, the order is:
type
body
text
label
tracking
additionalTracking
I am adding value in formControl with Addcontrol:
fg.addControl(key, FormCORA);
The console log of Object.keys(schema.properties) is in order I want:
["type", "body", "label", "tracking", "additionalTracking"]
The console log of schema is NOT in order I want:
How can I do to have label at first for example ?
The html block:
<ng-container *ngIf="tab.content.controls.blockList" [formGroup]="tab.content">
<div class="mt-3" [id]="tab.label + '-blocklist'">
<!--
Blocks settings
Here we construct the form for the blocks configuration
We use recursivity to loop over the fileds inside the root formGroup
in order to dynamically build forms according to the api exposed schema
-->
<nb-card status="danger" *ngIf="tab.content.controls.blockList.status === 'INVALID' && formSubmitted">
<nb-card-header class=h-auto>
<i class="fa fa-exclamation-triangle"></i> Please fill up every block <i class="fa fa-arrow-circle-down"></i>
</nb-card-header>
</nb-card>
<vccms-blocklist-edito
[blockOfType]="editoBlocksAvailable"
(blockSchema)="selectedSchema($event)"
[langCode]="tab.label"
></vccms-blocklist-edito>
</div>
</ng-container>
My selectedSchema function:
Thanks you in advance for you help ! I am taking any tips.
Related
I have a form for adding reviews, and added a custom star rating component. The 'rating' form value is required, so it won't let you submit without selecting a rating, but there is no feedback. So I would like it to highlight the review star section if it has no value on submit as if it were an angular form field.
<div class="stars-wrapper" title="Rating: {{ form.get('rating').value }} out of 5 stars">
<div class="star-button-wrapper" *ngFor="let i of [1,2,3,4,5]">
<button class="star-button" type="button" (click)="setRating(i)" attr.aria-label="Set Rating to {{i}} stars" >
<mat-icon class="full-star" *ngIf="form.get('rating').value >= i">star</mat-icon>
<mat-icon class="empty-star" *ngIf="form.get('rating').value < i">star_border</mat-icon>
</button>
</div>
</div>```
However you want to to "highlight" the rating if not chosen you can do it by checking the formcontrol validity. setRating function by my assumption sets the value for the formcontrol. So do you want to display a message if not chosen, well then just check that if
form.get('rating').value > 0
...and any other checks you might want to do, like has the form been submitted?
Want to attach a class to the field if it is not valid, well, then you could use something like:
[ngClass]="form.get('rating').value > 0 ? 'valid': 'not-valid'"
I see you are using angular material so you might want to tinker with the conditions if you are using the default angular material default settings for when the error messages are shown. But with checking the validity of the formfield you can apply styles etc to mimic "normal" material behavior.
I've been working with Angular for a bit and I know my way decently around Reactive forms and template-driven forms but I am having trouble with this problem, especially Angular's Reactive Form Array.
API response that I wish to POST/PUT to is formed like this, where the user is able to add a row and a dropdown will let them select the property, in this case, it's "p", "h1", "h2" and so on and the value they wished to have typed
"description": {
"body": [
{
"p": "test paragraph"
},
{
"h1": "test header"
}
],
I am in the process of converting our old JQuery and js nonsense to a Framework and it has been hard for me to wrap my mind on how to convert this process.
The user will click on Add item:
Then a row will be created via formArray I am assuming since I have tried this beforehand and it has worked but the dropdown to edit the property is giving me an issue.
And this is how it would look like when a few additional rows are created
I understand how formArrays work via this example:
Dynamically Add Rows Based on Condition Using Reactive Forms in Angular
so I will need a getter, as well as the function that creates the dynamic form and HTML but I am currently stuck at the moment.
I have eventually figured out most of the problem,
we will be using Angular's FormBuilder, FormArrays, and template interpolation.
Start by defining the form with form builder:
text-form.ts
this.textForm = this.fb.group({
lang: null,
description: this.fb.array([]),
});
You will be using Angular's getter in this case you want to grab description from the defined above form builder
get descriptionItems() {
return this.artifactTextForm.get('description') as FormArray;
}
Then you will create two functions one is used in a button to add rows and the other to delete the current row at the index in the Array
This will be a bit different from other examples as we will be using some ES6 tricks
addDescriptionItem() {
this.descriptionItems.push(this.fb.group({[this.selectedHeading]: ''}));
}
I defined this.selectedHeading as:
at the top of my component class
selectedHeading = 'h1';
Now create the button that will be created in the forloop on the template for each row
// deleteDescriptionItem removes the item at the index given in the formArray
deleteDescriptionItem(index: number) {
this.descriptionItems.removeAt(index);
}
Now comes one of the most important parts in the template to make sure everything looks nice. I am using Angular Material Design but it should work and perform the same natively.
<form [formGroup]="artifactTextForm">
<mat-dialog-content>
<mat-form-field appearance="fill">
<mat-label>Select Header Type</mat-label>
<mat-select [(value)]="selectedHeading">
<mat-option value="h1">Header 1</mat-option>
<mat-option value="h2">Header 2</mat-option>
<mat-option value="h3">Header 3</mat-option>
<mat-option value="h4">Header 4</mat-option>
<mat-option value="p">Paragraph</mat-option>
</mat-select>
</mat-form-field>
<!-- Description form array -->
<div formArrayName="description">
<div *ngFor="let item of descriptionItems.controls; let i = index" [formGroupName]="i">
<input
[formControlName]="this.selectedHeading"
placeholder="Enter Description Data"
[maxLength]="formMaxLength">
<button
mat-raised-button
color="primary"
type="button"
(click)="deleteDescriptionItem(i)">Delete Item</button>
</div>
<button mat-raised-button color="primary" type="button" (click)="addDescriptionItem()">Add Description</button>
</div>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-raised-button type="submit" (click)="onSubmit()">Save</button>
<button mat-raised-button type="reset" (click)="dialogRef.close()">Cancel</button>
</mat-dialog-actions>
</form>
Make sure the add button is outside of the div with the forloop and it should something like so.
side note:
Flame shot on ubuntu is amazing.
I have a problem with the validation of the autocomplete with Angular 9 when it is empty only on touch but it does not work and touch is always false.
the module link that i used :
https://www.npmjs.com/package/angular-ng-autocomplete
here is my code :
<div class="ng-autocomplete">
<ng-autocomplete #autoComplete
[data]="countries"
[searchKeyword]="keyword"
placeHolder="Enter the Country Name"
(selected)='selectEvent($event)'
(inputChanged)='onChangeSearch($event)'
(inputFocused)='onFocused($event)'
historyIdentifier="countries"
[itemTemplate]="itemTemplate"
[formControl] = "country"
[notFoundTemplate]="notFoundTemplate">
</ng-autocomplete>
<ng-template #itemTemplate let-item>
<a [innerHTML]="item.name"></a>
</ng-template>
<ng-template #notFoundTemplate let-notFound>
<div [innerHTML]="notFound"></div>
</ng-template><br><br><br><br>
<span *ngIf="country.touched && !country.value"> select something </span>
</div>
I faced the same problem. The problem is formControlName is tagged on 'ng-autocomplete' instead on 'input' html tag as we generally use it.
I did a workaround using the (inputFocused) event from ng-autocomplete and markAsRead() from Reactive forms to mark the form control as readed after the user put the focus on the autocomplete input.
HTML code:
<ng-autocomplete
[data]="data"
[searchKeyword]="keyword"
(selected)='selectEvent($event)'
(inputChanged)='onChangeSearch($event)'
(inputFocused)='onFocused($event)'
[itemTemplate]="itemTemplate"
[notFoundTemplate]="notFoundTemplate">
</ng-autocomplete>
(inputFocused) will trigger markAsRead() method in ts, so there I will change the touched property of the form control.
Typescript code:
markAsRead() {
this.form.get('from_control_name').markAsTouched();
}
This is how I changed touched input property.
Hope it helps anybody :).
Docs: ng-autocomplete y reactive forms.
I am trying to add a role to the required message generated from data-parsley-required-message, but do not have an idea how to do that.
This element is auto-generated when validating the required field with data-parsley:
<ul class="parsley-errors-list filled" id="parsley-id-5485">
<li class="parsley-required">This value is required.</li></ul>
I want to add attribute role="alert" as:
<li class="parsley-required" role="alert"> This value is required.</li>
Need to add a role to the data-parsley-required-message. How?
You simply need to change the default values of errorTemplate or errorWrapper. Looks like they are not documented though, but they should. Care to provide a PR?
I've started Angular2 lately and I am facing a problem. I want to create a form to generate some multiple choices questions and this is my problem :
I have a FormComponent which display the layout of my form.
The AnswerComponent can be added to the FormComponent to provide multiple choices about the question.
I have been using the DynamicComponentLoader to programatically add thoses AnswerComponent inside of my FormComponent.
The thing is the submit button must belong to the FormComponent and I do not know how to alert my AnswerComponent to send their data to the FormComponent so it cans gather all the data and create my question.
If anyone has an idea that would be great !
Thank you !
Let's take a sample. I have a form that manages company details:
<form [ngFormModel]="companyForm">
<field label="Name" [state]="companyForm.controls.name">
<input [ngFormControl]="companyForm.controls.name" [(ngModel)]="company.name"/> {{name.valid}}
</field>
<field label="Tags">
<tags [(labels)]="company.labels"></tags>
</field>
<button type="submit" [disabled]="!companyForm.valid">Submit</button>
</form>
As you can see, I use two sub components:
The field one that aims to build the layout for a field block using Bootstrap3. It accepts a variable area to provide the form element (input, select, textarea). This component also leverages the associated control to display validation errors if any.
The tags one that manages the tags attribute that is a list of string. It allows to display, add and remove tags.
You can see that every form element leverages two way binding. This means that each form element is associated to a property of an object of the component. Here it's the company one that is a property of the component.
This means that when you want to submit the form, you can use this company object to build the payload of the corresponding HTTP request for example.
Let's deal a bit more with the associated with the company object. For inputs, it's obvious using the ngModel directive with this syntax: [(ngModel)]. With the tags sub component, it's perhaps not so obvious.
In fact you need define inputs and outputs to manage the labels with two ways bindings:
#Input labels:string[]
#Output labelsChanged: EventEmitter
When labels are updated, you need to call the emit method of labelsChanged.
Here is the complete code for the TagsComponent component:
#Component({
selector: 'tags',
template: `
<div *ngIf="labels">
<span *ngFor="#label of labels" style="font-size:14px"
class="label label-default" (click)="removeLabel(label)">
{{label}} <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
</span>
<span> | </span>
<span style="display:inline-block;">
<input [(ngModel)]="labelToAdd" style="width: 50px; font-size: 14px;" class="custom"/>
<em class="glyphicon glyphicon-ok" aria-hidden="true" (click)="addLabel(labelToAdd)"></em>
</span>
</div>
`
})
export class TagsComponent implements OnInit {
#Input()
labels:string[];
#Output()
labelsChange: EventEmitter;
constructor(private elementRef:ElementRef) {
this.labelsChange = new EventEmitter();
}
removeLabel(label:string) {
var index = this.labels.indexOf(label, 0);
if (index != undefined) {
this.labels.splice(index, 1);
this.labelsChange.emit(this.labels);
}
}
addLabel(label:string) {
this.labels.push(this.labelToAdd);
this.labelsChange.emit(this.labels);
this.labelToAdd = '';
}
}
Hope it helps you,
Thierry