Suppose I have an array feedsArray, the example value may look like this:
this.feedsArray = [
{
id: 1,
type: 'Comment',
value: 'How are you today ?'
},
{
id: 2,
type: 'Meet',
name: 'Daily sync up'
}
]
Suppose I have registered two components: Comment and Meet, Each component has a prop setting as the following:
props: {
feed: Object
}
and the main component has the following definition:
<component v-for="feed in feedsArray" :feed="feed" :key="feed.id" :is="feed.type"></component>
As you can see, it uses is property to select different component. My question is, how to detect feed object change in the child component ? Like when I set
this.feedsArray[0] = {
id: 1,
type: 'Comment',
value: 'I am not ok'
}
How can the Comment component detect the changes ? I tried to add a watcher definition in the child component like the following:
watch: {
feed: {
handler (val) {
console.log('this feed is changed')
},
deep: true
}
},
But it doesn't work here. Anyone know how to solve this ?
Do not assign directly to an array using index - use splice() instead, otherwise JavaScript can not detect that you have changed the array.
If you want to change only the value of an already existing key of an object - then simply update it e.g. this.feeds[0].value = 'I am not okay any more';
This works for existing keys only - otherwise you have to use this.$set(this.feeds[0], 'value', 'I am not okay any more');
Related
I'm trying to use the v-calendar component from Vuetify.
I saw in the documentation I can use the event-start prop if my events don't have the same attributes' names.
The problem is that my events have embedded attributes and I don't know if event-start handles this case.
My events :
events: [
{
id: 'b9d93291-6d95-47b9-994a-ee9f266fb6b8',
type: 'reservation_item',
attributes: {
start_date: '2020-09-23T00:00:00.000Z',
end_date: '2020-09-25T00:00:00.000Z',
},
},
]
The events example from vuetify :
events: [
{
name: 'Weekly Meeting',
start: '2020-09-07 09:00',
end: '2020-09-07 10:00',
},
],
I tried to do something like that but it doesn't work.
<v-calendar
ref="calendar"
locale="fr-fr"
:now="today"
:value="today"
:events="events"
event-start="attributes.start"
color="primary"
type="month"
></v-calendar>
After spelunking the source code for the vuetify plugin, the latter expects that the value be present in the event object, as a direct property. So you cannot acces other nested "children", it has to be a direct property.
There are two alternatives to make this work:
1- map your events array by moving the properties inside attributes to the root of your object then pass this prop to v-calendar : event-start="startDate"
2- Create a javascript class (MyEvent) with a fromJson method that take the raw JSON from your API ( this way you encapsulate the JSON into domain objects) and return an array of MyEvent instances. this way you can do for example : events[0].start and you don't even have to pass it as a value to the event-start prop, since by default it expects a start attribute as a default value.
Another advantage of this alternative, is that since the event is now encapsulated into its own javascript class, you can add helper methods, or getters/setter or any logic that would otherwise be inside your "view" logic, and contribute to have a better separation of concerns.
To make this works, I had to change my events data
<template>
<v-calendar
ref="calendar"
locale="fr-fr"
:events="myEvents"
event-start="start"
color="primary"
type="month"
></v-calendar>
</template>
data: () => ({
events: [
{
id: 'b9d93291-6d95-47b9-994a-ee9f266fb6b8',
type: 'reservation_item',
attributes: {
start_date: '2020-09-23T00:00:00.000Z',
end_date: '2020-09-25T00:00:00.000Z',
},
},
],
}),
computed: {
myEvents() {
const reservations = this.reservations
reservations.forEach((element) => {
element.start = element.attributes.start_date
element.name = 'test'
element.end = element.attributes.end_date
})
return reservations
},
}
I have different groups of words on a single page, let's say nouns verbs and adjectives. The way to describe each group is with its 'part-of-speech'. This 'part-of-speech' is being printed inside a little box. So you have the 'part-of-speech' of the group, noun, in the little box, and I want to achieve that when I click on that box I hide verbs and adjectives. If I were to click on verb I would hide nouns and adjetives, and so on. Right now the 'part-of-speech' of each group is being passed in as a prop.
The problem is that I'd like to compare parts of speech that exist on the current page with the clicked part of speech, but I cannot manage to differentiate it.
In my template I've got:
<div class="part-of-speech">
<p class="pos">{{ pos }}</p>
</div>
and this { pos } is coming from my props
props: {
pos: {
type: String,
required: false,
default: "na"
}
},
mounted() {
console.log(this.pos)
}
This gives me all the parts-of-speech that are being printed on the page (take into account that this is a child-component of something else and these groups of words are printing as many times as there are groups). So I though that adding a method to detect the clicked part-of-speech would help.
<div class="part-of-speech" #click="handleSelectedPos(pos)">
<p class="pos">{{ pos }}</p>
</div>
props: {
pos: {
type: String,
required: false,
default: "na"
}
},
methods: {
handleSelectedPos(pos) {
console.log(pos);
console.log(this.pos);
}
}
When I click on the current item, I get the current part-of-speech, and as you can see this.pos in this context is no longer the list of parts-of-speech that are on the page but it has become the currently clicked part-of-speech. My idea was to make the comparison somehow between pos and this.pos, but they are now identical.
How to make the comparison to say: If there are parts-of-speech that are not equal to the one currently clicked, take some action (add a class or wtv) to hide the element.
If I understand well, what you would like to achieve, then some of the events shouldn't be handled by the subcomponents, but by the parent component.
Vue.component('partOfSpeech', {
props: ['pos', 'text'],
template: `<div :class="pos" #click='emitEvent'>{{text}}</div>`,
methods: {
emitEvent() {
this.$emit('emitpos', this.pos)
}
}
})
new Vue({
el: '#app',
data: {
words: [{
pos: 'noun',
text: 'noun1',
compare: false
},
{
pos: 'verb',
text: 'verb1',
compare: false
},
{
pos: 'adjective',
text: 'adjective1',
compare: false
},
{
pos: 'noun',
text: 'noun2',
compare: false
},
{
pos: 'verb',
text: 'verb2',
compare: false
},
{
pos: 'adjective',
text: 'adjective2',
compare: false
},
{
pos: 'verb',
text: 'verb3',
compare: false
},
{
pos: 'noun',
text: 'noun3',
compare: false
},
{
pos: 'adjective',
text: 'adjective1',
compare: false
},
]
},
methods: {
filterWords(val) {
this.words.forEach(e => {
if (e.pos === val) {
e.compare = true
} else {
e.compare = false
}
})
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<part-of-speech :key="i" v-for="(word, i) in words" :pos="word.pos" :text="word.text" v-on:emitpos="filterWords" :style="{ color: !word.compare ? 'black' : 'red'}" />
</div>
In the snippet above you can see that every
data is passed down to the child component as prop (except for compare - that's not needed there)
a click event is set up on each of the child components, that (#click) $emit() an event and their prop back to the parent
the parent has a v-on: for the event emitted, and executes a function on ALL the parts of speech (words in my app; the function colors words red that have the same pos as the clicked one).
MORE INFO
The problem is that sibling elements do not know anything about each other: they’re not supposed to share any information with each other.
A component can share its own unique state information with sibling components either via their common parent (by emitting an event (with a data payload)) or by using some form of state management solution (event bus or Vuex store are the most common in Vue - the latter is for serious cases, the former is for occasions that require more than simple event emitting, but doesn’t require anything really complicated).
Custom events in Vue: https://v2.vuejs.org/v2/guide/components-custom-events.html
Event bus: https://alligator.io/vuejs/global-event-bus/
Vuex state management: https://vuex.vuejs.org/
Using Redux Form I'm able to retrieve the values which I enter in username. I'm able to filter out the corresponding values, but I need to display my filtered values in the browser.
console.log("pilot.name--->", pilot.name);
Can you tell me how to do it? I provided my code snippet and sandbox below. My related code is in showResults.js: https://codesandbox.io/s/xl1r14w854.
var pilots = [
{
id: 2,
name: "Wedge Antilles",
faction: "Rebels"
},
{
id: 8,
name: "Ciena Ree",
faction: "Empire"
},
{
id: 40,
name: "Iden Versio",
faction: "Empire"
},
{
id: 66,
name: "Thane Kyrell",
faction: "Rebels"
}
];
var rebels = pilots.filter(function(pilot) {
// return pilot.faction === "Rebels";
// return pilot.faction === values.username;
if (pilot.faction === values.username) {
console.log("pilot.name--->", pilot.name);
}
});
I suggest to use a separate reducer for this. Imagine that you call that reducer PilotsReducer where you have your list of pilots in the state. That reducer is "listening" to a specific action like loginSubmitted that you trigger when you get the response from the server.
The payload of that action will be the username that you use to do the filter. In the reducer you can then do the filter and set a state property (e.g. rebels) to the result of the filter. Your component can then be attached to redux to pick the rebels property and it will re-render when that property changes.
This is a typical flow of react/redux, it is not specific to your example
On a side note you can improve it by using reselect so you would keep the entire list of pilots in the state, and filter them in a selector by passing the current username to it. Please check the docs and tutorials of redux and reselect to have a clear view of the entire workflow.
In a Vue Js component, I need to loop through an object on the mounted hook that's in local storage in Vuex to update the data properties as you can see in code example.
I'm trying to update this.title, this.body, this.id whereby the rightHere variable in the loop is outputting these names as string values as the var you can see.
this.rightHere
...is the problem I know, and is obviously trying to target a data property "rightHere" which doesn't exist. But I don't know how else to overcome this in javascript and make rightHere output the string as needed? So how do I use this in a loop to dynamically change but tell Vue to update this. on each iteration?
data() {
return {
title: '',
body: '',
id: '',
}
},
mounted() {
for (var rightHere in this.$store.getters.getObject) {
if (this.$store.getters.getObject.hasOwnProperty(rightHere )) {
this.rightHere = this.$store.getters.getObject[rightHere ]
}
}
},
You would typically set the key in your template. It's a reserved word.
<div v-for='item in items' :key='$store.getters.getKey(item)'>{{item.title}}</div>
I have the below JS code in my Ember app that gets called;
myPanels.accordionPanels = [];
myPanels.accordionPanels.push({
panel: {
name: "my-grid",
type: 'comp',
props: [{
key: 'elementId',
value: "myCustomId"
}]
}
});
So as you can see, I start by setting myPanels.accordionPanels = [] every time and then push the object.
However, I got the following error
Assertion Failed: Attempted to register a view with an id already in
use: myCustomId
So I am assuming that the object inside is not getting reset & it is able to find the earlier created "myCustomId".
Am I resetting the array (or rather the object inside it) correctly ?
Since I am able to push values using:
accordionPanels = [];
accordionPanels.push({
panel: {
name: "my-grid",
type: 'comp',
props: [{
key: 'elementId',
value: "myCustomId"
}]
}
});
make sure myPanels.accordionPanels doesn't have any prototype associated with it.
Try to inspect its value as:
myPanels.accordionPanels = [];
console.log(myPanels.accordionPanels); // see if it has values.
You can delete value using :
delete myPanels.accordionPanels PROTOTYPE