How to pass a Selector in a mixin-VUE - javascript

I'm trying to make a Mixin with which I'd like to calculate the offsetWidth of an element.
This is my Mixin:
export const boxWidth = (selector) => ({
mounted() {
selector.addEventListener(
'resize',
this.setBoxWidth
);
},
methods: {
setBoxWidth(e) {
let box = e.target;
this.myBoxWidth = box.offsetWidth;
console.log(this.myBoxWidth);
}
}
})
I trying to call this in a component:
<template>
<div
ref="visuraBox"
class="container"
>
<div class="field">
<label class="label">Name</label>
<div class="control">
<input
class="input"
type="text"
placeholder="e.g Alex Smith"
>
</div>
</div>
<div class="field ">
<label class="label">Name</label>
<div class="control">
<input
class="input"
type="text"
placeholder="e.g Alex Smith"
>
</div>
</div>
</div>
</template>
<script>
import { boxWidth } from '../mixins/boxWidth'
export default {
name: 'VisuraCatForm',
mixins: [boxWidth(this.$refs.visuraBox)],
data() {
return {
myBoxWidth: 0
}
},
created() {
const myBox = this.$refs.visuraBox
this.myBoxWidth = myBox.offsetWidth;
}
}
</script>
<style lang='scss' scoped>
#import '#/assets/design/components/_form.scss';
</style>
Obviously in this way the ref doesen't exist yet, so my answer is:
How can i pass a selector inside a mixin?
(P.S. I prefer to don't declare the mixin globally)

You're almost there...
I suggest you add a method to your mixin that handles what you already have within the mounted hook of the mixin:
listenForResize(selector) {
selector.addEventListener('resize', this.setBoxWidth);
}
Then you can call this new method from the mounted hook of your original component.

Related

vue.js: create component from string

I have two components named component-1 and component-2, along with this vue.js code:
<template>
<div id="app">
<input class="input" #keyup="update()">
<div class="render"></div>
</div>
</template>
<script>
export default {
methods:{
update () {
document.querySelector(".render").innerHTML=`<component-${
document.querySelector(".input").value
} />`
}
}
}
</script>
Whenever I run the code, the component doesn't render. Is there a way to get this component to render?
This isn't the right way to render components dynamically in Vue - instead you should use :is
<template>
<div id="app">
<input class="input" #keyup="update($event.target.value)">
<component :is="myComponent"></component>
</div>
</template>
<script>
export default {
data() {
return {
myComponent: undefined
}
},
methods:{
update (component) {
this.myComponent = component
}
}
}
</script>
A sandbox demo is here

Vue.js slots - how to retrieve slot content in computed properties

I have a problem with vue.js slots. On one hand I need to display the slot code. On the other hand I need to use it in a textarea to send it to external source.
main.vue
<template>
<div class="main">
<my-code>
<template v-slot:css-code>
#custom-css {
width: 300px
height: 200px;
}
</template>
<template v-slot:html-code>
<ul id="custom-css">
<li> aaa </li>
<li> bbb </li>
<li> ccc </li>
</ul>
</template>
</my-code>
</div>
</template>
my-code.vue
<template>
<div class="my-code">
<!-- display the code -->
<component :is="'style'" :name="codeId"><slot name="css-code"></slot></component>
<slot name="html-code"></slot>
<!-- send the code -->
<form method="post" action="https://my-external-service.com/">
<textarea name="html">{{theHTML}}</textarea>
<textarea name="css">{{theCSS}}</textarea>
<input type="submit">
</form>
</div>
</template>
<script>
export default {
name: 'myCode',
props: {
codeId: String,
},
computed: {
theHTML() {
return this.$slots['html-code']; /* The problem is here, it returns vNodes. */
},
theCSS() {
return this.$slots['css-code'][0].text;
},
}
}
</script>
The issues is that vue doesn't turn the slot content. It's an array of <VNode> elements. Is there a way to use slots inside the textarea. Or a way to retrieve slot content in the theHTML() computed property.
NOTE: I use this component in vuePress.
You need to create a custom component or a custom function to render VNode to html directly. I think that will be the simplest solution.
vnode to html.vue
<script>
export default {
props: ["vnode"],
render(createElement) {
return createElement("template", [this.vnode]);
},
mounted() {
this.$emit(
"html",
[...this.$el.childNodes].map((n) => n.outerHTML).join("\n")
);
},
};
</script>
Then you can use it to your component
template>
<div class="my-code">
<!-- display the code -->
<component :is="'style'" :name="codeId"
><slot name="css-code"></slot
></component>
<slot name="html-code"></slot>
<!-- send the code -->
<Vnode :vnode="theHTML" #html="html = $event" />
<form method="post" action="https://my-external-service.com/">
<textarea name="html" v-model="html"></textarea>
<textarea name="css" v-model="theCSS"></textarea>
<input type="submit" />
</form>
</div>
</template>
<script>
import Vnode from "./vnode-to-html";
export default {
name: "myCode",
components: {
Vnode,
},
props: {
codeId: String,
},
data() {
return {
html: "", // add this property to get the plain HTML
};
},
computed: {
theHTML() {
return this.$slots[
"html-code"
]
},
theCSS() {
return this.$slots["css-code"][0].text;
},
},
};
</script>
this thread might help How to pass html template as props to Vue component

raising an event from a child component to a container component in Vue.js?

I have a child Vue component (SearchBox.Vue) that is a simple text box that will fire filterByEvent when the user hits the Enter key. In the container components, I should get the string in the text box and execute a function filterList to filter the customer's list based on the name entered. My question is how to pass the text entered in the text box to the parent component?
SearchBox.vue
<template>
<div class="SearchBox">
<input type="text" id="SearchBox" placeholder="Search"
v-on:keyup.13="$emit('FilterByEvent', $event.target.value)" :value="value" >
<font-awesome-icon icon="search" />
</div>
</template>
<script>
export default {
name: 'SearchBox',
data() {
return {
value: ''
}
}
}
</script>
<style scoped>
.SearchBox {
display: flex;
}
</style>
the container code
<template>
<div>
<div>
<SearchBox #FilterByEvent="filterList(value)"></SearchBox>
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
import SearchBox from '#/components/templates/SearchBox.vue'
#Component({
components: {
SearchBox
}
})
export default class ContactList extends Vue {
data() {
return {
filterString: ''
}
}
filterList(filterStr :string) {
this.$data.filterString = filterStr;
}
}
</script>
The event is not working because the component is listening on a camel case event. Here is the explanation on why this doesn't work. Plus the #FilterByEvent expects a function to execute. So use instead
<SearchBox #filter-by-event="filterList"></SearchBox>
and emit the event this way:
<div class="SearchBox">
<input type="text" id="SearchBox" placeholder="Search"
v-on:keyup.13="$emit('filter-by-event', $event.target.value)" :value="value" >
I have found that passing the $event itself will pass the text box value
<SearchBox #FilterByEvent="filterList($event)"></SearchBox>

Property or method "sendResetMail" is not defined on the instance but referenced during render

I'm relatively new to Vue and super stuck with this error message when I try to make this reset email modal work in my Vue project:
Property or method "sendResetMail" is not defined on the instance but
referenced during render. Make sure that this property is reactive,
either in the data option, or for class-based components, by
initializing the property.
I have no idea what I need to do to make this work. I followed the Vue documentation and declared resetEmail in the data option.
ForgotPassword.vue:
<template>
<section>
<a #click="isComponentModalActive = true">
Forgot Password?
</a>
<b-modal :active.sync="isComponentModalActive" has-modal-card>
<modal-form v-bind="resetEmail"></modal-form>
</b-modal>
</section>
</template>
<script>
import firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/firestore'
const ModalForm = {
props: ['resetEmail'],
template: `
<form #submit.prevent="sendResetMail">
<div class="modal-card" style="width: auto">
<header class="modal-card-head">
<p class="modal-card-title">Forgot password?</p>
</header>
<section class="modal-card-body">
<b-field label="Email">
<b-input
type="email"
:value="resetEmail"
placeholder="Your email"
required>
</b-input>
</b-field>
</section>
<footer class="modal-card-foot">
<button class="button" type="button" #click="$parent.close()">Close</button>
<button class="button is-primary">Reset</button>
</footer>
</div>
</form>
`
}
export default {
components: {
ModalForm
},
data () {
return {
isComponentModalActive: false,
resetEmail: '',
feedback: null
}
},
methods: {
sendResetMail () {
var auth = firebase.auth()
var emailAddress = this.resetEmail
auth.sendPasswordResetEmail(emailAddress).then(function () {
// Email sent.
console.log('email send')
}).catch(function (error) {
// An error happened.
this.feedback = error.message
console.log(error.message)
})
}
}
}
</script>
This is the file where I use the ForgotPassword.vue component,
Login.vue:
<template>
<section class="section">
<div class="container">
<div class="columns">
<div class="column"></div>
<div class="column is-half">
<div class="box">
<h1 class="title">Login</h1>
<form #submit.prevent="login">
<b-field label="Email" :message="feedback" :type="type">
<b-input placeholder="Email" icon="email" type="email" v-model="email">
</b-input>
</b-field>
<b-field label="Password" :message="feedback" :type="type">
<b-input placeholder="Password" type="password" icon="textbox-password" password-reveal v-model="password">
</b-input>
</b-field>
<button type="submit" class="button is-primary is-fullwidth">Login</button>
<div class="field">
<div class="control">
<p class="control has-text-centered">
<ForgotPassword/>
</p>
</div>
</div>
<div class="field">
<div class="control">
<p class="control has-text-centered">
Don't have an account?
<a href="/register">
<router-link :to="{ name: 'Signup' }">
Signup
</router-link>
</a>
</p>
</div>
</div>
</form>
</div>
</div>
<div class="column"></div>
</div>
</div>
</section>
</template>
<script>
import firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/firestore'
import ForgotPassword from '#/components/auth/ForgotPassword'
export default {
name: 'login',
metaInfo: {
// specify a metaInfo.title, this title will be used
title: 'Login',
// all titles will be injected into this template
titleTemplate: '%s | Wterdrops.com'
},
components: {
ForgotPassword
},
data () {
return {
email: null,
password: null,
feedback: null,
type: null
}
},
methods: {
login () {
if (this.email && this.password) {
firebase
.auth()
.signInWithEmailAndPassword(this.email, this.password)
.then(cred => {
this.$router.push({
name: 'Dashboard'
})
})
.catch(err => {
this.feedback = err.message
})
this.feedback = null
} else {
this.feedback = 'Please fill in both fields.'
this.type = 'is-danger'
}
}
}
}
</script>
<style>
.login {
max-width: 400px;
margin-top: 60px;
}
</style>
I would be very grateful if someone can explain to me what I'm missing :)
You are referencing
<form #submit.prevent="sendResetMail">
inside your ModalForm component.
The problem is that this template is going to look for the method sendResetMail on that ModalForm component template when it gets rendered since you the code is referencing it. However this sendResetMail method is not directly associated on that.
You can consider using a mix-in if you need to use the sendResetMail in many places , or maybe just move that method directly to the same component "ModalForm" that is referencing it.
You can also look into for example eventBus to trigger the method by emitting an event.
The simplest option if you only need to call it from the MOdalForm component is to just move the sendResetMail direcly to that component. I believe that would probably fix your issue.

Vue.js: How the child components mutate the parent's state?

In vuejs.org, there said:
The two-way binding will sync the change of child’s msg property back to the parent’s parentMsg property. (Here is the link.)
But I'm confused that how could I change child's property so that this change can be synced back to its parent?
router
// Define rule of router.
router.map({
'/categories': {
// The List.vue
component: CategoryList,
subRoutes: {
// ... some rules ...
'/add': {
// The DetailAdd.vue
component: CategoryDetailAdd
}
}
}
});
List.vue (the parent)
<template>
<tab v-bind:tabs="tabs" :active="active"></tab>
<div class="col-lg-12">
<router-view :categories="categories"></router-view>
</div>
</template>
<script>
var Tab = require('../common/Tab.vue');
export default{
components:{
tab: Tab
},
data() {
return {
categories: [],
tabs: [],
active: '1'
};
},
ready() {
this.$http.get('/categories').then((response) => {
// success
this.$set('categories', response.data.categories);
this.$set('tabs', response.data.tabs);
this.$set('active', response.data.active);
}, (response) => {
// error
})
}
}
</script>
DetailAdd.vue (the child)
<template>
<form class="form-horizontal" method="post" action="/categories/add">
<div class="form-group">
<label for="name" class="col-md-2 control-label">name</label>
<div class="col-md-10">
<input id="name" type="text" class="form-control" name="name" value="" />
</div>
</div>
<div class="form-group">
<label for="category_id" class="col-md-2 control-label">superiror</label>
<formselect></formselect>
</div>
<div class="form-group">
<label for="sort_order" class="col-md-2 control-label">sort</label>
<div class="col-md-10">
<input id="name" type="text" class="form-control" name="sort_order" value="" />
</div>
</div>
<formbutton></formbutton>
</form>
</template>
<script>
var FormSelect = require('../common/FormSelect.vue');
var FormButton = require('../common/FormButton.vue');
export default{
components: {
formselect: FormSelect,
formbutton: FormButton
}
}
$(function() {
$('.nav-tabs').on('ready', function() {
$('.nav-tabs li').attr('class', '');
$('.nav-tabs li:last').attr('class', 'active');
});
});
</script>
I just want to mutate the active property in parent (List.vue), how to achieve this?
Thank all of you!
The two-way binding works as you might think it does: when you change a property in the parent, it gets changed in the children, and vice versa. Take a look at this as an example: https://jsfiddle.net/u0mmcyhk/1/, the children is able to change the state of the parent. If you remove .sync from the parent template, it stops working.
Having said that, .sync will be deprecated on 2.0, in favour of communication (broadcast, dispatch) or some state management like vuex.
More information: https://vuejs.org/api/#v-bind

Categories