Svelte / SvelteKit 'before:event'? - javascript

I have a custom Form.svelte component, which has its own submit handler, however I would like to expose this somewhat, to allow me to have a specific function run before the submit function is called.
A simplified version:
Form.svelte
<script>
const handleSubmit = async () => {
// Do things
}
</script>
<form on:submit={handleSubmit} class="new-form">
<slot />
</form>
customers/new/+page.svelte
<script lang="ts">
type Customer = { postcode: string, shipping_postcode: string };
let duplicateAddress = false;
const customer = { postcode: "", shipping_postcode: "" };
const beforeSubmit = () => {
if (duplicateAddress) customer.shipping_postcode = customer.postcode;
if (!customer.postcode) {
// Throw an error, for example.
}
}
</script>
<Form before:submit={beforeSubmit} data={customer}>
<input type="text" bind:value={customer.postcode} placeholder="Postcode" />
<input type="checkbox" bind:checked={duplicateAddress} />
</Form>
Is this possible and, if so, how do I implement this?

You could create an event dispatcher and emit your own event called e.g. beforeSubmit and give it a handler with on:beforeSubmit where you use the component:
<!-- Form.svelte -->
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
const handleSubmit = async () => {
dispatch('beforeSubmit');
// Do things
}
</script>
<form on:submit={handleSubmit} class="new-form">
<slot />
</form>
<!-- customers/new/+page.svelte -->
<script lang="ts">
// ...
const beforeSubmit = () => {
// ...
}
</script>
<Form on:beforeSubmit={beforeSubmit} data={customer}>
<input type="text" bind:value={customer.postcode} placeholder="Postcode" />
<input type="checkbox" bind:checked={duplicateAddress} />
</Form>

Tholle's answer shows how to do it correctly (and recommended) according to svelte way. But because of I was late and I have already started writing an answer, I will offer an alternative method as an option:
it would be possible to pass the beforeSubmit function as an argument to the Form component. This would allow in such a way as shown below, to make the execution of handleSubmit dependent on the result of the beforeSubmit execution
<script lang="ts">
import Form from './Form.svelte';
let duplicateAddress = false;
const customer = { postcode: "", shipping_postcode: "" };
const beforeSubmit = (e) => {
if (duplicateAddress) customer.shipping_postcode = customer.postcode;
e.preventDefault()
if (!customer.postcode) {
// Throw an error, for example.
alert('cancel')
return false;
}
return true;
}
</script>
<Form data={customer} beforeSubmit={beforeSubmit}>
<input type="text" bind:value={customer.postcode} placeholder="Postcode" />
<input type="checkbox" bind:checked={duplicateAddress} />
</Form>
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
export let beforeSubmit = null;
const handleSubmit = async (e) => {
alert('submit')
}
</script>
<form on:submit={beforeSubmit ? e => beforeSubmit(e) && handleSubmit(e) : handleSubmit} class="new-form">
<slot />
</form>

Related

How to pass a value in a form OnPost method React

const afterSubmission = (e, message) => {
e.preventDefault()
console.log(message);
if (message === "") return
displayMessage(message)
messageInput.value=""
}
<div id="message-container"></div>
<form onSubmit = {afterSubmission}>
<label for="message-input">Message</label>
<input type="text" id="message-input" />
<button type="submit" id="send-button">Send</button>
</form>
I have these two pieces of code, my question is how can I pass the value from the input tag in the form to the afterSubmission method?
Assuming this is the same component, an easy solution would be to use state.
Your code snippets aren't very useful, just as an FYI for future questions, but here's an example of me tracking form state.
import { useState } from "react";
export default function App() {
const [message, setMessage] = useState("test");
const handleChange = (e) => {
setMessage((old) => e.target.value);
};
const afterSubmission = (e) => {
e.preventDefault();
console.log(message);
};
return (
<div className="App">
<form onSubmit={afterSubmission}>
<input
type="text"
id="message-input"
value={message}
onChange={handleChange}
/>
<button
type="submit"
id="send-button"
>
Send
</button>
</form>
</div>
);
}
As you can see, this tracks your message in the component's state which makes it accessible in the afterSubmission function.

How to prevent adding empty task in react code?

I would like to prevent my application to prevent adding empty task. Following the code which adds new task in an array. May I ask please how to put condition to prevent adding empty task?
The application is built on Mozilla's guideline for the react app.
import React, {useState} from 'react';
function Form(props){
const [name, setName ] = useState('');
const [important, setImportant] = useState(false);
function handleChangeImportant(e){
console.log(e);
e.preventDefault();
setImportant(e.target.checked);
};
function handleChange(e) {
setName(e.target.value);
}
function handleSubmit(e){
e.preventDefault();
//alert('Moin');
props.addTask(name);
setName("");
}
return(
<form onSubmit={handleSubmit}>
<h2 className="label-wrapper">
<label htmlFor="new-todo-input" className="label__lg">
What needs to be done?
</label>
</h2>
<input
type="text"
id="new-todo-input"
className="input input__lg"
name="text"
autoComplete="off"
value={name}
onChange={handleChange}
/>
<input
type="checkbox"
id="todo-0"
value={important}
onChange={handleChangeImportant}
/> Important
<button type = "submit" className="btn btn__primary btn__lg">
Add
</button>
</form>
);
}
export default Form;
```
Check if name is not empty.
function handleSubmit(e){
e.preventDefault();
//alert('Moin');
if(name !==''){
props.addTask(name);
setName("");
}
}
function handleSubmit(e) {
e.preventDefault();
if (name === '') return;
props.addTask(name);
setName('');
}

handleSubmit function appears to be refreshing page without firing any internal logic

The handleSubmit function seems to refresh the page without firing any of the internal logic. I've set up a few console.log's along the way to test out if the internally declared const that's set to the venue property in the state would log, but nothing appears.
I've commented on various parts of the function stepwise starting with setting the scheduling variable to my Firebase schedule table.
After that, I changed the handleSubmit function from an arrow function to just handleSubmit(e) (sorry, I'm new to this so I'm not familiar with the terminology)
import React, {Component} from 'react';
import FrontNav from './nav.js';
import firebase from '../Firebase';
class FrontSchedule extends Component {
constructor () {
super();
this.state = {
venue:'',
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange = (e) => {
e.preventDefault();
this.setState ({
venue: e.target.value,
});
}
handleSubmit = (e) => {
e.preventDefault();
// let schedule = firebase.database().ref('schedule')
const item = {
venue: this.state.venue,
}
console.log(item);
// schedule.push(item);
// console.log(firebase.database().ref('schedule'));
console.log(this.state.venue);
// this.setState({
// venue:'',
// })
}
render(){
return(
<div>
<FrontNav/>
<h1>Schedule</h1>
<form>
<input type="text"
name="venue"
onChange={this.handleChange}
onSubmit={this.handleSubmit}
value={this.state.venue}/>
<button onSubmit={this.handleSubmit}> Enter Event </button>
</form>
</div>
);
}
}
export default FrontSchedule;
Herein lies the crux of the problem. The page refreshes and the input bar clears, but no error message is provided. At this point, I'm really confused about what is going on here. Any feedback is appreciated!
Let us consider the following example:-
<form>
<label>
Name:
<input type="text" name="name" />
</label>
<input type="submit" value="Submit" />
</form>
Now, when we press on submit button the default behavior to browse to a new page. If you want this behavior it works out of the box in ReactJS. But in cases where you need more sophisticated behavior like form validations, custom logic after the form is submitted you can use controlled components.
We can do so by using following:-
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
Now coming to your solution it can be implemented as follows:-
class FrontSchedule extends React.Component {
constructor () {
super();
this.state = {
venue:'',
}
/* this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this); */
}
handleChange = (e) => {
e.preventDefault();
this.setState ({
venue: e.target.value,
});
}
handleSubmit = (e) => {
event.preventDefault();
// let schedule = firebase.database().ref('schedule')
const item = {
venue: this.state.venue,
}
console.log(item);
// schedule.push(item);
// console.log(firebase.database().ref('schedule'));
console.log(this.state.venue);
// this.setState({
// venue:'',
// })
}
render(){
console.log(this.state);
return(
<div>
<h1>Schedule</h1>
<form onSubmit={this.handleSubmit}>
<input type="text"
name="venue"
onChange={this.handleChange}
onSubmit={this.handleSubmit}
value={this.state.venue}/>
<input type="submit" value="Submit"/>
</form>
</div>
);
}
}
ReactDOM.render(<FrontSchedule />, document.querySelector("#app"))
Hope it helps :)
You can read more at react documentationhere

Svelte Jest test case making document.getElementId as null

I am trying to write the unit test case for my svelte component using jest-transform-svelte library. Following is the component:
XYZ.svelte
<script>
function submit() {
var name = document.getElementById("name").value; // Here document.getElementById("name") is coming as null when run from unit test case.
dispatch("abc", {
text: name
});
}
</script>
<input id="name" value="" type="text" label="Name" />
<button label="Submit" variant="primary" on:click={submit} />
XYZ.test.js
import XYZ from "XYZ.svelte";
describe("XYZ Component", () => {
it('dispatches abc event on submit', (next) => {
const target = document.createElement('div');
const xyz = new XYZ({ target: target });
setTimeout(() => {
const button = target.querySelector("button");
button.click();
xyz.$on('abc', event => {
next();
});
}, 10);
});
});
When button.click on the test case happens, the call is triggering the submit function in XYZ.svelte but,
document.getElementById("name")
is coming as null because of which getting null pointer exception error.
Any idea what I am doing wrong?
In Svelte you can eschew document.getElementById by binding the element's reference to a variable. Try changing your component as follows:
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
let name = null;
function submit () {
dispatch("abc", {
text: name
});
}
</script>
<input bind:value={name} type="text" label="Name" />
<button label="Submit" variant="primary" on:click={submit} />

Clear an input field with Reactjs?

I am using a variable below.
var newInput = {
title: this.inputTitle.value,
entry: this.inputEntry.value
};
This is used by my input fields.
<input type="text" id="inputname" className="form-control" ref={el => this.inputTitle = el} />
<textarea id="inputage" ref={el => this.inputEntry = el} className="form-control" />
<button className="btn btn-info" onClick={this.sendthru}>Add</button>
Once I activate {this.sendthru} I want to clear my input fields. However, I am uncertain how to do so.
Also, as shown in this example, it was pointed out to me that I should use the ref property for input values. What I am unclear of is what exactly does it mean to have {el => this.inputEntry = el}. What is the significance of el in this situation?
Let me assume that you have done the 'this' binding of 'sendThru' function.
The below functions clears the input fields when the method is triggered.
sendThru() {
this.inputTitle.value = "";
this.inputEntry.value = "";
}
Refs can be written as inline function expression:
ref={el => this.inputTitle = el}
where el refers to the component.
When refs are written like above, React sees a different function object each time so on every update, ref will be called with null immediately before it's called with the component instance.
Read more about it here.
Declare value attribute for input tag (i.e value= {this.state.name}) and if you want to clear this input value you have to use this.setState({name : ''})
PFB working code for your reference :
<script type="text/babel">
var StateComponent = React.createClass({
resetName : function(event){
this.setState({
name : ''
});
},
render : function(){
return (
<div>
<input type="text" value= {this.state.name}/>
<button onClick={this.resetName}>Reset</button>
</div>
)
}
});
ReactDOM.render(<StateComponent/>, document.getElementById('app'));
</script>
I'm not really sure of the syntax {el => this.inputEntry = el}, but when clearing an input field you assign a ref like you mentioned.
<input type="text" ref="someName" />
Then in the onClick function after you've finished using the input value, just use...
this.refs.someName.value = '';
Edit
Actually the {el => this.inputEntry = el} is the same as this I believe. Maybe someone can correct me. The value for el must be getting passed in from somewhere, to act as the reference.
function (el) {
this.inputEntry = el;
}
I have a similar solution to #Satheesh using React hooks:
State initialization:
const [enteredText, setEnteredText] = useState('');
Input tag:
<input type="text" value={enteredText} (event handler, classNames, etc.) />
Inside the event handler function, after updating the object with data from input form, call:
setEnteredText('');
Note: This is described as 'two-way binding'
You can use input type="reset"
<form action="/action_page.php">
text: <input type="text" name="email" /><br />
<input type="reset" defaultValue="Reset" />
</form>
Now you can use the useRef hook to get some magic if you do not want to use the useState hook:
function MyComponent() {
const inputRef = useRef(null);
const onButtonClick = () => {
// #ts-ignore (us this comment if typescript raises an error)
inputRef.current.value = "";
};
return (
<>
<input ref={inputRef} type="text" />
<button onClick={onButtonClick}>Clear input</button>
</>
);
}
As I mentioned, if you are using useState that is the best way. I wanted to show you also this special approach.
Also after React v 16.8+ you have an ability to use hooks
import React, {useState} from 'react';
const ControlledInputs = () => {
const [firstName, setFirstName] = useState(false);
const handleSubmit = (e) => {
e.preventDefault();
if (firstName) {
console.log('firstName :>> ', firstName);
}
};
return (
<>
<form onSubmit={handleSubmit}>
<label htmlFor="firstName">Name: </label>
<input
type="text"
id="firstName"
name="firstName"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
/>
<button type="submit">add person</button>
</form>
</>
);
};
You can use useState:
import React, { useState } from 'react';
const [inputTitle, setInputTitle] = useState('');
then add value to your input component:
render() {
<input type="text" onChange={(e) => setInputTitle(e.target.value)}
value={inputTitle} />
<button onClick={handleSubmit} type="submit">Submit</button>
}
On your submit handler function:
setInputTitle('');
document.querySelector('input').defaultValue = '';
On the event of onClick
this.state={
title:''
}
sendthru=()=>{
document.getElementByid('inputname').value = '';
this.setState({
title:''
})
}
<input type="text" id="inputname" className="form-control" ref={el => this.inputTitle = el} />
<button className="btn btn-info" onClick={this.sendthru}>Add</button>
I used the defaultValue property, useRef, and onClick to achieve this.
let ref = useRef()
and then inside the return:
<input type="text" defaultValue="bacon" ref={ref} onClick={() => ref.current.value = ""} />
also if you want to use onChange for the input it wouldn't require any more configuration and you can just use it. If you want to have a dynamic defaultValue then you absolutely can, with useState.
A simple way to reset the input in React is by implementing the onBlur inside the input.
onBlur={cleanSearch}
ej:
const [search, setSearch] = useState('')
const handleSearch = ({target}) =>{
setSearch(target.value)
}
const cleanSearch = () =>setSearch('')
<input
placeholder="Search…"
inputProps={{ 'aria-label': 'search' }}
value={search}
onChange={handleSearch}
onBlur={cleanSearch}
/>
The way I cleared my form input values was to add an id to my form tag.
Then when I handleSubmit I call this.clearForm()
In the clearForm function I then use document.getElementById("myForm").reset();
import React, {Component } from 'react';
import './App.css';
import Button from './components/Button';
import Input from './components/Input';
class App extends Component {
state = {
item: "",
list: []
}
componentDidMount() {
this.clearForm();
}
handleFormSubmit = event => {
this.clearForm()
event.preventDefault()
const item = this.state.item
this.setState ({
list: [...this.state.list, item],
})
}
handleInputChange = event => {
this.setState ({
item: event.target.value
})
}
clearForm = () => {
document.getElementById("myForm").reset();
this.setState({
item: ""
})
}
render() {
return (
<form id="myForm">
<Input
name="textinfo"
onChange={this.handleInputChange}
value={this.state.item}
/>
<Button
onClick={this.handleFormSubmit}
> </Button>
</form>
);
}
}
export default App;

Categories