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

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>

Related

Vue - Keep input focused on route change

I have a TopNavBar component, that is present on every route. This component includes a search input field. When a user clicks on the input field the route changes from /bar to /foo but input focus is lost. How can I (re)focus on the input?
TopNavBar.vue
<template>
<input type="search" name="search-library" v-focus ref="searchInput" #focus="initSearch". />
</template>
<script setup>
const searchInput = ref(null);
<input type="search" name="search-library" v-focus ref="searchInput" #focus="initSearch". />
function initSearch() {
if (router.currentRoute.value.name != "/foo") {
router.push({ path: "/foo", query: { initSearch: true }, key: route.fullPath });
}
}
watch(
() => router.currentRoute.value.path,
(newRoute) => {
if (newRoute == "/foo") {
searchInput.value.focus();
}
}
);
</script>
I'm using Vue3 and Nuxt3. v-focusz directive is declared globally in /plugins` folder and works as expected.
Update
TopNavBar is inside Nuxt 3 layout. Also, upon further investigation I've realised that the input does focus on route change but immediately loses it again.
You can achieve this by using $refs, Attach a reference on input element and then call focus method on it.
In template:
<parent-component>
<search-component ref="searchComponentRef" />
</parent-component>
In script:
mounted() {
this.$refs.searchComponentRef.$el.focus();
}

Vue JS emit event to child component from parent component not triggering

I'm working in a Nuxt JS project. I have several components, and one of my components called ComplianceOptoutRawText includes a child component called ComplianceOptoutViaExternalService, and it's this child component where I'm using a v-on to listen for an $emit event from the ComplianceOptoutRawText so I can toggle a variable, but I'm not seeing it at all in my console logs and need to know what I'm doing wrong and need to change.
Here's my markup with everything that's appropriate:
ComplianceOptoutRawText
<template>
<div>
<div>
<div class="tw-hidden md:tw-block tw-font-bold tw-mb-4">
<ComplianceOptoutViaExternalService>
<template #trigger>
<button #click="$emit('shown-optout-intent', true)" type="button">here</button>
</template>
</ComplianceOptoutViaExternalService>
</div>
</div>
</div>
</template>
And then the child component itself:
ComplianceOptoutViaExternalService
<template>
<div>
<slot name="trigger"></slot>
<article v-show="wantsToOptOut" v-on:shown-optout-intent="hasShownOptoutIntent" class="tw-bg-white tw-p-4 md:tw-p-6 tw-rounded-xl tw-text-gray-800 tw-border tw-border-gray-300 tw-text-left tw-mt-4">
<validation-observer v-if="!didOptOut" ref="optoutServiceForm" key="optoutServiceForm" v-slot="{ handleSubmit }" tag="section">
<form>
...
</form>
</validation-observer>
</article>
</div>
</template>
<script>
export default {
data () {
return {
wantsToOptOut: false,
isOptingOut: false,
didOptOut: false
}
},
methods: {
/*
** User has shown opt out intent
*/
hasShownOptoutIntent (value) {
this.wantsToOptOut = !this.wantsToOptOut
}
}
}
</script>
Note that my child component uses a slot, this is so I can position everything as needed in the parent component, but at it's core, I have a parent component emitting a value and then listening for it via v-on:shown-optout-intent="hasShownOptoutIntent" which runs the hasShownOptoutIntent method.
But I never see anything from the button, even if I console.log this. What am I missing?
I'm doing something similar with an embedded component, so it's perhaps worth trying:
<button #click="$emit('update:shown-optout-intent', true)" type="button">here</button>
... and then:
v-on:shown-optout-intent.sync="hasShownOptoutIntent"
As an aside, it's safe to remove v-on and use: :shown-optout-intent.

Vue2: Use form component with input type textarea to display AND edit data (without directly manipulating props)

I am building an MVP and this is the first time I do web development. I am using Vue2 and Firebase and so far, things go well.
However, I ran into a problem I cannot solve alone. I have an idea how it SHOULD work but cannot write it into code and hope you guys can help untangle my mind. By now I am incredibly confused and increasingly frustrated :D
So lets see what I got:
Child Component
I have built a child component which is a form with three text-areas. To keep it simple, only one is included it my code snippets.
<template>
<div class="wrap">
<form class="form">
<p class="label">Headline</p>
<textarea rows="2"
v-model="propHeadline"
:readonly="readonly">
</textarea>
// To switch between read and edit
<button
v-if="readonly"
#click.prevent="togglemode()">
edit
</button>
<button
v-else
type="submit"
#click.prevent="togglemode(), updatePost()"
>
save
</button>
</form>
</div>
</template>
<script>
export default {
name: 'PostComponent'
data() {
return {
readonly: true
}
},
props: {
propHeadline: {
type: String,
required: true
}
},
methods: {
togglemode() {
if (this.readonly) {
this.readonly = false
} else {
this.readonly = true
}
},
updatePost() {
// updates it to the API - that works
}
}
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
And my parent component:
<template>
<div class="wrap">
<PostComponent
v-for="post in posts"
:key="post.id"
:knugHeadline="post.headline"
/>
</div>
</template>
<script>
import PostComponent from '#/components/PostComponent.vue'
export default {
components: { PostComponent },
data() {
return {
posts: []
}
},
created() {
// Gets all posts from DB and pushes them in array "posts"
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
Current Status
So far, everything works. I can display all posts and when clicking on "edit" I can make changes and save them. Everything gets updated to Firebase - great!
Problem / Error Message
I get the following error message:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value.
As the error says I should use a computed property based on the props value. But how can I achieve that?
Solution Approach
I believe I have to use a computed getter to return the prop value - how to do that?
And then I have to use the setter to emit an event to the parent to update the value so the prop passes it back down - how to do that?
I have found bits and pieces online but by now all I see is happy families passing around small packages of data...
Would be really thankful for a suggestion on how to solve this one! :)
Thanks a lot!
This error shows because of your v-model on texterea which mutate the prop, but in vue it is illegal to mutate props :
<textarea rows="2"
v-model="propHeadline"
:readonly="readonly">
</textarea>
So, what you could do is to use this created() lifecycle hook and set the propHeadline prop as data :
<script>
export default {
name: 'PostComponent'
data() {
return {
readonly: true,
headline: ""
}
},
props: {
propHeadline: {
type: String,
required: true
}
},
created() {
this.headline = this.propHeadline
}
}
</script>
An then, update the new variable on your textarea :
<textarea rows="2"
v-model="headline"
:readonly="readonly">
</textarea>

form onSubmit method do not work in react.js

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

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