I'm trying to debug a rather weird problem I've run into with a Vue.js application. Unfortunately it's way too complex to be able to isolate some code showing the problem, so I'm hoping that a clear description might be enough to enable people to give me some pointers as to where to look.
I have a component called ItemSelector, which has a 'targetList' prop of type Array. On a form, I use this component 3 times, passing a different array for each instance. Each array consists of sale item objects, and the ItemSelector component displays these items and allows the user to add, edit and delete them. All works just fine, except for one thing. The objects contain a property called 'subtotal'. If in one instance of my ItemSelector component I edit one of the item objects and clear the value of the 'subtotal' property, the item with the same index in the other instances of ItemSelector also has the 'subtotal' property cleared! That is, if I edit the second item in one of the ItemSelector instances, clearing the subtotal property, the second item, if present, in the other ItemSelector instances is also cleared. However, if I instead change the value to a different number, this has no effect on other instances of the component. It's only clearing the value which does it.
Does this make any sense at all to anyone? I cannot see how a change in one array could affect other arrays at all.
Related
After trying to find solution to this issue for hours on various forums i am posting this here.
So i have two components. 1) App and 2) Todo. Both renderes a list and i can complete items so there will be two lists one for incomplete items and one for complete items. you can click on item and it will be gone to complete items list.
So in my example you can see i am using same component but with two diffreent ways to give data to component. one using API and one using native js Data. in both cases it renderes but with api i can click on list item and it will be gone to completed list but with javascript array example it doesn't work. i am completely amazed with this because component is same. how it can affect like that.
many answer here do tell me that computed properties are not reactive as they are cached but what’s the solution to that ? i can put data variable but then the first case of api will not work because time it takes to fetch it. so please help me with this one.
complete code at sfc playground
You have reactivity issues the computed property probably expects that value to be constant because you provide a non-reactive array from the parent.
I think you have 2 options here:
you either provide a reactive prop from parent
or you set a local data attribute in the child-component so that vue will know that it can change
Your fiddle didn't work for me so I copied your code to codesandbox, I have both examples there but commented out the first solution, there you basically simply add the array to the data object and reference that in the code.
Second solution you can add a mounted hook to define reactiveAssignments to your data in the child component this way it will have the same reference so that's why it would work that way.
I think the first solution is simpler, but it is really up to which one you prefer.
You can check the solutions here in my codesanbox
A better approach could be though by setting up component events instead of v-models in the child you should use it in the parent because this way you are directly modifying the props. You can read more about this here: https://vuejs.org/guide/components/events.html#usage-with-v-model
I have a relatively complex API request object I need to make, with a large number of UI components responsible for updating different properties of the object.
I'm passing the basic request model as a prop from a parent component to its children, which pass it on to theirs (down several "generations").
At the parent level, I have a computed property that returns a field of this data model, and a watch on that computed property.
When a child component updates the property on the model, it successfully updates everywhere that has a reference to it, but the computed property on the parent fails to recalculate, and resultantly the watch never activates.
I'm guessing I've missed the point somewhere along here, but I can't think about how else to update without resorting to long event chains through the UI.. How should I be approaching this instead?
To anyone with a similar question - from my research it seems that modifying reference values on props is not the intended approach for VueJS. Which is a shame, because initially it seemed like quite a neat pattern.
I've implemented vuex now, which is working well, and avoids long lines of events going back to the original owner of the prop data.
IF you wanted to press it, then modifying references on the object itself will force updates down the chain. So (e.g.) if you wanted to update an array property of the prop data, then instead of "pushing" to it, you would replace the whole array object (causing other components with computed properties on that array property to recalculate). But again, not recommended.
I have this weird bug with a slot that is unreliable in certain unknown cases.
Components
There are 3 hierarchical components.
The grandchild (headlessTable), which offers a slot named arrayValue.
The child (collapsableCard), which passes the slot between grandchild and parent.
The parent (orderDataCard), who decides to render a link for that slot.
Problem: Instead of rendering the link of the parent, the default slot html of the child is being rendered when new data is loaded.
Datastructure (orderDetails)
process (obj)
mark (string)
common (obj)
additionalArguments (array)
category (string)
type (string)
name (string)
value (string)
salesOrganisation (obj)
invoices (array)
invoiceAgreementId (string)
paymentType (string)
Reproduction
Stackblitz or Codesandbox
Please look at the field additionalArguments, it contains a link.
Press ALT+M to simulate fetching new data. Now, instead of rendering a link, the default slot html for that named slot is rendered instead.
You can press ALT+J to load the original data, but this time there's no link.
Initial data (ALT+J)
Loaded data (ALT+M)
Type
Equal value
mark
str
false
common
common
obj
true
salesOrganisation
salesOrganisation
obj
true
invoices (empty)
invoices
arr
false
How 2 resolve
if you uncomment line 68 in app.js (or line 73 in App.vue if you're on codesandbox), which is the field called mark
if invoices is not initially empty in app.js
if mark is removed from html in orderDataCard
if salesOrganisation is removed from html in orderDataCard
if the html in the v-for template section for invoiceItems is empty in orderDataCard
Obviously, these are not solutions.
Notes
In any case, there is no dependence or anything between any of the fields, so it's hard for me to understand why this happens and I suspect this to be a bug with vue. I already created an issue for this. However, devs won't look at the reproduction, because they think it's not minimal as #lines > 100. As soon as I delete any more meaningful lines, the bug is resolved and the removed code is not faulty, so it's very frustrating to work on this. I could still remove lines that are not meaningful, but that would make it more difficult for everyone involved to understand what data is being rendered.
Is anyone able to acknowledge the fact that this is a problem with vue and that the code is not reducible OR (I would prefer this) is anyone able to fix this?
The problem is linked to Vue handling of multiple instances of the same component. In OrderDataCard.vue you have two instances of Collapsable-Card without unique keys. In this case:
Vue uses an algorithm that minimizes element movement and tries to
patch/reuse elements of the same type in-place as much as possible.
I don't quite know how these algorithms work, and why, apparently, it reused the second instance (without a defined slot content), but, setting a unique key for these components solved the issue.
See the working code sandbox: https://codesandbox.io/s/admiring-hamilton-5ytpp?file=/src/components/OrderDataCard.vue:133-149.
Note: I couldn't trigger keyboard events in my browser, so I triggered them on button click.
This may not be the solution, but could help find it:
Objects
I noticed you are working with objects and turning them into arrays. Objects properties can be problematic to work with, because unlike arrays updated properties are not propagated. This is a problem with JavaScript, not Vue. Vue was only possible because of observers introduced, but objects are still not part of that.
You might run into problems when an object is partially updated.
I would suggest looking at Vue.set.
Old code of mine invokes it explicitly by window.Vue.set() for changes in object properties so Vue can propagate them correctly.
That is kind of a bug in Vue, but again stems from JavaScript itself.
Computed arrays
I'm not entirely sure but the computed arrays don't save the above issue with working with objects.
I would go the safe route and use Vue.set() when updating objects and object properties. You can still use the computed arrays then.
Otherwise the obvious: Make real arrays out of the objects instead of working with objects half the time.
this.process
Is there a good reason you are using this.process explicitly instead of the component's props? Or is that a component from a library?
Slots
Have you tried the exact same code but without using the collapsable-card? Just output the link itself? It might point to slot problems in the collapsable-card component. Maybe also partially because of the objects thing from above.
I just have a query on deleting displayobject instance. Let me elaborate on this:
I had created a custom component called ‘PanelItem’ (which basically contains a Text Area and a close button in a Panel container)
Then in the main.mxml, I had utilized the above custom component as follows:
var tempPanel: PanelItem = new PanelItem();
Subsequently assigned values for its attributes such as x,y, width, height and id for tempPanel and then added child instances as below
addChild(tempPanel);
The above code displays one instance of the custom displayobject. My problem is that when the ‘close’ button on the panel is clicked, I want the displayobject instance to be removed from the memory.
To do the ‘close’ action, I had added the following to the code
tempPanel.removAllChildren();
tempPanel.visible = false;
But the above only removes the children of displayobject and the doesnot removes the displayobject instance completely from memory. I read somewhere I need to ‘delete’ the displayobject, but could not find any reference to the same in the help file
Any thoughts on how do I go about removing the displayobject completely from memory?
Anther question I have is, if I had invoked multiple instances of tempPanel, how do I get a count of the number of instances.
TempPanel.numChildren() only returns the number of child instance (which got invoked thru addChild method) and not the actual numbers of displayobjects floating around.
Any help on the above will be much appreciated.
Thanks
Srinivasan S
you could extend from CasaSprite (http://as3.casalib.org/docs/org_casalib_display_CasaSprite.html) which has a destroy function.
I have a question with Value Converters, I have an array w/c filters inactive item, when I am editing an item and change the status property to 'INACTV', the table does not change. but when adding/removing items in the array, it refreshes, my workaround is creating a binded _signal property to force the filtering, is there a way not to do this?
< tr repeat.for="item of ARRAY | filtercustom:'STATUS_CD':'INACTV':_signal" >
I'm not sure I understood how the filter is supposed to work. But if it should hide inactive items, maybe you could do something like
<tr repeat.for="item of ARRAY" if.bind="!item.STATUS_CD='INACTV'">
I don't know if it is possible to put a value converter inside a "repeat.for". Looks weird.
I hope this helps to push you in the right direction.
No, there is no direct, clean way to do this at the moment. Repeat.for uses a CollectionObserver for array observation which only responds to pop/push/reverse/shift/sort/splice/unshift.
Only when one of these methods is called on the array, the observer fires and the array is fed to your ValueConverter again.
Your signal solution is about as clean as it gets. It's more efficient than the alternative of refreshing the whole array from manually instantiated property observers on your STATUS_CD property on every item in the array.
Which is what I do in some similar situations, because I don't like using signals. But that's just a matter of preference.