Grab template form input in Vue 3 class components (like done in Angular) - javascript

I started vue 3 today, and opted for the class-based approach in the cli. I'm from the Angular background, so forgive me for thinking like Angular. Every example I see (even in the docs) is still using the Vue({...}) thing, however, I wanna do something like this (still thinking Angular-ish)
In angular, I can do this
<form #formData="ngForm" (ngSubmit)="onSubmit(formData.value)">
<input (ngModel)="name" name="name" placeholder="name">
</form>
Then in component
...
export class AppComponent {
public name!: string;
onSubmit(formData: string) {
console.log(formData)
}
}
What would be the vue 3 class components approach like the above?
I currently have this in vue 3
export default class Welcome extends Vue {
name!: string;
onSubmit(formData: any) {
console.log(formData)
}
}
<template>
<div>
<form #submit.prevent="onSubmit">
<p>
<label for="name">Name</label> <br>
<input type="text" placeholder="name" id="name" name="name" v-model="name"/>
</p>
<button type="submit">Send</button>
</form>
</div>
</template>
What changes do I need to do to the above to connect the form in template to the component?

Without a third party library there isn't the same type of functionality that angular provides. Angular is doing a bunch of additional things to enhance the form object for validation and value tracking, and Vue natively does not do that. However you could instead put your data properties in an object to group them together. That way when you need to access them in something like the submit event to perhaps send all the values to an API, you can simply refer to that object instead of having to handle/build each property separately:
Class:
export class AppComponent {
// create object with bound form properties
public values: { name: string; } = { name: '' };
onSubmit() {
console.log(this.values); // { name: '' }
// axios.post('/api', this.values).then(res => console.log(res.data));
}
}
Template:
<template>
<div>
<form v-on:submit.prevent="onSubmit">
<p>
<label for="name">Name</label> <br>
<input type="text" placeholder="name" id="name" name="name" v-model="values.name"/>
</p>
<button type="submit">Send</button>
</form>
</div>
</template>
That being said, if you do need advanced form features like validation, sanitization, and similar there are plenty of libraries that do it well.
Hopefully that helps!

The native FormData constructor takes a <form> element, which creates the values of the form for all named inputs (<input>s or <textarea>s with name attribute).
So you could update your onSubmit method to retrieve the form data values:
export default class Welcome extends Vue {
name!: string;
onSubmit(formData: any) {
const form = e.target
const formData = new FormData(form)
}
}
Example using the Options API:
<template>
<div>
<form #submit.prevent="onSubmit">
<p>
<label for="name">Name</label> <br>
<input type="text" placeholder="name" id="name" name="name" v-model="name"/>
</p>
<button type="submit">Send</button>
</form>
</div>
</template>
<script>
export default {
methods: {
onSubmit(e) {
const form = e.target
const formData = new FormData(form)
console.log({ formData: Array.from(formData.entries()) })
}
}
}
</script>
demo

Related

How do I send data from a WYSIWYG to a database (Vue.js)

I just started using the Vue2Editor with the intention to replace the many forms that I use to send text and image data to my Firebase database.
My problem is that I cannot get it to add the data entered in the editor.
When using forms, I would just attach an event handler to the form itself and make a function that allowed the transfer.
Example:
<form #submit.prevent="addText">
<label for="fname">First name:</label>
<input type="text" id="fname" name="fname" v-model="fname">
</form>
<button type="submit" variant="success">Save</button>
But when using the Vue2Editor, I do not get any form tags.
I just get a "vue-editor" tag. I tried adding the event handler inside this tag, but nothing happens.
I don't get any errors, but the data is not transferred to the database upon submitting it.
This is the code:
<template>
<div class="container">
<div class="text_editor">
<h2>Add new content</h2>
<vue-editor #submit.prevent="addText" v-model="textblock" />
<button type="submit" class="textblock_btn" variant="success">Save</button>
</div>
</div>
</template>
<script>
import db from '#/firebase/init'
import Vue from "vue";
import Vue2Editor from "vue2-editor";
Vue.use(Vue2Editor);
export default {
name: 'textblock',
data () {
return {
textblock: null
}
},
methods: {
addText(){
db.collection('textblock').add({
textblock: this.textblock
}).then(() => {
this.$router.push({ name: 'Index' })
}).catch(err => {
console.log(err)
})
}
}
}
</script>
You can still wrap the component in a form as the WYSIWYG editor's data is bound to the v-model property.
<form #submit.prevent="addText">
<div class="text_editor">
<h2>Add new content</h2>
<vue-editor v-model="textblock" />
<button type="submit" class="textblock_btn" variant="success">Save</button>
</div>
</form>
Within the addText method you now have this.textblock with the appropriate data on form submission.

React.js - How do I uniquely identify a field within a dynamically generated component?

I am currently creating a form that has an unknown number of sensor fields within it. I've gotten the front end working beautifully. However, now I want to grab the user info out of those dynamically generated component fields and I can't figure out how. Here is where I'm generating the components:
{this.state.sensors.map((item, i) => (
<UpdateSensorInfo
key={i}
sensorName={item.sensorName}
sensorLowerLimit={item.sensorLowerLimit}
sensorUpperLimit={item.sensorUpperLimit}
/>
))}
And here is the actual component itself:
import React, { Component } from "react";
import "./updateSensorInfo.css";
class UpdateSensorInfo extends Component {
render() {
return (
<div>
<div className="sensorInfoFrame">
<div className="sensorFieldBody">
<label className="sensorTextFieldLabel">Sensor Name:</label>
<input
type="text"
name="text"
placeholder=""
className="sensorTextField"
defaultValue={this.props.sensorName}
required
/>
</div>
<div className="sensorFieldBody">
<label className="sensorTextFieldLabel">Sensor Upper Limit:</label>
<input
type="number"
name="text"
placeholder=""
className="sensorTextField"
defaultValue={this.props.sensorUpperLimit}
required
/>
</div>
<div className="sensorFieldBody">
<label className="sensorTextFieldLabel">Sensor Lower Limit:</label>
<input
type="number"
name="text"
placeholder=""
className="sensorTextField"
defaultValue={this.props.sensorLowerLimit}
required
/>
</div>
</div>
</div>
);
}
}
export default UpdateSensorInfo;
I would like to uniquely identify each text field within each generated component. How can I do this?
For anyone who has a similar situation, I have found a solution that works. There is a way to use the key that is created for each of the components within each component. use the following as an id or name for the input element:
id={`${this._reactInternalFiber.key}-additionalNameHere`}
This uses the key and will allow you to loop over the inputs within the components.

I'm getting an error using React: "Invalid DOM property `for`. Did you mean `htmlFor`"

I'm creating a simple CRUD app using React for my front-end, and I'm having trouble with this error:
app.js:21988 Warning: Invalid DOM property `for`. Did you mean `htmlFor`?
Here's my code:
import React, { Component } from 'react';
import axios from 'axios';
export default class Add extends Component {
constructor()
{
super();
this.state={
blogs_name:''
}
}
render() {
return (
<div>
<form>
<div className="form-group">
<label for="blogs_name">Title</label>
<input
type="text"
className="form-control"
id="blogs_name"
value={this.state.blogs_name}
/>
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
}
I assume that it has to do something with the for property in my label.
Any help is appreciated.
When using React, you can't use the for keyword in JSX, since that's a javascript keyword (remember, JSX is javascript so words like for and class can't be used because they have some other special meaning!)
To circumvent this, React elements use htmlFor instead (see React docs for more information).
So, your render function should be (I only replaced for with htmlFor):
render() {
return (
<div>
<form onSubmit={this.onSubmit} >
<div className="form-group">
<label htmlFor="blogs_name">Title</label>
<input type="text" className="form-control" id="blogs_name"
value={this.state.blogs_name}
onChange={this.onChangeBlogsName} />
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
</div>
);
}
Replace for="" with htmlFor=""
In your case change this
<label for="blogs_name">Title</label>
To this
<label htmlFor="blogs_name">Title</label>
for is a reserved word in JavaScript this is why when it comes to HTML attributes in JSX you need to use something else, React team decided to use htmlFor respectively. You can check the list of attributes from here
Short answer, Remove
for="blogs_name"
In your case.

How to disable a Dynamic forms in Angular2

I have created a dynamic form based on data[fields] from JSON, but I need the form to be initially disabled, so that when we click on Edit then only form becomes editable.
Here is my code for form:
<div class="col-md-8 " [ngSwitch]="fieldInfo.dataTypeName">
<input *ngSwitchCase="'Text'"
class="form-control"
[(ngModel)]="pageInfoBeans.nameValueMap[fieldInfo.name]"
name="{{fieldInfo.name}}"
[required]="fieldInfo.preRequiredInd"
[maxLength]="fieldInfo.fieldSize">
<input *ngSwitchCase="'Email Address'"
type="email"
class="form-control"
[(ngModel)]="pageInfoBeans.nameValueMap[fieldInfo.name]"
name="{{fieldInfo.name}}"
[required]="fieldInfo.preRequiredInd"
[maxLength]="fieldInfo.fieldSize">
and in my component HTML which populates from above switch case :
<app-form class="" [fieldInfo]="fieldItem.fieldInfo" [pageInfoBeans]="pageInfoBeans"></app-form>
Initially set the form to disabled.
component.ts
showForm?:boolean = false;
component.html
<button (click)="showForm = !showForm">Edit</button>
<form *ngIf="showForm">
...form markup
</form>
You need to do something like this:
<button class='form-control' (click)='isEditable = !isEditable'>Edit Mode</button>
<div class="col-md-8 " *ngIf='isEditable' [ngSwitch]="fieldInfo.dataTypeName">
<input *ngSwitchCase="'Text'"
class="form-control"
[(ngModel)]="pageInfoBeans.nameValueMap[fieldInfo.name]"
name="{{fieldInfo.name}}"
[required]="fieldInfo.preRequiredInd"
[maxLength]="fieldInfo.fieldSize" />
<input *ngSwitchCase="'Email Address'"
type="email"
class="form-control"
[(ngModel)]="pageInfoBeans.nameValueMap[fieldInfo.name]"
name="{{fieldInfo.name}}"
[required]="fieldInfo.preRequiredInd"
[maxLength]="fieldInfo.fieldSize" />
</div>
[disabled]="!isEditable" where initialize isEditable = 'disabled' this could be added in both the text and email input fields.
Also in your edit button you can add a callback for click where you can set isEditable = ''.
#Directive({
selector : ["canbedisabled"]
})
export class Canbedisabled{
constructor(private el: ElementRef) {
}
#Input()
set blocked(blocked : boolean){
this.el.nativeElement.disabled = blocked;
}
}
<input formControlName="first" canbedisabled [blocked]="isDisabled">
You can solve it with a Directive. A directive named Canbedisabled and a property "blocked", for example. Write a setter for blocked and set it to nativelement.disabled property.
refer: https://github.com/angular/angular/issues/11271

Creating a Dynamic Form in a Aurelia View

Problem Overview
I have a controller with a view model which contains initially an empty array which will be used for storing 'Test Inputs'. I want to provide the user a button to add a new Test Input on the form which adds a new Test Input object to the array and displays the fields needed to edit its properties.
This works when the button is pressed for the first time (but with incorrect binding) but fails to create additional form elements when pressed again.
The Controller with View Model
import {inject} from 'aurelia-framework';
import {HttpClient, json} from 'aurelia-fetch-client';
import {Router} from 'aurelia-router';
import 'fetch';
import toastr from 'toastr';
#inject(HttpClient, Router)
export class create {
constructor(http, router) {
this.vm = {
test: {
id: 0,
description: "",
testOutput: {
id: 0,
valueType: "",
value: ""
},
testInputs: []
}
};
}
}
The user will be able to add a Test Input to the array by pressing a button which delegates to this function:
addTestInput() {
this.vm.test.testInputs.push({
argumentName: "",
valueType: "",
value: ""
});
}
This function pushes to the Test Inputs array in my view model object a new testInput object.
View
In my view I have added a repeat for binding for each object in the TestInputs array. The loop is intending to create the form elements for editing the properties of each Test Input object in the TestInputs array.
<p if.bind="vm.test.testInputs.length === 0">This test has no inputs. Click the button below to add one.</p>
<template if.bind="vm.test.testInputs.length > 0" repeat.for="testInput of vm.test.testInputs">
<div class="form-group">
<label for="testInputArgumentName${$index}">Argument Name</label>
<input value.bind="testInput.argumentName" type="text" class="form-control" id="testInputArgumentName${$index}" placeholder="Argument Name">
</div>
<div class="form-group">
<div class="form-group">
<label for="testInputValueType${$index}">Expected Value Type</label>
<select class="form-control" value.bind="testInput.valueType">
<option repeat.for="valueType of valueTypeList" value.bind="valueType">${valueType}</option>
</select>
</div>
</div>
<div class="form-group">
<label for="testInputValue${$index}">Expected Value</label>
<template if.bind="testInput.valueType === 'Boolean'">
<select class="form-control" value.bind="testInput.valueType">
<option>true</option>
<option>false</option>
</select>
</template>
<template if.bind="testInput.valueType !== 'Boolean'">
<input value.bind="testInput.value" type="text" class="form-control" id="testInputValue${$index}" placeholder="Expected Value">
</template>
</div>
</template>
<button click.delegate="addTestInput()" type="submit" class="btn btn-success">Add Test Input</button> <button type="submit" class="btn btn-success">Create Test</button>
When I first press the Add Test Input button the form elements are added to the page as expected. However if I press the button again the additional from elements for the new object added to the array are not created.
Also the fields seem to be binding to the local loop variable testInput rather than the specific object in the array.
Attempted Solutions
I have had a go using the suggestions at:
Blog Post on Dynamic Forms in Aurelia
Two Way Binding Array in Aurelia
Aurelia Gitter Chat Log - Help
from jsobell
But they don't seem to have worked for me. Anyone have any ideas?
Your problem is simple. You cannot use if and repeat on the same element. Also in your case are redundant with the p on the first line.
Simple do this:
<template repeat.for="testInput of vm.test.testInputs">
...
</template>
You can find more info here

Categories