form onSubmit method do not work in react.js - javascript

The form onSubmit method(_updateThing) is not fired in my react.js app.
The source code is like the following.
I think the problem is easy, but I spend lots of time to check it , can't solve it.Please help me.
what is wrong with my code:
export default React.createClass({
displayName: 'ThingContainer',
statics: {
load: function (context) {
return ThingActions.getData(context);
}
},
mixins: [ContextMixin, MaterialRebindMixin],
getInitialState() {
return getThings();
},
_updateThing(e) {
alert(1);
e.preventDefault();
},
_setChangedText(event) {
alert('change');
},
render() {
return (
<div>
<div>
<div>
<h2>Title</h2>
</div>
<form onSubmit={this._updateThing}>
<div >
<Label htmlFor="changeQuantity" text="" />
<Input id="changeQuantity" name="changeQuantity" type="text" onChange={this._setChangedText} />
</div>
<div className="form-footer">
<div style={{float: 'right'}}>
<input type="submit" value="submit" />
</div>
</div>
</form>
</div>
</div>
);
}
});
I changed "form onSubmit={this._updateThing}" into "form onSubmit={this._updateThing.bind(this)}", but nothing changed.
I also using Chrome dev console to check html source,onSubmit method(_updateThing) is not shown in the html source.
Capture
Thanks in advances.

The problem is that the context of this is not being preserved. If you are using React.createClass, this is automatically bound (source) which may throw you for a loop if you are a React dev moving to ES6 classes. With ES6 class constructor syntax, this is not the case and you must bind your own methods when appropriate. The most common way of doing this would be to bind(this) within your JSX.
For example, instead of
onSubmit={this._updateThing}
try
onSubmit={this._updateThing.bind(this)}

OKay,I found the reason!
I am using server side rendering(React.renderToString) to render the HTML for the component.
So the component is only rendered, but not mounted, so any methods related to mounting are not called.
Detail:
https://facebook.github.io/react/docs/top-level-api.html#reactdomserver.rendertostring

Related

Vue3 - add event listeners like #click, #blur to elements / components dynamically

I'm currently building a "form kit" for vue, so I can use a single FormComponent for multiple different forms and configure the entire form with a configObject. This configObject could look something like that:
configObj = {
name: 'SampleForm',
classes: ['sample-form', 'and', 'more', 'classes']
groups: [
{
name: 'email-address',
classes: ['sample-class'],
input: {
type: 'text',
},
label: {
value: 'E-Mail'
},
additionalHtml: [
{
tag: 'button', //this should render a button: <button></button>
classes: ['additional', 'classes'],
value: 'OK', //the button text
events: {
click() { //the logic to execute on click }
}
}
]
]
Based on that configObjet I want vue to render the individual forms, in that case:
<form id="SampleForm" class="sample-form and more classes">
<div class="form-group email-address sample-class">
<label>E-Mail:</label>
<input type="text" />
<button class="additional classes">Ok</button>
</div>
</form>
The FormComponent imports the components for every formGroup, i.e. FormGroupText, FormGroupSelect, ...
And the template for the FormGroupText component looks like that:
<template>
<div class="form-group">
<label>{{ config.label.value }}</label>
<input :type="config.input.type" />
<component v-for="element in config.additionalHtml" :key="element.tag" :is="element.tag"></component>
Now what's missing here are the events! And that's the question - can I dynamically add event listeners here?
I could just add
#click="element['click']
but then I had to add every possible event here. I want just the events specified in the configObject. I'm really not sure if this is possible.
Does someone has an idea how I could achieve this? Or also something similar, I simply don't want to code every single form I need - i want a reusable kit I can use to build forms.
Thanks in advance :)
Try binding $listeners with v-on="eventsObject"
In your case that would look like this:
<component
v-for="element in config.additionalHtml"
:key="element.tag"
:is="element.tag"
v-on="element.events" // add this line
></component>
PS
I would suggest changing the :key="element.tag" to something else.
You can have more of the same tag in a loop, and then this will couse an error of duplicated key. Any way good luck 😉

v-on:change not calling method in nuxt.js

This seems like a simple thing to do so I'm not exactly sure what I'm doing wrong here, I followed this question how to fire an event when v-model changes
but I cant seem to get it to work..
This is my component
<template>
<div>
<input type="text" v-model="searchTerm" v-on:change="search" />
</div>
</template>
<script>
export default {
data() {
return {
searchTerm: ''
}
},
methods: {
search() {
console.log(this.searchTerm);
}
}
}
</script>
now basically what I'm trying to do is when a user starts typing in the input, fire this event and console.log() the updated searchTerm, but when I start typing nothing is logged to the console??
Am I doing something wrong? Is this not how you listen to v-model changes in nuxt?
Try to use #input instead of #change event like so :
<template>
<div>
<input type="text" v-model="searchTerm" v-on:input="search" />
</div>
</template>

Svelte: Replace nested component by changing binded variable

I am writing Svelte project, where I have Message component which represents some js object.
There is ability to edit this object. For this purpose I desided to use two nested component MessageEditable and MessageReadable.
They should replace each other, depending on Message component state.
The problem is that when I am trying to save result of editing and
change MessageEditable to MessageReadable by setting isEditing property to false I get error:
image of error from console
Did I make a mistake or this is normal behavior? Is binding a good approach or there is more optimal for exchanging of values with parent components?
Message:
<div class="message">
{#if isEditing}
<MessageEditable bind:message bind:isEditing />
{:else}
<MessageReadable {message}/>
{/if}
<div class="message__controllers">
<button on:click="set({isEditing: true})">Edit</button>
</div>
</div>
<script>
import MessageEditable from './MessageEditable.html';
import MessageReadable from './MessageReadable.html';
export default {
components:{
MessageEditable,
MessageReadable,
},
data:() => ({
message:{
id: '0',
text: 'Some message text.'
},
isEditing: false,
}),
}
</script>
MessageEditable:
<form class="message-editable" on:submit>
<label><span >text</span><input type="text" bind:value=message.text required></label>
<label><span>id</span><input type="text" bind:value=message.id required></label>
<div><button type="submit">Save</button></div>
</form>
<script>
export default {
events:{
submit(node){
node.addEventListener('submit', (event) => {
event.preventDefault();
this.set({isEditing: false});
});
},
},
};
</script>
MessageReadable:
<div class="message-readable">
<p><span>text: </span>{message.text}</p>
<p><span>id: </span>{message.id}</p>
</div>
Its probably better to use a method than a custom event handler since you are performing actions on submit. I tested this code in the REPL and didn't experience the error you are getting. I changed your events to a methods property and removed the node functionalities.
<form class="message-editable" on:submit="save(event)">
<label><span >text</span><input type="text" bind:value=message.text required></label>
<label><span>id</span><input type="text" bind:value=message.id required></label>
<div><button type="submit">Save</button></div>
</form>
<script>
export default {
methods: {
save(event){
event.preventDefault();
this.set({isEditing: false});
},
},
};
</script>
https://svelte.technology/repl?version=2.10.0&gist=d4c5f8e3864856d27a3aa8cb5b2e8710

Changing vue instance variables from within component

I've been teaching myself Vue.js, and have been utilising components to increase modularity.
One thing that I am struggling with is manipulating variables in the Vue instance from within the component. I have got it working well with v-model within a component by passing the variable in the jade file as a prop
eg loginform(slot="login-form" v-bind:form-submit="loginSubmit" v-bind:login-data="loginData")
Where loginData contains variables username & password which are 'v-modelled' to the inputs within the component. Then in the component template:
<input type="password" class="text-field" v-model="formData.password" />
However I have a tooltip component that I am wanting to use twice: One for the username field & one for the password field. The visibility of these tooltips are given by tooltips.username.vis and tooltips.password.vis respectively.
I can't seem to pass that variable as a prop in order to manipulate without getting the avoid manipulating props warning, despite v-model within the component not giving these warnings. The tooltip component is given below:
Vue.component('tooltip', {
props: ['show', 'message', 'click'],
template:
<transition name="shrink">
<div v-show="show" v-on:click="click" class="tooltip">
<div class="tooltip-arrow"></div>
<div class="tooltip-container">{{message}}</div>
</div>
</transition>
});
Does anyone have any idea on how I can achieve the desired affect (Hiding the tooltip on mouse click). I have tried passing a method as the click prop that has different arguments based on whether the tooltip is for the username or password input, however I get click undefined warnings. I could make two seperate functions but I would rather not explicitly write two functions that do the same thing.
You shouldn't attempt to modify props from within a component as Vue's warnings tell you, changes to props do not flow up from the component to the prop so any changes will be overwritten.
For what you're trying to achieve you should look into Vue's Custom Events https://v2.vuejs.org/v2/guide/components-custom-events.html
HTML
<div id="app">
<form>
<div>
<label>Username</label>
<input type="username" v-model="formData.username" />
<tooltip :show="tooltips.username.vis"
:message="tooltips.username.message" #tooltip:hide="tooltips.username.vis = false" />
</div>
<div>
<label>Password</label>
<input type="text" v-model="formData.password" />
<tooltip :show="tooltips.password.vis"
:message="tooltips.password.message" #tooltip:hide="tooltips.password.vis = false" />
</div>
</form>
</div>
JS
Vue.component('tooltip', {
props: ['show', 'message'],
template: `<transition name="shrink">
<div v-show="show" class="tooltip" #click="hide">
<div class="tooltip-arrow"></div>
<div class="tooltip-container">{{message}}</div>
</div>
</transition>`,
methods: {
hide () {
this.$emit('tooltip:hide');
},
}
});
new Vue({
el: "#app",
data: {
formData: {
username: '',
password: ''
},
tooltips: {
username: {
message: 'Fix your username',
vis: true
},
password: {
message: 'Fix your password',
vis: true
}
}
}
});
https://jsfiddle.net/10fjkoob/12/

Setting focus of an input element in vue.js

I'm trying to set the focus of an input element in Vue.js. I found some help online but none of the explanation worked for me.
Here's my code :
<template>
<form method="post" action="" v-on:submit.prevent="search">
<input type="text" placeholder="Person name" required v-model="name" v-el="nameInput" />
<input type="text" placeholder="Company" required v-model="company" v-el="domainInput" />
<input type="submit" value="Search" class="btn show-m" />
</form>
</template>
<script>
export default {
data () {
return {
contacts: [],
name: null,
company: null
}
},
ready: {
// I tried the following :
this.$$.nameInput.focus();
this.$els.nameInput.focus();
// None of them worked !
}
methods: {
search: function (event) {
// ...
// I also would like to give the focus here, once the form has been submitted.
// And here also, this.$$ and this.$els doesn't work
},
}
}
</script>
I tried this.$$.nameInput.focus(); and this.$els.nameInput.focus(); for what I could find online to target the focus, but this.$$ is undefined, and this.$els is empty.
If that can help, I'm using vue.js v1.0.15
Thank you for your help.
In vue 2.x you can solve it with a directive.
Vue.directive('focus', {
inserted: function (el) {
el.focus()
}
})
Then you can use v-focus attribute on inputs and other elements:
<input v-focus>
Another solution using Vue 2.x and ref.
You can use the ref/$refs attribute to target your input and focus it.
In the example a simple method is used which can target the inputs using the ref attribute supplied to the inputs.
Then access the $refs property on your instance to get a reference to the DOM element.
<script>
export default {
// ...
mounted: function () {
this.focusInput('nameInput');
},
methods: {
// This is the method that focuses the element
focusInput: function ( inputRef ) {
// $refs is an object that holds the DOM references to your inputs
this.$refs[inputRef].focus();
},
search: function (event) {
this.focusInput('domainInput');
},
}
}
</script>
<template>
<form method="post" action="" v-on:submit.prevent="search">
<input type="text" placeholder="Person name" required v-model="name" ref="nameInput" />
<input type="text" placeholder="Company" required v-model="company" ref="domainInput" />
<input type="submit" value="Search" class="btn show-m" />
</form>
</template>
This solution is best for a one off situation or for a reusable component. For a more global approach the directive is the way to go.
Setting focus inside a child element
(for those of you that struggled for hours like me)
Parent:
<template>
<div #click="$refs.theComponent.$refs.theInput.focus()">
<custom-input ref="theComponent"/>
</div>
</template>
Child (CustomInput.vue):
<template>
<input ref="theInput"/>
</template>
There are a couple of issues.
First of all, v-els are defined like this:
<input v-el:input-element/>
That'll turn the variable to a camelCase in the code. You can read up more on this weird functionality here.
Other than that, you should be able to access the variable through this.$els.inputElement. Mind you, it will only appear in the component that you're defining that element (or the main app itself, if you defined it there).
Secondly, the automatic focusing does not seem to be working on Firefox (43.0.4), at least on my machine. Everything works great on Chrome, and focuses as expected.
Using ref I managed to focus an Input on mounted like this.
Template :
<b-form-input v-model="query" ref="searchInput" ></b-form-input>
Javascript :
mounted(){
this.$refs.searchInput.$el.focus()
}
Vue 3.x
Use a custom directive.
app.directive('focus', {
mounted(el) {
el.focus()
}
})
Here is how you use it:
Step 1:
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.directive('focus', {
mounted(el) { // When the bound element is inserted into the DOM...
el.focus() // Focus the element
}
})
/* Optional:
Add a slight delay if the input does not focus.
app.directive('focus', {
mounted(el) { // When the bound element is inserted into the DOM...
setTimeout(() => {
el.focus() // Focus the element
}, 500)
}
}) */
await router.isReady()
app.mount('#app')
Then in your component:
Step 2:
// MyInput.vue
<input v-focus>
Vue docs
According to vue 2.x, you can also register "directive" locally in component to get autofocus.
just write directive in component:
export default {
directives: { focus: {
inserted: function (el) {
el.focus()
}
}
}
}
Then in a template, you can use the new v-focus attribute on any element, like this:
<input type="text" v-focus>

Categories

Resources