Svelte: Trigger update of {#each} in child component [duplicate] - javascript

This question already has answers here:
Svelte's reactivity with array operation
(2 answers)
Closed 6 months ago.
I am trying to update a child component with an {#each} loop inside it to add additional items by push()ing them to the array that is iterated by the child component. While an individual variable (say a string or number) works fine and array that feeds a {#each} loop doesn't seem to.
I am using a bind: to pass live data to the child component and have also tried to use a $: array statement in the child. Whatever I do, pushing new data to the array in the parent has no effect on the child, is there a way to do this or do I have to split off and re-render the whole child component {#each} loop every time?
I have posted my test script in a REPL: https://svelte.dev/repl/e1f24f8fccfa4533a1d682d163af9a66?version=3.49.0
Type text in to the input box and hit the 'Note This' button.
If you watch the JS Console you can see the array expanding, but the {#each} loop is not rendering the extra items to the child component.

Svelte works in a way where it only detects updates from assignment. You can read more about it here:
https://svelte.dev/docs#component-format-script-2-assignments-are-reactive.
Using methods such as push won't automatically trigger updates.
In your case you can just add testData = testData after pushing the new object and it works fine.

Related

React. Store JSX class component and change it's props later

Im developing a react image editor, and I want to draw lines on the canvas, to do this I have a class that i add to the canvas elements array (this stores all the diferent elements, text, images and lines that the canvas redraws every frame).
But when i do this:
let pos = this.stageRef.current.getPointerPosition()
let new_line = (<Line
key={this.state.itemArray.length}
stroke={'#df4b26'}
strokeWidth={5}
globalCompositeOperation={'source-over'}
points={[pos.x, pos.y]}
/>)
this.state.itemArray.push(new_line)
this.setState({isDrawing: true})
console.log(new_line)
I get this object:
This object it's located inside the itemsArray, all the items are rendered on the main render here:
And it's rendered correctly, but all it's props are read-only and i can't call any methods of the object, what i need it's the "real" React object, the one you get when you call a reference.
(I want to add more points to the line object onMoveMouse event)
this:
Is there any way I can get the Line object (i just created) accesing to the itemsArray object instead of using the ref?
I don't know how can i create a Line object directly and call it's methods without a Ref ¿Do I really need a ref to an object i just created 2 lines above?.

How to reasign object with `this` in Angular component?

I have 2 variables inside an Angular component/class FilterComponent that point to an object. The first one (localStorage) stores the results of the current filtering choices that the user makes and is connected with toogle buttons with [(ngValue)]. The second one (globalStorage) gets the results of filtering choices that the user made earlier within other components (if you for example click on fruit on home page,globalStorage.food gets updated, but not the localStorage within FilterComponent). I have a method that should assign gloablStorage to localStorage when the user open the filter that looks like this:
openFilter() {
this.localStorage = this.globalStorage; //this line is responsible for strange behaviour
...
}
This works exactly like it should, but only after the apply button on filter (within FilterComponent) is clicked, which is responsible for sending the contents of localStorage to globalStorage (via next/behaviourSubject).
For some mysterious reason, before that happens, the values of toggleButtons get assign to globalStorage as well as to localStorage (although they are connected only to localStorage). Why is this happening and how can I fix it?
when you do this.localStorage = this.globalStorage; and these 2 variables are objects, it means this.localStorage now point/refer to this.globalStorage, so from now on this 2 variable will refer to 1 place.
If you just want to copy data inside it you should do a copy instead of simple assign by using either Object.assign(shallow copy) or this.local = {...this.global} for loop deep copy
Thank you all for your help. The solution (that's good enough for now) was to reassign each property:
this.localStorage.property1 = this.localStorage.property1;
this.localStorage.property2 = this.localStorage.property2;

How to create the tree view using angularjs and json?

How to add the child value when we checked the parent element? How to passing Checkbox value to javascript?
I don't know the code which you have been written let me know code.
Most probably you required to create one action and call it when you checked checkbox and this action push objects into $scope.items or you can use map in javascript for faster operation while fetching.

Angularjs: buggy two way data binding

I am trying to create recursive grid layout using directives.
There is a parent container(appliedgrids) which contains array of grids inside it.
A grid contains array of columns inside it.
Each column has two properties: span(width of column) and data (data inside column)
Each column data contains either grid again or a widget. If it contains grid then it make recursive call to grid directive.
My problem is when I delete a grid using remove button inside it- it gets removed from appliedgrid container but two way data binding doesn't work as it should. In place of current grid, last grid gets removed from the UI.
Link- http://plnkr.co/edit/DzKIHKvJdLoZiYY3jgDx?p=preview
Steps to reproduce:
1) Click remove button on first grid, you will see that in place of first, second grid gets removed. While json data of appliedgrid contains second grid inside it. So two way binding of angular doesn't work as it supposed to.
I did a little thinking in my previous answer and it turns out it was not correct.
Firstly, do not use track by $index. It makes no sense in your case. track by is an optimisation of ng-repeat to correlate (potentially new) objects in the array that are "business-wise" equal with old objects in the array, so that it re-uses scopes and DOM elements in an effort to minimize DOM manipulation. That is: if you give ng-repeat a hint which new object in the new array is "equal" to an old object in the old array, it will reuse its scope and hopping that the new object is not dramatically different compared to the old one, less $watch callbacks will fire and less DOM updates will occur.
Your actual problem is that you are "statically" or "once-off" binding data with statements like:
$scope.gridIndex = $parse($attrs.gridIndex)($scope);
$scope.gridValues=$parse($attrs.appliedgrid)($scope);
$scope.gridParent=$parse($attrs.appliedgrids)($scope);
The first grid item is indeed removed from the array but ng-repeat does not remove its scope and DOM element because track by $index is used. But still, the new 0-index object (2nd, previously) is used to update the scope (the one created for the 1st object).
You do not see this reflecting to the UI because $scope.gridValues was evaluated in the beginning and is not evaluated again.
So, even though $scope.appliedgrid now points to [{span:12,data:[object]}], $scope.gridValues still points to [{span:6,data:[object]},{span:6,data:[grid2]}].
Removing track by $index solves the problem because ng-repeat tracks objects by reference so each object is associated with the same scope until it is removed from the array.
You can verify it with AngScope, a small Firebug-based scope inspector. You have to open it in a separate tab with "Launch the preview in a separate window" in order for it to work in plunker.
I tried to find a quick fix for it but there was no luck. I guess, you have to re-write it using isolated scopes and real 2-way binding.
Short answer: remove track by $index from ng-repeat.
Long answer: When you are write track by $index you're actually saying to ng-repeat that:
The 1st DOM element will be associated to an object that is tagged as the "0" object
The 2nd DOM element will be associated to an object that is tagged as the "1" object
When you remove the 1st object from the array, angular digests and finds out the following:
The 1st DOM element is still associated to an object tagged as the "0" object
The 2nd DOM element is not associated to any object, so it has to be removed
This is because when ng-repeat runs again, your previously 2nd object which was tagged as "1", is now your 1st and only object which is tagged as "0", since the $index is evaluated again starting from 0.
Angular believes that the 1st DOM element still points to the same object cause it finds it tagged as "0", regardless that it's a completly different object. Under the hood, $scope has the correct model values but ng-repeat skips re-rendering of the DOM element.
It very difficult to write down what really happens. Hope I got it right and helped you.

knockout.js referring to parent object

I have a complex knockout.js object which is effectively an observableArray with another observableArray inside. I have a remove function which asynchronously removes chosen element from the second array. An item is being archived in the database, while one of its observable properties on client side is being set to false making it to disappear from the screen.
A remove button event is created using $root keyword:
Remove
What gives me access to details of the chosen element using "this" keyword. My problem is, that while deleting item from the second array I would like to change something to its parent item in the first array. As I mentioned "this" keyword refers to the child item, is there any way I could access the parent item at the same time as well?
mhu's answer is a antipattern because it creates a dependency between ViewModel and the structure of the View.
Instead do
Remove
Parent Viewmodel
removeActivity: function(activity) {
this.activities.remove(activity);
}

Categories