VueJs validate child component names in parent component - javascript

I have a parent component as shown below that implements dynamic component switching. The challenge is that I want to ensure that the componentArgs name data at index 0 is valid. The line this.components.indexOf(componentArgs[0]) > -1 throws a TypeError.
Parent.vue?e9d7:xx Uncaught TypeError: Cannot read property 'indexOf'
of undefined
Clearly, this.components is not the correct way to access the parents components. How do I accomplish this?
<template>
<component v-bind:is="currentForm" v-on:switchcomponent="switchComponent"></component>
</template>
<script>
import Login from '#/components/Login'
import Signup from '#/components/Signup'
export default {
name: 'Parent',
components: {
login: Login,
signup: Signup
},
data () {
return {
title: 'Sign Up or Login',
email: '',
currentForm: 'login'
}
},
methods: {
switchComponent: function (componentArgs) {
// componentArgs format: [name, title, email (Use empty string if N/A.)]
let name = 0
let title = 1
let email = 2
console.log('component: ' + componentArgs)
if (this.isValidComponent(componentArgs)) {
this.currentForm = componentArgs[name]
this.title = componentArgs[title]
this.email = componentArgs[email]
}
},
isValidComponent: function (componentArgs) {
let exactLength = 3
if (componentArgs.length === exactLength) {
console.log('components ' + this.components)
if (this.components.indexOf(componentArgs[0]) > -1) {
console.log('component exists')
return true
}
return false
}
return false
}
}
}
</script>

The components property can be accessed from
this.$options.components
So your function could be
isValidComponent(name){
return !!this.$options.components[name];
}
where name is the name of the component. I can't really tell what you are passing in componentArgs.
Example.

Related

Conditionally accessing Pinia value from template in Vue 3

I'm trying to render a component only if the value accessed from my Pinia store is true -
// MessageArea.vue
import { useValidationStore } from '../../stores/ValidationStore.js'
data() {
return {
errors: {
english: useValidationStore().english.error,
},
}
},
<template>
<p v-if="errors.english">
<EnglishErrorMessage />
</p>
</template>
By default, it is false -
import { defineStore } from "pinia";
export const useValidationStore = defineStore("validation", {
state: () => {
return {
english: {
input: '',
error: false,
},
}
}
})
I'm also accessing that same value from another file to check for the error -
<script>
import { useValidationStore } from '../../../stores/ValidationStore';
export default {
data() {
return {
input: useValidationStore().english.message,
error: useValidationStore().english.error,
}
},
methods: {
validate(input) {
this.input = input.target.value
const legalChars = /^[A-Za-z\s]*$/.test(this.input);
if (!legalChars && this.input !== "") {
this.error = true;
} else if (this.input === "" || (legalChars && !legalChars)) {
this.error = false;
}
console.log(this.error)
console.log(this.input)
}
}
}
</script>
<template>
<input
#input="validate"
:value="input"
/>
</template>
The console log values are updating reactively. Also, this.error will be logged as true when an illegal character is entered into the input. The reactivity is behaving as expected. So, I'm not sure why '' will not render in this case?
Am I not accessing the pinia value correctly in the template?
I've tried to understand what 'mapState()' and 'mapStores()' from the docs and if they can help me but I'm still confused.
You need to mutate the store state in validate method.
If you want to use pinia in options api you can use mapState and mapWritableState in computed property to remain rective.
Take a look here codesandbox please.

Issue when trying to interact with an API in Vuejs?

datalist.js
import axios from "axios";
export const datalist = () => {
return axios.get("myapiurl/name...").then((response) => response);
};
HelloWorld.vue
<template>
<div>
<div v-for="item in items" :key="item.DttID">
<router-link
:to="{
name: 'UserWithID',
params: { id: item.DepaD },
query: { DepaD: item.DepaID },
}"
>
<div class="bt-color">{{ item.DepaName }}</div>
</router-link>
</div>
<br /><br /><br />
<User />
</div>
</template>
<script>
import User from "./User.vue";
import { datalist } from "./datalist";
export default {
name: "HelloWorld",
components: {
User,
},
data() {
return {
items: datalist,
};
},
mounted() {
datalist().then((r) => {
this.items = r.data;
});
},
};
</script>
User.vue
<template>
<div>
<div v-for="(item, key) in user" :key="key">
{{ item.Accv }}
</div>
</div>
</template>
<script>
import { datalist } from "./datalist";
export default {
name: "User",
data() {
return {
lists: datalist,
};
},
computed: {
user: function () {
return this.lists.filter((item) => {
if (item.DepaD === this.$route.params.id) {
return item;
}
});
},
},
};
</script>
Error with the code is,
[Vue warn]: Error in render: "TypeError: this.lists.filter is not a function"
TypeError: this.lists.filter is not a function
The above error i am getting in User.vue component in the line number '20'
From the api which is in, datalist.js file, i think i am not fetching data correctly. or in the list filter there is problem in User.vue?
Try to change the following
HelloWorld.vue
data() {
return {
items: [],
};
},
mounted() {
datalist().then((r) => {
this.items = r.data;
});
},
User.vue
data() {
return {
lists: []
};
},
mounted() {
datalist().then((r) => {
this.lists = r.data;
});
},
At least this suppress the error, but i cant tell more based on your snippet since there are network issues :)
Since your datalist function returns a Promise, you need to wait for it to complete. To do this, simply modify your component code as follows:
import { datalist } from "./datalist";
export default {
name: "User",
data() {
return {
// empty array on initialization
lists: [],
};
},
computed: {
user: function() {
return this.lists.filter((item) => {
if (item.DeploymentID === this.$route.params.id) {
return item;
}
});
},
},
// asynchronous function - because internally we are waiting for datalist() to complete
async-mounted() {
this.users = await datalist() // or datalist().then(res => this.users = res) - then async is not needed
}
};
now there will be no errors when initializing the component, since initially lists is an empty array but after executing the request it will turn into what you need.
You may define any functions and import them, but they wont affect until you call them, in this case we have datalist function imported in both HelloWorld and User component, but it did not been called in User component. so your code:
data() {
return {
lists: datalist,
};
},
cause lists to be equal to datalist that is a function, no an array! where .filter() should be used after an array, not a function! that is the reason of error.
thus you should call function datalist and put it's response in lists instead of putting datalist itself in lists
Extra:
it is better to call axios inside the component, in mounted, created or ...
it is not good idea to call an axios command twice, can call it in HelloWorl component and pass it to User component via props

Problem with a function to filter results defined in a computed property on vue.js

I'm studying vue.js and I create a little project: https://codesandbox.io/s/focused-gould-hs7k5?file=/src/components/Modal.vue
. I've created two components: Home and Modal. When I click on a button defined in the Home I opened a modal. In this component I used two dependencies: axios and infinite scroll. I create a function "loadMore" that I invoke on mounted property. This function load elements, that I stored in "photos" variable, from a GET call. After this I wanted to implementes a search filter bar. I created an input tag with a v-model "searchString". I defined this property in the data and I created a function "filteredResources" that filters the results.
<script>
import axios from "axios";
export default {
name: "Modal",
data() {
return {
photos: [],
results: [],
busy: false,
limit: 10,
searchString: ""
};
},
methods: {
loadMore() {
this.busy = true;
axios
.get("https://jsonplaceholder.typicode.com/photos")
.then(res => {
const append = res.data.slice(
this.photos.length,
this.photos.length + this.limit
);
this.photos = this.photos.concat(append);
this.busy = false;
})
.catch(function(error) {
console.log(error);
this.busy = false;
});
}
},
mounted() {
this.loadMore();
},
computed: {
filteredResources: () => {
if (!this.searchString) return this.photos;
let regex = new RegExp(this.searchString, "i");
return this.photos.filter(photo => regex.test(photo.title));
}
}
};
</script>
This is the template:
<section class="modal-body">
<slot name="body">
<input type="text" v-model="searchString" placeholder="Enter your search terms">
<ul>
<div
v-infinite-scroll="loadMore"
infinite-scroll-disabled="busy"
infinite-scroll-distance="limit"
class="productsContainer">
<li v-for="photo in photos" :key="photo.id">
<img :src="photo.url" height="200">
<p class="titleDescription">Title: {{photo.title}}</p>
</li>
</div>
</ul>
</slot>
</section>
If in the v-for directive I tried to put filteredResources instead of photos, for doing a search, I get an error:
"[Vue warn]: Error in render: "TypeError: Cannot read property 'searchString' of undefined"
But if I try to debug when filteredResources is invoked the 'searchString' variable is defined like an empty string.
Try this :
computed: {
filteredResources: (){
if (!this.searchString || this.searchString == '') return this.photos;
let regex = new RegExp(this.searchString, "i");
return this.photos.filter(photo => regex.test(photo.title));
}
}

Updating VueJS component data attributes when prop updates

I'm building a VueJS component which needs to update the data attributes when a prop is updated however, it's not working as I am expecting.
Basically, the flow is that someone searches for a contact via an autocomplete component I have, and if there's a match an event is emitted to the parent component.
That contact will belong to an organisation and I pass the data down to the organisation component which updates the data attributes. However it's not updating them.
The prop being passed to the organisation component is updated (via the event) but the data attibute values is not showing this change.
This is an illustration of my component's structure...
Here is my code...
Parent component
<template>
<div>
<blink-contact
:contact="contact"
v-on:contactSelected="setContact">
</blink-contact>
<blink-organisation
:organisation="organisation"
v-on:organisationSelected="setOrganisation">
</blink-organisation>
</div>
</template>
<script>
import BlinkContact from './BlinkContact.vue'
import BlinkOrganisation from './BlinkOrganisation.vue'
export default {
components: {BlinkContact, BlinkOrganisation},
props: [
'contact_id', 'contact_name', 'contact_tel', 'contact_email',
'organisation_id', 'organisation_name'
],
data () {
return {
contact: {
id: this.contact_id,
name: this.contact_name,
tel: this.contact_tel,
email: this.contact_email
},
organisation: {
id: this.organisation_id,
name: this.organisation_name
}
}
},
methods: {
setContact (contact) {
this.contact = contact
this.setOrganisation(contact.organisation)
},
setOrganisation (organisation) {
this.organisation = organisation
}
}
}
</script>
Child component (blink-organisation)
<template>
<blink-org-search
field-name="organisation_id"
:values="values"
endpoint="/api/v1/blink/organisations"
:format="format"
:query="getQuery"
v-on:itemSelected="setItem">
</blink-org-search>
</template>
<script>
export default {
props: ['organisation'],
data() {
return {
values: {
id: this.organisation.id,
search: this.organisation.name
},
format: function (items) {
for (let item of items.results) {
item.display = item.name
item.resultsDisplay = item.name
}
return items.results
}
}
},
methods: {
setItem (item) {
this.$emit('organisationSelected', item)
}
}
}
</script>
How can I update the child component's data properties when the prop changes?
Thanks!
Use a watch.
watch: {
organisation(newValue){
this.values.id = newValue.id
this.values.search = newValue.name
}
}
In this case, however, it looks like you could just use a computed instead of a data property because all you are doing is passing values along to your search component.
computed:{
values(){
return {
id: this.organisation.id
search: this.organisation.name
}
}
}

Access to props inside javascript

I just start to learn vuejs 2 but I have an issue, I'm passing a props to a child component like this :
<div class="user">
<h3>{{ user.name }}</h3>
<depenses :user-id="user.id"></depenses>
</div>
And here is how I use it in my child component :
export default {
name: 'depenses',
props: ['userId'],
data () {
return {
depenses: []
}
},
mounted: function () {
this.getDepenses()
},
methods: {
getDepenses: function () {
this.$http.get('myurl' + this.userId).then(response => {
this.depenses = response.body
this.haveDepenses = true
}, response => {
console.log('Error with getDepenses')
})
}
}
}
this.userId is undefined but I'm able to display it with <span>{{ userId }}</span> and I can see in the vuejs console the param userId with the value
Why I have an undefined value in the js ?
Ok I found why I couldn't get my value, thanks to #Saurabh comment, he reminds me that I get my props from an async way, so I had to set a watcher when my props change, like this :
watch: {
userId: function () {
this.getDepenses()
}
},

Categories

Resources