Expected array got function. Passing function into component vuejs - vue.js

I am trying to pass a function into my component and I keep getting this error back. "Invalid prop: type check failed for prop "form_type". Expected Array, got Function." My function returns an array so I am a little lost on how to fix this.
The function I am referencing is selectedType & the component in question is ChildTab
<template>
<div class="row">
<q-field
label="Contact Type"
:labelWidth="3"
error-label="Please select a contact type"
:error="!!failed_validations.contact_type"
>
<q-select v-model="contact_type" :options="contact_types"/>
</q-field>
</div>
<ChildTabs
:form_type="selectedType"
/>
<q-field class="float-right">
<q-btn color="faded" v-on:click="goBack()">Cancel</q-btn>
<q-btn color="green-6" v-on:click="selectedType()">Submit</q-btn>
</q-field>
</div>
</div>
</template>
<script>
'use strict';
import ChildTabs from '../tabs';
export default {
name: 'contacts-view',
data: function () {
return {
contact_type: '',
contact_types: [
{
label: 'Pregnancy',
value: 'pregnancy',
form_type: [
'BreastFeeding',
'Pregnancy'
]
},
{
label: 'Post Partum (Includes Birth)',
value: 'postpartum',
form_type: [
'Birth',
'BreastFeeding',
'PostPartum'
]
},
{
label: '1 - 2 Month',
value: '1_2_months',
form_type: [
'BreastFeeding',
'DuoMonths'
]
},
{
label: '6 Month',
value: '6_months',
form_type: [
'SixMonth'
]
}
],
}
},
props: {
},
computed: {
selectedType: function ()
{
var values = this.contact_types.map(function(o) { return o.value });
var index = values.indexOf(this.contact_type);
this.selectedForms = this.contact_types[index].form_type
// console.log(this.selectedForms);
return this.selectedForms;
}
},
methods: {
},
created: function () {
this.selectedType();
},
components: {
ChildTabs
}
}
</script>

As you try to call selectedType on click "Submit", maybe you should call it as a method.
Inside selectedType you bind a selectedForms property. Why don't you just initialize this property inside data as an empty array and pass it as a props of your ChildTabs component ?
<template>
<div class="row">
<ChildTabs :form_type="selectedForms" />
</div>
</template>
export default {
name: 'contacts-view',
data: function () {
return {
selectedForms: [],
// ...
}
},
methods: {
selectedType() {
var values = this.contact_types.map(function(o) { return o.value });
var index = values.indexOf(this.contact_type);
this.selectedForms = this.contact_types[index].form_type
}
},
//...
}
Fiddle example

What you bind as a prop in a component goes as same in the component. So as you're referencing selectedType in your ChildTabs component - the method selectedType will be received by ChildTabs as a prop. So either you edit your propType in ChildTabs component and invoke that passed method as needed or you call the selectedType method on the fly when passed in as a prop like
<ChildTabs :form_type="selectedType()" />
This will call that method then and will bind the resulting array as prop

Related

Return model value formatted with Vue.js

I have a table in Vue.js application, listing a URL and an Id. This URL is defined by the user, so I created and input component, with a input text, using the URL as value and the Id as parameter. The v-model of this component is an array, where I need to store data as JSON objects like this:
{
"id": 1,
"url": "www.some-url.com"
}
How can I catch changes in the url field and return for its parent to append in an array?
Component:
<template>
<div class="row">
<div class="col-md-12">
<input type="text"
class="form-control"
v-model="value.url">
</div>
</div>
</template>
<script>
export default {
name: 'inputUrl',
props: {
url: {
type: [String],
description: 'URL'
},
id: {
type: Number,
description: 'Id'
}
},
components: {
}
data() {
return {
value: {
id: this.id,
url: this.default
}
};
},
methods: {
},
mounted() {
},
watch: {
}
}
</script>
Usage:
<inputUrl
:id="1"
url="www.some-url.com"
v-model="array">
</inputUrl>
I passed the array variable to the InputUrl component then used v-on directive to passing the current input value to a custom function for appending the new values to the array variable.
Here an example.

Replace tag dynamically returns the object instead of the contents

I'm building an chat client and I want to scan the messages for a specific tag, in this case [item:42]
I'm passing the messages one by one to the following component:
<script>
import ChatItem from './ChatItem'
export default {
props :[
'chat'
],
name: 'chat-parser',
data() {
return {
testData: []
}
},
methods : {
parseMessage(msg, createElement){
const regex = /(?:\[\[item:([0-9]+)\]\])+/gm;
let m;
while ((m = regex.exec(msg)) !== null) {
msg = msg.replace(m[0],
createElement(ChatItem, {
props : {
"id" : m[1],
},
}))
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
}
return msg
},
},
render(createElement) {
let user = "";
let msg = this.parseMessage(this.$props.chat.Message, createElement)
return createElement(
'div',
{
},
[
// "hello",// createElement("render function")
createElement('span', '['+ this.$props.chat.Time+'] '),
user,
msg,
]
)
}
};
</script>
I thought passing createElement to the parseMessage method would be a good idea, but it itsn't working properly as it replaces the tag with [object object]
The chatItem looks like this :
<template>
<div>
<span v-model="item">chatITem : {{ id }}</span>
</div>
</template>
<script>
export default {
data: function () {
return {
item : [],
}
},
props :['id'],
created() {
// this.getItem()
},
methods: {
getItem: function(){
obj.item = ["id" : "42", "name": "some name"]
},
},
}
</script>
Example :
if the message looks like this : what about [item:42] OR [item:24] both need to be replaced with the chatItem component
While you can do it using a render function that isn't really necessary if you just parse the text into a format that can be consumed by the template.
In this case I've kept the parser very primitive. It yields an array of values. If a value is a string then the template just dumps it out. If the value is a number it's assumed to be the number pulled out of [item:24] and passed to a <chat-item>. I've used a dummy version of <chat-item> that just outputs the number in a <strong> tag.
new Vue({
el: '#app',
components: {
ChatItem: {
props: ['id'],
template: '<strong>{{ id }}</strong>'
}
},
data () {
return {
text: 'Some text with [item:24] and [item:42]'
}
},
computed: {
richText () {
const text = this.text
// The parentheses ensure that split doesn't throw anything away
const re = /(\[item:\d+\])/g
// The filter gets rid of any empty strings
const parts = text.split(re).filter(item => item)
return parts.map(part => {
if (part.match(re)) {
// This just converts '[item:24]' to the number 24
return +part.slice(6, -1)
}
return part
})
}
}
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<template v-for="part in richText">
<chat-item v-if="typeof part === 'number'" :id="part"></chat-item>
<template v-else>{{ part }}</template>
</template>
</div>
If I were going to do it with a render function I'd do it pretty much the same way, just replacing the template with a render function.
If the text parsing requirements were a little more complicated then I wouldn't just return strings and numbers. Instead I'd use objects to describe each part. The core ideas remain the same though.

[Vue warn]: Property or method "names" is not defined on the instance but referenced during render

I'm setting up a Vue.js project and connecting it to Firebase for the real time database.
Problem: I am able to save the data to the Firebase database however I am not able to render it to the view.
Error Message:
[Vue warn]: Property or method "names" is not defined on the instance
but referenced during render.
I have tried to adjust the vue instance "names" property by adding it the data function instead of making it a separate property in the instance, but that is not working.
<div id="app">
<label for="">Name</label>
<input type="text" name="" id="" v-model="name">
<button #click="submitName()">Submit</button>
<div>
<ul>
<li v-for="personName of names"
v-bind:key="personName['.key']">
{{personName.name}}
</li>
</ul>
</div>
</div>
<script>
import {namesRef} from './firebase'
export default {
name: 'app',
data () {
return {
name: "levi",
}
},
firebase: {
names: namesRef
},
methods: {
submitName() {
namesRef.push( {name:this.name, edit:false} )
}
}
}
</script>
<style>
Expected Result: Data saved to Firebase is rendered on the view
Actual result:
[Vue warn]: Property or method "names" 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.
Essentially, you have an incorrect attribute in your Vue instance.. You need to move firebase into data..
([CodePen])
I was unable to get this working in a Stack Snippet..
~~~THE FIX~~~
VUE/JS
firebase.initializeApp({
databaseURL: "https://UR-DATABASE.firebaseio.com",
projectId: "UR-DATABASE"
});
const database = firebase.database().ref("/users");
const vm = new Vue({
el: "#app",
data: {
firebase: {
names: []
},
name: "SomeName"
},
methods: {
getFirebaseUsers() {
this.firebase.names = [];
database.once("value", users => {
users.forEach(user => {
this.firebase.names.push({
name: user.child("name").val(),
id: user.child("id").val()
});
});
});
},
handleNameAdd() {
let id = this.generateId();
database.push({
name: this.name,
id: id
});
this.name = "";
this.getFirebaseUsers();
},
generateId() {
let dt = new Date().getTime();
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => {
let r = ((dt + Math.random() * 16) % 16) | 0;
dt = Math.floor(dt / 16);
return (c == "x" ? r : (r & 0x3) | 0x8).toString(16);
});
}
},
mounted() {
this.getFirebaseUsers();
}
});
HTML
<script src="https://www.gstatic.com/firebasejs/6.1.1/firebase.js"> .
</script>
<div id="app">
<label for="">Name</label>
<input type="text" name="" id="" v-model="name">
<button #click="handleNameAdd">Submit</button>
<div>
<ul>
<li v-for="(person, index) in firebase.names"
v-bind:key="person.id">
{{person.name}} | {{person.id}}
</li>
</ul>
</div>
</div>
OLD ANSWER:
This is what it should look like inside of data:
...
data() {
firebase: {
names: [],
}
}
...
Therefore, the data in your v-for would be referenced via firebase.names like:
...
<li v-for="(personName, index) in firebase.names"
:key="index"> // <<-- INDEX IS NOT THE BEST WAY TO STORE KEYS BUT ITS BETTER THAN NOTHING
//:key="personName.id // <<-- YOU COULD ALSO DO SOMETHING LIKE THAT, IF YOU HAVE A UNIQUE ID PER PERSON
{{personName.name}}
</li>
...
OPTIMAL FIX:
You could use a computed property if you wanted to automatically save/retrieve data from firebase each time a user adds a new name...as outlined in the CodePen and Code Snippet..
THE ISSUE:
<script>
import {namesRef} from './firebase'
export default {
name: 'app',
data () {
return {
name: "levi",
}
},
firebase: { // <<--- THIS IS INVALID, AND WHY IT'S NOT RENDERING
names: namesRef // CHECK YOUR CONSOLE FOR ERRORS
},
methods: {
submitName() {
namesRef.push( {name:this.name, edit:false} )
}
}
}
</script>
Try this.
You need to return the object in data
<script>
import {namesRef} from './firebase'
export default {
name: 'app',
data () {
return {
name: "levi",
firebase: {
names: namesRef
},
}
},
methods: {
submitName() {
namesRef.push( {name:this.name, edit:false} )
}
}
}
</script>
<style>
Use a computed property for names. Computed is more appropriate than data in this case, mainly because the component does not own the data. If it eventually resided in a vuex store, for instance, it would then react to external changes.
<script>
import {namesRef} from './firebase'
export default {
name: 'app',
data () {
return {
name: "levi",
}
},
computed: {
names() {
return namesRef
}
}
methods: {
submitName() {
namesRef.push( {name:this.name, edit:false} )
}
}
}
</script>
Try this
<script>
import {namesRef} from './firebase'
export default {
name: 'app',
data () {
return {
name: "levi",
}
},
cumputed: {
names: namesRef
},
methods: {
submitName() {
namesRef.push( {name:this.name, edit:false} )
}
}
}
</script>
Got mine working.
The solution is pretty simple.
Add names:[] to data object so it looks like:
...
data () {
return {
name: "levi",
names:[]
}
},
....
That's pretty much it.
Explaination
The firebase object data needs to be defined in order to use it
If you have more issues check the vuex documentation replicate that to your code.

How to fix Warning: `getFieldDecorator` will override `value`,so please don't set `value and v-model` directly and use `setFieldsValue` to set it.?

I'm coding a custom validation form component using ant-design-vue
I have changed my code nearly same as the example showed on the official website, but still got warning, the only difference is the example use template to define child component, but I use single vue file
//parent component
...some other code
<a-form-item
label="account"
>
<ReceiverAccount
v-decorator="[
'receiverAccount',
{
initialValue: step.receiverAccount,
rules: [
{
required: true,
message: 'need account',
}
]
}
]"
/>
</a-form-item>
...some other code
//child component
<template>
<a-input-group compact>
<a-select
:value="type"
#change="handleTypeChange"
>
<a-select-option value="alipay">alipay</a-select-option>
<a-select-option value="bank">bank</a-select-option>
</a-select>
<a-input
:value="number"
#change="handleNumberChange"
/>
</a-input-group>
</template>
<script>
export default {
props: {
value: {
type: Object,
default: () => {}
}
},
data() {
const { type, number } = this.value
return {
type: type || 'alipay',
number: number || ''
}
},
watch: {
value(val = {}) {
this.type = val.type || 'alipay'
this.number = val.number || ''
}
},
methods: {
handleTypeChange(val) {
this.triggerChange({ val })
},
handleNumberChange(e) {
const number = parseInt(e.target.value || 0, 10)
if (isNaN(number)) {
return
}
this.triggerChange({ number })
},
triggerChange(changedValue) {
this.$emit('change', Object.assign({}, this.$data, changedValue))
}
}
}
</script>
I expect everything is fine, but the actual is I got 'Warning: getFieldDecorator will override value, so please don't set value and v-model directly and use setFieldsValue to set it.'
How can I fix it? Thanks in advance
because I am new of ant-design-vue, after one day research, solution is change :value to v-model and remove value props in the child component
<template>
<a-input-group compact>
<a-select
v-model="type"
#change="handleTypeChange"
>
<a-select-option value="alipay">alipay</a-select-option>
<a-select-option value="bank">bank</a-select-option>
</a-select>
<a-input
v-model="number"
#change="handleNumberChange"
/>
</a-input-group>
</template>

Error with prop definition in vuejs

I use datatable (https://github.com/pstephan1187/vue-datatable) component in VueJs 2.
My component is the following:
<template>
<div>
<div id="desktop">
<div v-if="visibility.personsTable">
<datatable-persons
:columns="persons_table_columns"
:data="rows"
filterable paginate
></datatable-persons>
</div>
</div>
</div>
</template>
<script>
import VueJsDatatable from 'vuejs-datatable';
import Profile from './user/Profile';
import ConnectionService from '../components/services/ConnectionService';
const connectionService = new ConnectionService();
Vue.component('showuser', {
template: `
<button class="btn btn-xs btn-primary" #click="goToUpdatePage">Профиль</button>
`,
props: [row],
methods: {
goToUpdatePage: function(){
}
}
});
export default {
components: {
datatablePersons: VueJsDatatable,
usersTable: VueJsDatatable,
},
data() {
return {
rows: [],
persons_table_columns: [
{label: 'id', field: 'id'},
{label: 'Имя', field: 'firstname'},
{label: 'Фамилия', field: 'lastname'},
{label: 'Отчетство', field: 'middlename'},
{label: 'Профиль', component: 'showuser'}
],
visibility: {
personsTable: false,
}
}
},
methods: {
showPersons() {
this.$http.get(hostname + 'name=person_list&session_id=' +
sessionApiId).then(response => {
this.rows = connectionService.renderMultipleInstances(response.body);
this.visibility.usersTable = false;
this.visibility.personsTable = true;
}, response => {
// error callback
});
},
}
}
</script>
I have the following error:
Uncaught ReferenceError: row is not defined
at Object.defineProperty.value (app.js:5309)
at webpack_require (app.js:20)
Due to documentation of "table" component, it should watch for "row prop" And using official example it works properly.
In the definition of your showuser component props should be an array of strings:
props: ['row']
These strings should match the names of the attributes you use to pass data to the component.
Also from the sinppet I would guess you want it to be 'rows' not 'row'.