Getting error while using Angular Form Array - angular-reactive-forms

Why am I getting this error, and how to get it fixed:
TS2740: Type 'AbstractControl<any, any>' is missing the following properties from type 'FormGroup': controls, registerControl, addControl, removeControl, and 2 more.
At this point it is giving error in my html:
my html:
<button type="button" (click)="addName()">Add Name</button>
<div formArrayName="names">
<div *ngFor="let nm of getNames.controls">
<div [formGroup]="nm"> //THIS LINE IS GIVING ERROR
<input formControlName="fname" type="text" placeholder="First Name">
<input formControlName="lname" type="text" placeholder="Last Name">
</div>
</div>
</div>
<div>
<button type="submit">Submit</button>
</div>
my ts:
export class TestComponent implements OnInit {
constructor(private fb: FormBuilder) { }
form = this.fb.group({
names: this.fb.array([])
});
get getNames(){
return this.form.controls["names"] as FormArray;
}
addName(){
const newName = this.fb.group({
fname: ['', Validators.required],
lname: ['', Validators.required]
});
this.getNames.push(newName);
}
onSubmit():void{
console.log(this.form.value)
}
ngOnInit(): void {
}
}

I got it resolved by using $any in html as below.
<div *ngFor="let nm of $any(getName).controls; let i=index">

hi i us this way try it
<div *ngFor="let nm of getName.controls; let i=index" [formGroupName]="i">

Related

Stuck on uploading image by formData

I have a page that user can change his picture profile, but I'm stuck when I do a submitHandle, because when I try to console.log(formData) it always gives me empty results.
this is my input file of picture profile
<input id="file-upload" type="file" name="fileUpload" class="d-none" #change="fileUpload">
<label for="file-upload">
<img class="ap-img__main rounded-circle wh-120 bg-lighter d-flex" :src="`/dashboard/img/author/profile/`+user.avatar"alt="profile img" v-if="!imagePreview">
<img class="ap-img__main rounded-circle wh-120 bg-lighter d-flex" :src="imagePreview" alt="profile img" v-if="imagePreview">
<span class="cross" id="remove_pro_pic">
<i class="fas fa-camera"></i>
</span>
</label>
this is the methods
fileUpload(e) {
let selectedImage = e.target.files[0];
this.imageLocation = selectedImage;
console.log(this.imageLocation.name);
let reader = new FileReader();
reader.readAsDataURL(this.imageLocation)
reader.onload = e => {
this.imagePreview = e.target.result;
}
},
and it has results like this,
But when I click the save button to save file, by formData method, it will have empty results.
This is the button update,
<div class="button-group d-flex pt-25 justify-content-start">
<button #click="submitHandle" class="btn btn-primary btn-default btn-squared text capitalize radius-md shadow2">Update details
</button>
</div>
this is the submitHandle methods,
submitHandle() {
e.preventDefault();
var formData = new FormData();
formData.append('image', this.imageLocation);
formData.append('name', this.user.name);
formData.append('username', this.user.username);
formData.append('email', this.user.email);
formData.append('phone', this.user.phone);
formData.append('gender', this.user.gender);
formData.append('birth', this.user.birth);
formData.append('bio', this.user.bio);
formData.append("facebook", this.user.facebook);
formData.append("instagram", this.user.instagram);
console.log(formData);
},
and when on console log,
it is just shown like this,
please anyone, can help me to solve this?
Thank you very much.
Logging FormData
The default toString() of FormData does not log its entries, so it only seems like the FormData is empty.
A quick way to log the entries is to wrap FormData.prototype.entries in an array:
console.log(Array.from(formData.entries()))
const formData = new FormData();
formData.append('image', 'my image');
formData.append('name', 'my name');
formData.append('username', 'my username');
formData.append('email', 'my email');
formData.append('phone', 'my phone');
formData.append('gender', 'my gender');
formData.append('birth', 'my birth');
formData.append('bio', 'my bio');
formData.append("facebook", 'my facebook');
formData.append("instagram", 'my instagram');
console.log(Array.from(formData.entries()));
Sending FormData with axios
axios.patch takes the FormData instance as its second argument:
axios.patch(apiServerUrl, formData)
Note you can easily create the FormData from the given <form> element if all its <input>s have the appropriate name attribute set, as seen in this example:
<template>
<form #submit.prevent="submitHandle">
<label>Profile Image <input type="file" name="image" autocomplete="photo" v-model="imageLocation"></label>
<label>Username <input type="text" name="username" autocomplete="username" v-model="user.username"></label>
<label>Email <input type="email" name="email" autocomplete="email" v-model="user.email"></label>
<label>Phone <input type="tel" name="phone" autocomplete="tel" v-model="user.phone"></label>
<label>Gender <input type="text" name="gender" autocomplete="sex" v-model="user.gender"></label>
<label>Birthdate <input type="date" name="birth" autocomplete="bday" v-model="user.birth"></label>
<label>Bio <textarea type="text" name="bio" v-model="user.bio"></textarea></label>
<label>Facebook <input type="text" name="facebook" v-model="user.facebook"></label>
<label>Instagram <input type="text" name="instagram" v-model="user.instagram"></label>
<button type="submit">Submit</button>
</form>
<pre>{{ response }}</pre>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
response: null,
imageLocation: null,
user: {
username: null,
email: null,
phone: null,
gender: null,
birth: null,
bio: null,
facebook: null,
instagram: null,
}
}
},
methods: {
submitHandle(e) {
const form = e.target
const formData = new FormData(form)
axios.patch('https://httpbin.org/patch', formData)
.then(resp => this.response = resp.data)
}
}
}
</script>
<style>
label {
display: block;
}
</style>
demo

ERROR Error: No value accessor for form control with name:

I have followed
the following guide
Angular-Reactive forms
I modified a bit the code to put it in a module, adding some fields to the originals model and it worked.
after upgrading to angular 8, the module is still working, but the console in Chrome for every control gives me this error:core.js:6150 ERROR Error: No value accessor for form control with name: 'title' at _throwError (forms.js:2603) at setUpControl (forms.js:2377) at FormGroupDirective.addControl (forms.js:5699) at FormControlName._setUpControl (forms.js:6274) at FormControlName.ngOnChanges (forms.js:6219) at FormControlName.rememberChangeHistoryAndInvokeOnChangesHook (core.js:1506) at callHook (core.js:2525) at callHooks (core.js:2492) at executeInitAndCheckHooks (core.js:2443) at selectIndexInternal (core.js:8389)
this is my code for question-form-component.html:
<div [formGroup]="form">
<label [attr.for]="question?.key">{{ question?.label }}</label>
<div [ngSwitch]="question?.controlType">
<input
ngDefaultControl
*ngSwitchCase="'textbox'"
[formControlName]="question?.key"
[id]="question?.key"
[type]="question?.type"
[value] = "question?.value"
/>
<div [ngSwitch]="question?.controlType">
<ion-textarea
ngDefaultControl
*ngSwitchCase="'textArea'"
[formControlName]="question?.key"
[id]="question?.key"
autoGrow="question?.autoGrow"
[value]= "question?.value"
></ion-textarea>
</div>
<div *ngSwitchCase="'barcodeScanner'">
<app-barcode-scanner
[formControlName]="question?.key"
(ngModelChange)="question?.onChange($event)"
[id]="question?.key"
[value]="question?.value"
></app-barcode-scanner>
</div>
<div *ngSwitchCase="'datebox'">
<ion-datetime [formControlName]="question?.key" [id]="question?.key">
</ion-datetime>
</div>
<div *ngSwitchCase="'switchBox'">
<p class="switchText">
{{ getValue ? question.labelTrue : question.labelFalse }}
</p>
<ion-icon
[name]="getValue ? question.iconTrue:question.iconFalse"
></ion-icon>
<ion-toggle
[formControlName]="question?.key"
[id]="question?.key"
></ion-toggle>
</div>
<div
[formControlName]="question?.key"
[id]="question?.key"
>
<div *ngSwitchCase="'itemSelector'"
[formControlName]="question?.key"
[id]="question?.key"
ngDefaultControl
>
<app-selector-items
[formControlName]="question?.key"
[id]="question?.key"
[text]="question?.text" [service]="question?.service" (selectedItem)="question.selectedItem($event)"
[sorterFunction]="question?.sorterFunction"
[filterFunction]="question?.ItemsFilterFunction"
[createPopup]= "question?.createPopup"
[item]="question?.value"
(selectedItem)="question.selectedItem($event)" >
</div>
<select
[id]="question?.key"
*ngSwitchCase="'dropdown'"
[value]="question?.value"
[formControlName]="question?.key"
>
<option *ngFor="let opt of question?.options" [value]="opt?.value">{{
opt.key
}}</option>
</select>
<div *ngSwitchCase="'geobox'">
<input-geolocation
[id]="question?.key"
[address]="question.value?.adress"
[formControlName]="question?.key"
(ngModelChange)="question?.onChange($event)"
></input-geolocation>
</div>
{{ question ? question.label : "" }} is required
this is my question-form-component.ts:
import {
Component,
Input,
OnInit,
Output,
EventEmitter,
ChangeDetectionStrategy,
ɵConsole,
OnChanges,
SimpleChanges
} from '#angular/core';
import { FormGroup, FormControl } from '#angular/forms';
import { QuestionBase } from '../../models/question-base';
#Component({
selector: 'app-question',
templateUrl: './question-form-component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class QuestionFormComponent implements OnInit, OnChanges {
public value: any;
#Input() question: QuestionBase;
#Input() form: FormGroup;
ngOnInit() {
this.value = this.question ? this.question.value : undefined;
this.form = this.form
? this.form
: new FormGroup({
// I need an instance of formgroup for run the tests
name: new FormControl(this.question.key),
value: new FormControl(this.question.value)
});
}
ngOnChanges(changes: SimpleChanges) {
}
get isValid() {
return this.question ? this.form.controls[this.question.key].valid : false;
}
get getValue() {
return this.question.key ? this.form.get(this.question.key).value : '';
}
getIcon() {
return this.form.get(this.question.key).value ? String(this.question.iconTrue) : String(this.question.iconFalse)
}
}
thanks in advance

Toggle form in nested v-for loop in VueJS

I have a list of nested comments. Under each comment, I'd like to add a "reply" button that, when click, show a reply form.
For now, everytime I click a "reply" button, it shows the form. But the thing is, I'd like to show only one form on the whole page. So basically, when I click on "reply" it should close the other form alreay opened and open a new one under the right comment.
Edit :
So I was able to make some slight progress. Now I'm able to only have one active form opening on each level of depth in the nested loop. Obviously, what I'm trying to do now is to only have one at all.
What I did was emitting an event from the child component and handle everything in the parent component. The thing is, it would work great in a non-nested comment list but not so much in my case...
Here is the new code:
In the parentComponent, I have a handleSelected method as such:
handleSelected (id) {
if(this.selectedItem === id)
this.selectedItem = null;
else
this.selectedItem = id;
},
And my childComponent:
<template>
<div v-if="comment">
<div v-bind:style=" iAmSelected ? 'background: red;' : 'background: none;' ">
<p>{{ comment.author.name }}<br />{{ comment.created_at }}</p>
<p>{{ comment.content }}</p>
<button class="button" #click="toggle(comment.id)">Répondre</button>
<button class="button" #click="remove(comment.id)">Supprimer</button>
<div v-show="iAmSelected">
<form #submit.prevent="submit">
<div class="form-group">
<label for="comment">Votre réponse</label>
<textarea class="form-control" name="comment" id="comment" rows="5" v-model="fields.comment"></textarea>
<div v-if="errors && errors.comment" class="text-danger">{{ errors.comment[0] }}</div>
</div>
<button type="submit" class="btn btn-primary">Envoyer</button>
<div v-if="success" class="alert alert-success mt-3">
Votre réponse a bien été envoyée !
</div>
</form>
</div>
</div>
<div v-if="comment.hasReply">
<div style="margin-left: 30px;">
<comment v-for="comment in comments"
:key="comment.id"
:comment="comment" #remove-comment="remove"
:is-selected="selectedItem" #selected="handleSelected($event)">
</comment>
</div>
</div>
</div>
</template>
<script>
import comment from './CommentItem'
export default {
name: 'comment',
props: {
isSelected: Number,
comment: {
required: true,
type: Object,
}
},
data () {
return {
comments: null,
fields: {},
errors: {},
success: false,
loaded: true,
selectedItem: null,
}
},
computed: {
iAmSelected () {
return this.isSelected === this.comment.id;
}
},
methods: {
remove(id) {
this.$emit('remove-comment', id)
},
toggle(id) {
this.$emit('selected', id);
},
handleSelected(id) {
if(this.selectedItem === id)
this.selectedItem = null;
else
this.selectedItem = id;
},
},
mounted(){
if (this.comment.hasReply) {
axios.get('/comment/replies/' + this.comment.id)
.then(response => {
this.comments = response.data
})
}
}
}
</script>
Thanks in advance for your help!

GraphQL Apollo recieving data object undefined

I am doing a mutation, that takes a username and password, and in response from the server it gets a login response.
<Mutation mutation={SIGN_UP}>
{(signUp, data) => (
<form onSubmit={e => {
e.preventDefault();
signUp({
variables: {
user: {
"email": input_email.value as String,
"password": input_password.value as String
}
}
});
console.log("Signup", signUp)
console.log("Data", data.data);
input_email.value = "";
input_password.value = "";
}}>
<div className="col-sm">
<div className="signin__header">Signup IMPACT R&D</div>
</div>
<div className="col-sm signin__input" >
<div className="signin__input__header" >Email</div>
<input className="signin__input__email" type="text" placeholder="Enter email" required ref={node=> {input_email = node}}></input>
<div className="signin__divide-line"></div>
</div>
<div className="col-sm signin__input">
<div className="signin__input__header" >Password</div>
<input className="signin__input__password" type="password" placeholder="Enter password" ref={node=> {input_password = node}}></input>
<div className="signin__divide-line"></div>
</div>
<div className="col-sm-3">
<button className="signin__input__button" type="submit">Sign Up</button>
</div>
</form>
)}
</Mutation>
This is my GraphQLtag.
export const SIGN_UP : any = gql`
mutation Register($user: UserInput!) {
register(user: $user) {
status,
refreshToken,
token,
error
}
}
`;
in the browser network i can see that I receive the data. But still my data object is null, when i try to print it out... HELP!
Found out that i needed to wait for the response with a then promise after the signup
}).then ((res: any) => {
errorMessage = res.data.register.token;
});

How to include new component in axios response

I have an component with text input. After fill input and submit form i sand axios query and after response i need stay on the same page with error popup or include new component with response data.
my component
<template>
<div class="col-md-12">
<div class="form-container">
<form v-on:submit="prepareCollage()" class="main-form">
<div class="form-group">
<input type="hidden" name="_token" value="">
<input placeholder="your text" v-model="text" name="query" type="text" class="form-control">
</div>
<button class="go btn btn-primary">Go!</button>
</form>
</div>
</div>
</template>
<script>
export default {
data () {
return {
text : '',
}
},
methods: {
prepareCollage(){
event.preventDefault();
axios.get('/api/prepare?query='+encodeURIComponent(this.text))
.then(function(result){
const result_data = result.data;
// if controller gave an error
if(result_data.error === true){
let error_text = result_data.error_text;
if(typeof(result_data.with_link) != 'undefined' && result_data.with_link.length > 0){
error_text += "<a href='"+result_data.with_link+"'>"+result_data.link_text+"</a>";
}
Vue.swal({
title: 'Error!',
html: error_text,
type: 'error',
})
}else{
// here i need include new component
}
});
}
}
}
</script>
in "else" block i have to include new vue component with data from this component. I have never used vuejs and have difficulty with understand this.

Resources