Code Monkey home page Code Monkey logo

Comments (6)

fabien avatar fabien commented on May 23, 2024 1

@elbywan thanks - I've been able to track it down, now that you've proven that it does work. By default Baobab uses immutable: true, which uses Object.freeze. This was causing issues for me, with the tree not updating at all when modifying the model data. What I did before, was 'unfreeze' the data using Lodash's cloneDeep, which worked - at the expense of added overhead, and indeed, complete re-rendering of the tree.

At the time, I did not think there was any difference between immutable edits and completely cloned trees to be honest. Now I switched to immutable: false (which apparently is recommended during production anyway) and all is well!

from bosket.

elbywan avatar elbywan commented on May 23, 2024

Using Angular, I modified the input variable for the model, but change detection is not happening.

That's because the TreeView component uses the changeDetection: ChangeDetectionStrategy.OnPush which prevents updates unless an event is triggered or if an input variable reference change (mutating objects do not trigger the change detection since the object reference stays the same).

Did you try using a ChangeDetectorRef to update the tree view after mutating the model ?

// Inject the ChangeDetectorRef
constructor(private _cdRef ChangeDetectorRef, /* ... */){}

update(){
  // should update the tree view
  this._cdRef.markForCheck()
  this._cdRef.detectChanges()
}

Or you could try replacing this.model = JSON.parse(JSON.stringify(this.model)) with this.model = [ ...this.model ] which is in my opinion a little bit more elegant ! (but it will only change the top reference, I'm not 100% sure it will solve the issue since your mutation is nested)

from bosket.

awallat avatar awallat commented on May 23, 2024

Thanks for the info! Your recommendation for a copy of the model is much better. I'm fine with that.
For the best performance it would be better to get the tree node (as a reference by it's ID) and set there only the model. Then the whole tree would not have to be updated but only the sub tree. Don't know if this will be relevant, since I assume that updating the whole tree in most cases already is efficient enough.

I would love to see some useful functions in the TreeView, like "getNodeById(id)" and "expandPath(id1, id2, ...)", where the latter one returns a promise, so that I know when expand is finished (when using asynchronous children). It's probably a topic for another ticket, but I'd like to know your opinion about it. I find it quite difficult to implement something like this in a bulletproof way ... for example expand a deep node path and afterwards select some node there.

Thanks so far!

from bosket.

elbywan avatar elbywan commented on May 23, 2024

Hey @awallat, thanks for the feedback πŸ‘

For the best performance it would be better to get the tree node (as a reference by it's ID) and set there only the model. Then the whole tree would not have to be updated but only the sub tree.

The problem with this approach is that nodes are not rendered when folded. If you want to add a node dynamically, you could retrieve the related framework component node and refresh it manually, but nothing guarantees you that the node actually exists !

I think a good compromise if you want to optimize performance is updating the subtree and its direct ancestors in an immutable style. This way you don't have to meddle with the framework components, you just take advantage of the change detection algorithm.

I added the add and path functions which can do just that :

import {Β tree } from "@bosket/tools"

let model = [
    { label: "a", children: [
        { label: "b" }, 
        { label: "c" }]
    },
    { label: "d" },
    { label: "e", children: [
        { label: "f" }, 
        { label: "g", children: [
            { label: "h", children: [
                { label: "i" }
            ]}
        ]}
    ]}
]

// element "h"
const h = model[2].children[1].children[0]

// Returns the ancestors of the element "h" and itself
console.log(tree(model, "children").path(h).map(_ => _.label))
// also works with a matching function
console.log(tree(model, "children").path(_ => _.label === "h").map(_ => _.label))
// -> ["e", "g", "h"]

// Add "z" to the tree at position "h", with each ancestor reference updated
// Triggers angular change detection for only the relevant subtree
model = tree(model, "children").add(h, { label: "z" })
// also works with a matching function
model = tree(model, "children").add(_ => _.label === "h", { label: "z" })

I would love to see some useful functions in the TreeView, like "getNodeById(id)" and "expandPath(id1, id2, ...)", where the latter one returns a promise

I'm not too keen on exposing functions in angular components.
I'd rather empower the (framework agnostic) model tree which in turn should trigger the component(s) update.

About getNodeById(id) if it's about retrieving the TreeViewNode instances, devs can already access them by using @ViewChildren / @ContentChildren. If it's about retrieving a node from the model tree, the new pathmethod should cover the use case.

About expandPath(...ids), I will add another function (soon πŸ˜‰) in the @bosket/tools tree.js file which returns a promise after unwrapping X nested asynchronous children (promises).

Thanks again for your input, I hope that these answers are helpful !

from bosket.

fabien avatar fabien commented on May 23, 2024

I'm using Riot instead of Angular, and even though I can update the tree dynamically (my tree model uses an immutable data structure through https://github.com/Yomguithereal/baobab), the issue I'm having with updating the whole tree like that, is any unfolded/open nodes are closed again when the model changes.

What happens is similar to this setup: https://jsfiddle.net/b5wpavyx/

A workaround would involve being able to capture the open state of the tree, and reapply this after the tree (model) updates. Would this be a valid approach?

from bosket.

elbywan avatar elbywan commented on May 23, 2024

@fabien

I didn't check explicitely with baobab, but with this setup using riot and the provided immutable tree.add method I cannot reproduce your issue :

fiddle (Unfold 5, then 8, then click the button to add dynamically)

A workaround would involve being able to capture the open state of the tree

It would be tough, because the "expand" state is stored in the riot nodes.

What you could do is add an expand boolean property to your model, and a corresponding fold strategy which makes this property controls the folding/unfolding of the elements. Should not be too difficult I think.

from bosket.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.