I have code like below
created () {
EventBus.$on('change',this.formated);
},
props: ['applicants',],
data() {
return {
values: [],
}
},
methods : {
formated (item) {
//some code here
}
}
I have some checkboxes in another component. I am trying to catch values of those checkboxes in this component. After catching those values I would like to filter the props applicants. I would like to find out which applicants have those values of checkboxes. Then I would like to pass them to values[]. Then I would like to iterate those values in HTML.
After reading the comments I believe this is what you need to do. You can use the prop as well as the data passed through the event bus in the formatted() method. You need to combine the two and set them to values. I have pasted code below. However, not knowing the structure of either two objects I can't help you further. This is just plain JavaScript. So probably you need something like Object.assign or the .filter or .map methods.
created () {
EventBus.$on('change',this.formatted);
},
props: ['applicants'],
data() {
return {
values: [],
}
},
methods : {
formatted (item) {
this.applicants // access the data from your prop
item // access the data from your event bus
this.values = someCombinationOf(this.applicants, item)
}
}
After setting values from the formatted () function you can iterate over it (for example with v-for). Whenever an event is emitted through the event bus this will update your component. Whenever the prop changes the component will be updated also.
Related
I have this object of arrays that I'm tryin to watch every update of.
myData = {
"299":[527],
"376":[630,629]
}
I read this documentation on watching an object which instructed to use either this.$set(object, propertyName, value) or Object.assign({}, this.object, dataToBeAppended) to watch an object. I used this.$set.
export default {
...
data() {
return {
myData: {},
};
},
watch: {
myData(newVal) {
console.log(`🔴localStorage`);
},
},
methods: {
onFoldChange(propertyName) {
const newArr = [...]
this.$set(this.myData, propertyName, newArr);
},
}
}
Unlike what I expected, vue captures changes on property only. Changes in value to an existing property are not being watched. For example, if a property "299" was newly added, it will print 🔴localStorage. When the value of a property "299" is updated from [527] to something else, nothing is fired. When I print myData, I see every value updated correctly. It is just that watch isn't capturing the changes.
The documentation also described we can watch an array using this.$set(this.myData, indexOfItem, newValue) so I also tried array version of the above code, like this.
this.$set(this.myData[propertyName], index, newValueToAdd);
This time it doesn't listen at all. Not even the first entry.
Is there any better way to solve this issue? How do others watch an object? Is the complication coming from the type of values (array) ?
Currently, myData watcher observes only an object. Object contains pointers to arrays as in JS Objects & Arrays are passed by reference not by copy. That's why it can detect only changes in keys and with simple values. If you want to observe it deeper - I mean also those subarrays (or subobjects) - just use deep watch.
watch: {
myData: {
deep: true,
handler (newVal) {
console.log(`🔴localStorage`);
}
}
}
Another possible solution could be to use some Array.prototype operation to modify an array if it already exists. E.g:
methods: {
onFoldChange(propertyName) {
if (propertyName in this.myData && Array.isArray(this.myData[propertyName])) {
this.myData[properyName].push(162) // Some random value
} else {
const newArr = [...]
this.$set(this.myData, propertyName, newArr);
}
},
}
So I have the following piss of code in my child component
props: {
prop_accounts: Array,
prop_pushing_destination: String,
prop_selected_account: String,
prop_selected: Boolean,
shop_settings: Object,
name_of_selected_account_field: String
},
data() {
return {
accounts: this._.cloneDeep(this.prop_accounts),
pushing_destination: this._.cloneDeep(this.prop_pushing_destination),
selected_account: this._.cloneDeep(this.prop_selected_account),
selected: this._.cloneDeep(this.prop_selected)
}
},
The parent props pass all the props and all seams to work well but the parent is constantly sampling the backend for changes and if they acquire it updates the props of child and although I can see that props are changed the data stays the same now if I throw the data and use props directly all works well but I get the following warning
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. Prop being mutated: "selected_account"
There are two ways you could handle this: use the props directly and emit changes to the parent; or use computed properties to compute the value based on the data and props.
Emitting changes is probably what you want to do 99% of the time as any changes to the props either internal or external will change what your component is doing. Using computed props allows for changes to the props to be used only if the data hasn't been modified internally.
Computed props
props: {
defaultAccounts: Array,
defaultSelected: Boolean,
...
}
data: () => ({
customAccounts: null,
customSelected: null,
})
computed: {
accounts() {
return (this.customAccounts == null) ? this.defaultAccounts : this.customAccounts
},
selected() {
return (this.customSelected == null) ? this.defaultSelected : this.customSelected
}
}
You could even define setters on the computed props to set the value of the data properties.
Emit changes
Component:
props: {
accounts: Array,
selected: Boolean,
...
}
methods: {
accountsChanged(value) {
this.$emit('update:accounts', value)
},
selectedChanged(value) {
this.$emit('update:selected', value)
}
}
Where you use component:
<my-component :accounts.sync="accounts" :selected.sync="false"></my-component>
See Sync Modifier Documentation for more info.
I haven't tested this code so it may need tweaking to get it working correctly.
I'm working on a project, similar as a bill manager, so I want that the subtotal get recalculated every time that quantity or unit value change, I have tried and searched to accomplish this using watcher or computed properties, but I don't find the right approach, cause I need to access the whole scope of the element when another change, like this.
Model structure:
detail
quantity
unit value
subtotal (should be a computed or updated)
So I think I should be able of doing something like this:
Vue.component('item', {
template: '#item',
props: {
item: Object,
},
computed:{
total: function(){
return this.quantity*this.unit_value;
}
},
watch:{
'item.quantity':()=>{
this.subtotal = this.quantity*this.unit_value;
}
}
});
I have several components being read from a list
I merged the approach using watcher and computed in the same code to make it shorter.
The problem is that I haven't found a way to access the hole element from inside itself, anyone could pls explain the right way? thanks
You shouldn't use arrows functions there, use method declarations.
If you want to watch for a property of the item object, you'll have to watch for the item object itself, and additionally use the deep: true flag of the watcher.
Final detail, you are using several properties that are not declared in your data. Declare them, otherwise they will not be reactive, that is, the computed will not recalculate when they change.
See code:
Vue.component('item', {
template: '#item',
props: {
item: Object,
},
data() {
return {
subtotal: null, // added data properties
quantity: null,
unit_value: null
}
},
computed: {
total: function() {
return this.quantity * this.unit_value;
}
},
watch: {
item: { // watching for item now
deep: true, // using deep: true
handler() { // and NOT using arrow functions
this.subtotal = this.quantity * this.unit_value;
}
}
}
});
As the title says, I'm trying to change the value of a prop/data in a component, but the trigger is being fired from outside the component, from something that has nothing to do with Vuejs.
Currently I trying to use a Simple State Manager, based on the docs from here, like so:
var store = {
debug: true,
state: {
progress: 23
},
setProgress (uff) {
if (this.debug) console.log(uff)
this.state.progress = uff
}
}
The documentation leads me to believe that if the value of progress is mutated, the value of my Vue instance would also change if I link them accordingly. But this doesn't work in a component (my guess would be it's cause it's a function).
This is part of my component:
Vue.component('transcoding', {
data () {
return {
progress: store.state.progress
}
},
template: `
<v-progress-circular
:size="130"
:width="25"
:value="progress"
color="teal"
>
{{progress}}
</v-progress-circular>
`
})
So, when I trigger a store.setProgress(value), nothing happens. The log messages do happen, but the state isn't updated in the component.
This is the script that's currently triggering the state change:
App.cable.subscriptions.create(
{ channel: "ProgressChannel", room: "2" },
{ received: function() {
store.setProgress(arguments[0])
}}
)
It's an ActionCable websocket in Ruby on Rails. The trigger works perfectly, but I just cannot make the connection between the state change and the component.
I tried loading this script in the mounted() event for the component, thinking I could reference the value like this:
Vue.component('transcoding', {
data () {
return {
progress: 0
}
},
template: `
<v-progress-circular
:size="130"
:width="25"
:value="progress"
color="teal"
>
{{progress}}
</v-progress-circular>
`,
methods: {
setProgress: function(uff) {
this.progress = uff
}
},
mounted() {
App.cable.subscriptions.create(
{ channel: "ProgressChannel", room: "2" },
{ received: function() {
this.setProgress(arguments[0])
}}
)
}
})
But this gives me an error saying that this.setProgress is not a function, which is obvious since I'm calling it within the create method of App.cable.subscriptions.
How can I make this work? I realize I'm mixing things with my question, but I wanted to illustrate what my goal is. I simply want to know how to make the component's progress data to update, either from the outside, or from the component itself if I can make it find the function.
You are initializing your data item to the value from the store:
data () {
return {
progress: store.state.progress
}
}
Changes to the store will not propagate to your data item. You could eliminate the data item and just use store.state.progress where you need it, or you could create an computed that returns its value if you want a local single-name handle for it.
I had an API call to the backend and based on the returned data, I set the reactive data dynamically:
let data = {
quantity: [],
tickets: []
}
api.default.fetch()
.then(function (tickets) {
data.tickets = tickets
tickets.forEach(ticket => {
data.quantity[ticket.id] = 0
})
})
Based on this flow, how can I set watcher for all reactive elements in quantity array dynamically as well?
You can create a computed property, where you can stringify the quantity array, and then set a watcher on this computed property. Code will look something like following:
computed: {
quantityString: function () {
return JSON.stringify(this.quantity)
}
}
watch: {
// whenever question changes, this function will run
quantityString: function (newQuantity) {
var newQuantity = JSON.parse(newQuantity)
//Your relevant code
}
}
Using the [] operator to change a value in an array won't let vue detect the change, use splice instead.