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
Related
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.
I am attached the custom select component from visual design. I need to reproduce it. up to my level I am not able to customize the select component to mach this mock.
But as a option, I can able to create a drop down using ul to match the same. But In my angular app, I am using Reactiveform, so, where I could not able to add the validation errors.
so what is the correct way to handle this kind of scenario?
Mytry
here is my mock up:
Using ReactiveForms you can set Validators to any of your Form Controls
public readonly form: FormGroup = this.fb.group({
SelectedValue: new FormControl("",Validators.required)
});
Then in your template you can use any options you want defined in your component or otherwise, and set some flags and helpful messages when the form control is not valid.
Validators.required will make sure that the field is required for the form to be valid
<div [formGroup]="form">
<select formControlName="SelectedValue">
<option *ngFor="let opt of options" [value]="opt.key">{{opt.value}}</option>
</select>
<h3 *ngIf="!form.controls.SelectedValue.valid">Selected Value field is required</h3>
<button disabled="!form.controls.SelectedValue.valid" (click)="doSomething()"> Click me</button>
</div>
Your component (Do not forget the imports!):
****
import { FormBuilder, FormGroup, FormControl, Validators } from '#angular/forms';
****
constructor(private fb: FormBuilder){}
options = [{key:1,value:"A"},
{key:2,value:"B"},
{key:3,value:"C"}];
public readonly form: FormGroup = this.fb.group({
SelectedValue: new FormControl("",Validators.required)
});
doSomething(){
//ur logic goes here
alert(this.form.value.SelectedValue);
}
Another option is to use ng-select if you are using angular bootstrap in your project which has some extra functionality like clear and styling.
I am using innerHtml to show html content from variable in angular but it is not supporting external style and ngFor also.in the following screen whenever user click on the add service button then one row should increment.
this is the data having in service list with.
<div class="col-sm-6 col-md-4">
<label class="mobileNumberLabel " for="mobilrNumber">Select Service</label>
<div class="nice-wrap">
<select class="js-example-basic-single nice-textbox" style="width: 105%">
<optgroup *ngFor="let item of serviceObject" label='{{item.categoryName}}'>
<option *ngFor="let service of item.services">
{{service.serviceName}} - <span >{{item.categoryName}}</span>
</option>
</optgroup>
</select></div>
</div>
I tried with innerHtml
<div [innerHTML]="divItem"></div>
but it is showing like this
Angular doesn't work the way you envision it to work, based on your question. When you write a template in Angular, it's actually not HTML -- it's a custom Angular syntax known to the Angular compiler (ngc). Your templates are transformed to TypeScript code which manipulates the DOM based on the model (which is then transformed to JavaScript by tsc so it can be executed by the browser).
This transformation is happening while you're compiling your code. When it reaches user's browsers, there's nothing of Angular templates left in it: no *ngFor, no {{ interpolation.
Therefore, setting innerHTML to Angular's syntax (such as *ngFor or {{ }}) won't work, because you're trying to do in run-time of the application. You'll have to tweak your logic and the way you're building templates to a more structural way.
Instead of setting a string with {{ a }} which you [innerHTML] into an element, switch your logic so that you have an object, for example, { a: 'value' }, and pre-define your template such as {{ obj.a }} in that element.
I think you want to add a reactive form dynamically into the HTML.
I came across this situation some time back as I had to add new paragraphs dynamically.
In this code, I have a text area as input for the paragraph. I can add a paragraph by clicking on the Add New Paragraph button and delete a paragraph by clicking on the Remove button.
.html file should have -
<div formArrayName="contentDescriptions">
<div *ngFor="let contentItem of contentDescriptions.controls; let i=index">
<textarea [formControlName]="i" name="" id="" cols="30" rows="6" placeholder="Enter Description"></textarea>
<button (click)="deleteContentDescription(i)"> Remove</button>
</div>
<div>
<button (click)="addNewParagraph()">
<span class="icon icon-plus"></span> Add New Paragraph
</button>
</div>
</div>
.ts file should have -
form = this.fb.group({
contentDescriptions: this.fb.array([
this.fb.control('')
])
});
get contentDescriptions() {
return this.form.get('contentDescriptions') as FormArray;
};
constructor(private fb: FormBuilder) { }
ngOnInit(){
this.form.patchValue({
contentDescriptions: this.contentSelected.descriptionArray
});
}
addNewParagraph(){
this.contentDescriptions.push(this.fb.control(''));
}
deleteContentDescription(i){
this.contentDescriptions.removeAt(i);
}
I want to create the following component in ember (templates / components / echo-hlabel-tf.hbs)
<div id="echo-hlabel-tf">
<span id="tf-label">{{label}}</span>
<span id="tf-value">
<input type="text" value={{textValue}}
{{action "validateRegExp" textValue regExp post on="focusOut"}}
{{action "showText" textValue post on="mouseUp"}}
/>
</span>
</div>
Just a textfield and a label placed in the same row . I have created the functions in the controller of the component (components / echo-hlabel-tf.js):
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
validateRegExp(text,regExpStr){
let regExpObj = new RegExp(regExpStr)
if(regExpObj.test(text)){
console.log('entry matches')
}else{
console.log('entry is wrong');
}
},
showText(text){
console.log(text);
}
}
});
I want to use this component from another template (template / programmers.hbs) :
<ul>
{{people-list title="List of programmers" people=model}}
</ul>
<div>
{{echo-hlabel-tf
label="Textfield horizontal"
textValue="..."
regExp="^([a-z0-9]{5})$"
}}
</div>
The thing is that , even the actions are fired as the events trigger, the variable that represents the value of the input (textValue) always stands the same ("...") . seems that the binding between the outer template, and the components is created at the beginning, but if I put more letters in the textfield , the value of textValue remains the same and doesn't change . I would like to, as I'm adding letters to the textBox, print them with the console with the method showText, but I always print "..."
You are using pure html5 input that does not do two way binding by itself. That is even if you type something, it is not reflected to textValue attribute. You can use ember's builtin input component as in this twiddle. I believe this will solve your problem.
I am using a custom-element with my Aurelia app. When I am binding data from my view-model with the custom-element, it is working fine. Even if I make some changes in the data in the custom-element control, the change is also reflected to the data in my view model, thanks to the two-way data binding.
However, if I make some changes in the data from the view model (using javascript), the data is not updated in the custom-element. I have replicated this problem for a simpler setting.
Simple View Model
export class Playground {
public testObj: any;
counter = 0;
constructor() {
this.testObj = {
prop1: "This is prop1 value"
, collection2: [{ id: "item21" }]
}
}
updateProp1() {
alert("before update: "+this.testObj.prop1);
this.testObj.prop1 = "Sayan " + this.counter++;
alert(this.testObj.prop1);
}
verifyChange() {
alert(this.testObj.prop1);
}
}
Simple View
<template>
<h1>
Playground
</h1>
<div >
<div repeat.for="item of testObj.collection2">
<div class="row">
<div class="col-sm-4">
<input type="text" class="form-control" placeholder="prop1"
value.bind="$parent.testObj['prop1']">
</div>
</div>
</div>
<button click.delegate="updateProp1()" class="btn btn-primary"> Update Prop1 </button>
<button click.delegate="verifyChange()" class="btn btn-primary"> Verify Change </button>
</div>
</template>
Now whenever I click Verify Change after changing the value in textbox, the changed value comes in the alert. But, if I click the Update Prop1 button, the prop1 value gets updated, but the change doesn't reflect in the view.
I am not sure exactly what I am missing.
Note: Instead of using $parent.testObj['prop1'], if $parent.testObj.prop1 is used, the databinding works as it should. However, my actual custom-element is of generic kind and the property name is not known before hand, hence it seems that I need to keep using $parent.testObj['prop1'] notation instead of dot notation: $parent.testObj.prop1.
At pointer/suggestion/feedback is appreciated.
Update: If anyone can point out the the difference between the dot notation and indexer notation w.r.t. aurelia data-binding (I have checked this already), that will be of great help.
This was an issue in earlier builds of the aurelia/binding module. Here's the related issue(s):
https://github.com/aurelia/binding/issues/75
https://github.com/aurelia/binding/pull/76
I tried your view/view-model in the latest version of aurelia and everything worked. Here's a screencast of what I saw: http://screencast.com/t/KqcuqXWjkU2
Make sure you have the latest version of the aurelia components installed- update steps here: http://blog.durandal.io/2015/05/01/aurelia-may-status-and-releases/