I build a Form in angular2 and typescript.
I try to add a list of checkboxes dynamically and it's not working for me, Where is my mistake?
The template code:
<div formArrayName="categories">
<div class="form-group" *ngFor="let category of updateDetailsForm.controls.categories.controls; let i = index">
<label>
<input type="checkbox" formControlName="{{i}}">
{{category.name}}
</label>
</div>
</div>
The Typescript code:
updateDetailsForm: FormGroup;
private categories : Category[] = []
constructor(private router: Router,
private ss : SearchService,
private formBuilder: FormBuilder)
{
this.initForm() // before the categories loaded
this.ss.getAllCategories().subscribe(
data => {
this.categories = data
this.initForm() // refresh the form with categories
}
)
}
initForm() {
let allCategories: FormArray = new FormArray([]);
for (let i = 0; i < this.categories.length; i++) {
allCategories.push(
new FormGroup({
'name': new FormControl([this.categories[i].categoryName])
})
)
}
this.updateDetailsForm = this.formBuilder.group({
'image' : [''],
'categories': allCategories
})
}
This is my UI result, and I get the following error:
"inline template:17:33 caused by: control.registerOnChange is not a function"
The number of checkboxes is correct but the text is missing and I can't update the form result with user selection.
What the error means?
How can I insert the right text next the checkbox?
How can update the user selection into the form value?
You've built out your form model in such a way that we'll need to access each FormGroup that you pushed onto the array and then access the named control within:
<span formGroupName="{{i}}">
<input type="checkbox" formControlName="{{category.name}}">
{{category.name}}
</span>
We'll also need to tweak how we're pushing values so that the name is set uniquely instead of always set as "name":
let fg = new FormGroup({});
fg.addControl(this.categories[i].name, new FormControl(false))
allCategories.push(fg)
Here's a plunker to demo it: http://plnkr.co/edit/OTiqwr1oCb8uEThQyDHx?p=preview
I think because of same name categories of your component variable and form group control you are getting error. Check it by making below change in your component and form HTML :
You can check FormArrayName directive for more reference.
//Component
updateDetailsForm: FormGroup;
private allCategories : Category[] = []
constructor(private router: Router,
private ss : SearchService,
private formBuilder: FormBuilder) {
this.initForm() // before the categories loaded
this.ss.getAllCategories().subscribe(
data => {
this.allCategories = data
this.initForm() // refresh the form with categories
}
)
}
initForm() {
let allCategories: FormArray = new FormArray([]);
for (let i = 0; i < this.allCategories.length; i++) {
allCategories.push(new FormControl());
}
this.updateDetailsForm = this.formBuilder.group({
'image' : [''],
'categories': allCategories
})
}
// Form HTML
<div formArrayName="categories">
<div class="form-group" *ngFor="let category of categories.controls; let i = index">
<label>
<input type="checkbox" formControlName="i">
{{allCategories[i].name}}
</label>
</div>
</div>
Related
How can I send a single string value through view from in the WebAPI made in ASP.NET core.
Here is my API:
[HttpPost]
[Route("studentlogin/{value}")]
public IActionResult StudentLogin(string value)
{
string name = String.Concat(value.Where(s => !Char.IsWhiteSpace(s)));
name = name.ToLower();
var newStudent = new Student();
newStudent.FullName = name;
db.Entry(newStudent).State = EntityState.Added;
db.SaveChanges();
var selectStudent = db.Students.Where(ss => ss.FullName == name).FirstOrDefault();
var id = selectStudent.Id;
string idToString = id.ToString();
string answer = sha384converter(idToString);
selectStudent.HashId = answer;
db.Entry(selectStudent).State = EntityState.Modified;
db.SaveChanges();
return Ok();
}
I want to send paramenter value in it through View Forms, How can I send data in it?
Thanks in advance.
[Route("studentlogin/{value}")]
If you are using the above route, the request URL should like this: https://localhost:44310/api/apicontrollername/studentlogin/XXX, the parameter will be transferred from the URL and route.
to send paramenter value in it through View Forms, How can I send data
in it?
To send parameter value from posted form fields, you could modify the code as below:
In the API controller, remote the route parameter and add the FromForm attribute.
[HttpPost]
[Route("studentlogin")]
public IActionResult StudentLogin([FromForm]string value)
{
string name = "Default value";
if (!string.IsNullOrEmpty(value))
{
name = String.Concat(value.Where(s => !Char.IsWhiteSpace(s)));
}
... // add your code
return Ok(name);
}
Then, create a model which contain the value property, like this:
public class StudentLoginViewModel
{
public string value { get; set; }
}
In the View page, display the above model in the form:
#model WebApplication6.Models.StudentLoginViewModel;
<form id="myform">
<div class="form-group">
<label asp-for="value" class="control-label"></label>
<input asp-for="value" class="form-control" />
<span asp-validation-for="value" class="text-danger"></span>
</div>
<div class="form-group">
<input type="button" id="btnsubmit" value="Submit" class="btn btn-primary" />
</div>
</form>
and add the following script at the end of the above view page:
#section Scripts{
<script>
$(function () {
$("#btnsubmit").click(function () {
//use the serialize() method to encode a set of form elements as a string for submission
var data = $("#myform").serialize();
//you can also create JavaScript object and send the data.
//var data = { value: $("#value").val() };
$.ajax({
url: '/api/todo/studentlogin', // change the url to yours.
type: "post",
contentType: 'application/x-www-form-urlencoded',
data: data,
success: function (result) {
console.log(result);
}
});
});
})
</script>
}
The result as below:
I'm trying to implement rating system in my shopping app, but received error console when trying to open the page.
The error on console are:
Function Query.where() requires a valid third argument, but it was undefined. - it points to:
this.stars = this.starService.getProductStars(this.movieId)
AND
const starsRef = this.afs.collection('stars', ref => ref.where('movieId', '==', movieId));
Below is my code:
rating.page.html: (where I put my 2 of components which are TestRate and Star-Review)
<ion-content>
<app-testrate></app-testrate>
<app-star-review-component></app-star-review-component>
</ion-content>
testrate.component.html:
<div *ngIf="movie | async as m">
<h1>
{{m.title}}
</h1>
<img [src]="m.image" width="100px">
<p>
{{m.plot}}
</p>
<star-review [movieId]="movieId" [userId]="userId"></star-review>
</div>
testrate.component.ts:
export class TestrateComponent implements OnInit {
userDoc: AngularFirestoreDocument<any>;
movieDoc: AngularFirestoreDocument<any>;
user: Observable<any>;
movie: Observable<any>;
constructor(private afs: AngularFirestore) { }
ngOnInit() {
this.userDoc = this.afs.doc('users/test-user-3')
this.movieDoc = this.afs.doc('movies/battlefield-earth')
this.movie = this.movieDoc.valueChanges()
this.user = this.userDoc.valueChanges()
}
get movieId() {
return this.movieDoc.ref.id
}
get userId() {
return this.userDoc.ref.id
}
}
star-review.component.html:
<h3>Average Rating</h3>
{{ avgRating | async }}
<h3>Reviews</h3>
<div *ngFor="let star of stars | async">
{{ star.userId }} gave {{ star.movieId }} {{ star.value }} stars
</div>
<h3>Post your Review</h3>
<fieldset class="rating">
<ng-container *ngFor="let num of [5, 4, 3, 2, 1]">
full star
<input (click)="starHandler(num)"
[id]="'star'+num"
[value]="num-0.5"
name="rating"
type="radio" />
<label class="full" [for]="'star'+num"></label>
half star
<input (click)="starHandler(num-0.5)"
[value]="num-0.5"
[id]="'halfstar'+num"
name="rating"
type="radio" />
<label class="half" [for]="'halfstar'+num"></label>
</ng-container>
</fieldset>
star-review.component.ts:
export class StarReviewComponentComponent implements OnInit {
#Input() userId;
#Input() movieId;
stars: Observable<any>;
avgRating: Observable<any>;
constructor(private starService: StarService) { }
ngOnInit() {
this.stars = this.starService.getProductStars(this.movieId)
this.avgRating = this.stars.pipe(map(arr => {
const ratings = arr.map(v => v.value)
return ratings.length ? ratings.reduce((total, val) => total + val) / arr.length : 'not reviewed'
}))
}
starHandler(value) {
this.starService.setStar(this.userId, this.movieId, value)
}
}
star.service.ts:
export class StarService {
constructor(private afs: AngularFirestore) { }
// Star reviews that belong to a user
getUserStars(userId) {
const starsRef = this.afs.collection('stars', ref => ref.where('userId', '==', userId));
return starsRef.valueChanges();
}
// Get all stars that belog to a Product
getProductStars(movieId) {
const starsRef = this.afs.collection('stars', ref => ref.where('movieId', '==', movieId));
return starsRef.valueChanges();
}
// Create or update star
setStar(userId, movieId, value) {
// Star document data
const star: Star = { userId, movieId, value };
// Custom doc ID for relationship
const starPath = `stars/${star.userId}_${star.movieId}`;
// Set the data, return the promise
return this.afs.doc(starPath).set(star)
}
}
I'm trying to pass form values including checkboxes in angular 6 forms using formbuilder but I'm unable to read the value from checkbox. I am getting all the values from all the other input fields but only checkbox is not responding Here is my code:
<form [formGroup]="myGroup" (submit)="submit(myGroup.value)">
<div class="row">
<div class="col-sm-4" *ngFor="let info of myGroup.controls['myInfo'].controls; let i = index">
<label for="{{labelValue[i].name}}"> {{labelValue[i].label}}
<input type="{{labelValue[i].type}}" class="{{labelValue[i].class}}" [formControl]="info">
</label>
</div>
</div>
<div class="row">
<button class="form-control btn-sub" type=”submit”>
Submit Details
</button>
</div>
My component class:
import { ProposalService, CustomerDetails, ProposalNumber } from 'src/app/Services/Proposal-service/proposal.service';
export interface InputType{
name:string;
type: string;
label: string;
class:string;
}
export class ProposalComponent implements OnInit {
public labelValue: InputType[] = [
{name:"fname",type:"text",label:"First Name", class:"form-control"},
{name:"form60",type:"checkbox",label:"Is Collection Of form 60", class:"form-control"},
{name:"eia-num",type:"number",label:"EIA Number", class:"form-control"}
];
title = "Customer Details";
details: Observable<CustomerDetails>;
pNumber: ProposalNumber ;
public information: CustomerDetails[] = [
{name:"First Name", value:""},//
{name:"IsCollectionOfform60", value:true},
{name:"EIA Number", value:""}
];
myGroup : FormGroup;
constructor(private formBuilder: FormBuilder,
private _proposalService: ProposalService) { }
ngOnInit() {
this.myGroup = this.formBuilder.group({
myInfo: this.constructFormArray()
});
this.pNumber = <ProposalNumber>{proposalNumber: 0 ,message:"", status: ""};
}
constructFormArray()
{
const arr = this.information.map(cat => {
return this.formBuilder.control(cat.value);
});
return this.formBuilder.array(arr);
}
submit(form){
//this.loading = true;
console.log(form);
let mySelectedAddon = form.myInfo.map((currentValue,i)=> {
return { "name" : this.information[i].name , "value" : currentValue}
}
);
console.log(mySelectedAddon);
this._proposalService.loadCustomer(mySelectedAddon).subscribe((res: ProposalNumber) =>{
//this.loading = false;
console.log(res);
this.pNumber.proposalNumber = res.proposalNumber;
this.pNumber.message = res.message;
console.log(this.pNumber.proposalNumber);
return this.myGroup.value;
});
}
}
You need to use the 'change' event and pass the respective input value and event to a method onChange where you check if it's checked, then add the respective value to the formarray, if it's unchecked, remove the chosen email from the form array.
You can refer the below link:
https://stackblitz.com/edit/angular-rskaug?file=src%2Fapp%2Fapp.component.ts
Above example is useful to get the values of checkbox dynamically.
I'm trying to build a form with conditional fields from a JSON schema using react-jsonschema-form and react-jsonschem-form-conditionals.
The components I'm rendering are a FormWithConditionals and a FormModelInspector. The latter is a very simple component that shows the form model.
The relevant source code is:
import React from 'react';
import PropTypes from 'prop-types';
import Engine from "json-rules-engine-simplified";
import Form from "react-jsonschema-form";
import applyRules from "react-jsonschema-form-conditionals";
function FormModelInspector (props) {
return (
<div>
<div className="checkbox">
<label>
<input type="checkbox" onChange={props.onChange} checked={props.showModel}/>
Show Form Model
</label>
</div>
{
props.showModel && <pre>{JSON.stringify(props.formData, null, 2)}</pre>
}
</div>
)
}
class ConditionalForm extends React.Component {
constructor (props) {
super(props);
this.state = {
formData: {},
showModel: true
};
this.handleFormDataChange = this.handleFormDataChange.bind(this);
this.handleShowModelChange = this.handleShowModelChange.bind(this);
}
handleShowModelChange (event) {
this.setState({showModel: event.target.checked});
}
handleFormDataChange ({formData}) {
this.setState({formData});
}
render () {
const schema = {
type: "object",
title: "User form",
properties: {
nameHider: {
type: 'boolean',
title: 'Hide name'
},
name: {
type: 'string',
title: 'Name'
}
}
};
const uiSchema = {};
const rules = [{
conditions: {
nameHider: {is: true}
},
event: {
type: "remove",
params: {
field: "name"
}
}
}];
const FormWithConditionals = applyRules(schema, uiSchema, rules, Engine)(Form);
return (
<div className="row">
<div className="col-md-6">
<FormWithConditionals schema={schema}
uiSchema={uiSchema}
formData={this.state.formData}
onChange={this.handleFormDataChange}
noHtml5Validate={true}>
</FormWithConditionals>
</div>
<div className="col-md-6">
<FormModelInspector formData={this.state.formData}
showModel={this.state.showModel}
onChange={this.handleShowModelChange}/>
</div>
</div>
);
}
}
ConditionalForm.propTypes = {
schema: PropTypes.object.isRequired,
uiSchema: PropTypes.object.isRequired,
rules: PropTypes.array.isRequired
};
ConditionalForm.defaultProps = {
uiSchema: {},
rules: []
};
However, every time I change a field's value, the field loses focus. I suspect the cause of the problem is something in the react-jsonschema-form-conditionals library, because if I replace <FormWithConditionals> with <Form>, the problem does not occur.
If I remove the handler onChange={this.handleFormDataChange} the input field no longer loses focus when it's value changes (but removing this handler breaks the FormModelInspector).
Aside
In the code above, if I remove the handler onChange={this.handleFormDataChange}, the <FormModelInspector> is not updated when the form data changes. I don't understand why this handler is necessary because the <FormModelInspector> is passed a reference to the form data via the formData attribute. Perhaps it's because every change to the form data causes a new object to be constructed, rather than a modification of the same object?
The problem is pretty straightforward, you are creating a FormWithConditionals component in your render method and in your onChange handler you setState which triggers a re-render and thus a new instance of FormWithConditionals is created and hence it loses focus. You need to move this instance out of render method and perhaps out of the component itself since it uses static values.
As schema, uiSchema and rules are passed as props to the ConditionalForm, you can create an instance of FormWithConditionals in constructor function and use it in render like this
import React from 'react';
import PropTypes from 'prop-types';
import Engine from "json-rules-engine-simplified";
import Form from "react-jsonschema-form";
import applyRules from "react-jsonschema-form-conditionals";
function FormModelInspector (props) {
return (
<div>
<div className="checkbox">
<label>
<input type="checkbox" onChange={props.onChange} checked={props.showModel}/>
Show Form Model
</label>
</div>
{
props.showModel && <pre>{JSON.stringify(props.formData, null, 2)}</pre>
}
</div>
)
}
class ConditionalForm extends React.Component {
constructor (props) {
super(props);
this.state = {
formData: {},
showModel: true
};
const { schema, uiSchema, rules } = props;
this.FormWithConditionals = applyRules(schema, uiSchema, rules, Engine)(Form);
this.handleFormDataChange = this.handleFormDataChange.bind(this);
this.handleShowModelChange = this.handleShowModelChange.bind(this);
}
handleShowModelChange (event) {
this.setState({showModel: event.target.checked});
}
handleFormDataChange ({formData}) {
this.setState({formData});
}
render () {
const FormWithConditionals = this.FormWithConditionals;
return (
<div className="row">
<div className="col-md-6">
<FormWithConditionals schema={schema}
uiSchema={uiSchema}
formData={this.state.formData}
onChange={this.handleFormDataChange}
noHtml5Validate={true}>
</FormWithConditionals>
</div>
<div className="col-md-6">
<FormModelInspector formData={this.state.formData}
showModel={this.state.showModel}
onChange={this.handleShowModelChange}/>
</div>
</div>
);
}
}
ConditionalForm.propTypes = {
schema: PropTypes.object.isRequired,
uiSchema: PropTypes.object.isRequired,
rules: PropTypes.array.isRequired
};
ConditionalForm.defaultProps = {
uiSchema: {},
rules: []
};
For anyone bumping into the same problem but using Hooks, here's how without a class :
Just use a variable declared outside the component and initialize it inside useEffect. (don't forget to pass [] as second parameter to tell react that we do not depend on any variable, replicating the componentWillMount effect)
// import ...
import Engine from 'json-rules-engine-simplified'
import Form from 'react-jsonschema-form'
let FormWithConditionals = () => null
const MyComponent = (props) => {
const {
formData,
schema,
uischema,
rules,
} = props;
useEffect(() => {
FormWithConditionals = applyRules(schema, uischema, rules, Engine)(Form)
}, [])
return (
<FormWithConditionals>
<div></div>
</FormWithConditionals>
);
}
export default MyComponent
Have you tried declaring function FormModelInspector as an arrow func :
const FormModelInspector = props => (
<div>
<div className="checkbox">
<label>
<input type="checkbox" onChange={props.onChange} checked={props.showModel}/>
Show Form Model
</label>
</div>
{
props.showModel && <pre>{JSON.stringify(props.formData, null, 2)}</pre>
}
</div>
)
I have a list of country objects, that I access and use with my reactive form. I create each one as a form control dynamically, because this list will be changing. Then I attempt to get the form and values in real time (not using a submit button), as the checkboxes get clicked, by using the ngOnChanges hook. this is obviously not working, what hook should I use? on another note, is this a bad way to accomplish this? what would be a better approach?
component
export class GeoDropComponent implements OnInit, OnChanges {
countries = [
{
name : 'USA',
continent : 'north america'
},
{
name : 'Canada',
continent: 'north america'
}
];
countriesForm: FormGroup;
constructor() { }
ngOnInit() {
// add checkbox for each country
this.countriesForm = new FormGroup({});
for (let i = 0; i < this.countries.length; i++) {
this.countriesForm.addControl(
this.countries[i].name, new FormControl(false)
)
}
}
ngOnChanges() {
console.log(this.countriesForm);
}
}
html
<div class="geo-list">
<div class="content-box container">
<form [formGroup]="countriesForm">
<div class="country" *ngFor="let country of countries">
<input
type="checkbox"
formControlName="{{country.name}}"
>
{{ country.name }} | {{ country.continent }}
</div>
</form>
</div>
</div>
you can try like this. when ever search checkbox is selected or selected change method will update selected items
pseudo code
<input
type="checkbox"
formControlName="{{country.name}}"
(change)="search(country, $event)
>
component file.
selectedItems : any [] = [];
search(country, event) {
var index = this.selectedItems.indexOf(country.name);
if (event.target.checked) {
if (index === -1) {
this.selectedItems.push(country.name);
}
} else {
if (index !== -1) {
this.selectedItems.splice(index, 1);
}
}
}
}