localvoid / liquid

[UNMAINTAINED] Library to build User Interfaces in Dart [Virtual DOM]
http://localvoid.github.io/liquid/
BSD 2-Clause "Simplified" License
29 stars 3 forks source link

Replacing components with same structure #9

Closed Janamou closed 9 years ago

Janamou commented 9 years ago

Hi, thank you for the library, its great! Anyway I have problem and do not know if it is bug or feature :-)

I have two components which have totally same HTML structure but hold different types of data:

final vAListView = dynamicTreeFactory(({data, parent}) {
  return div()([
    h1()("A title"),
    ul()(data.map((i) {
        return vAView(
          key: i.id,
          data: i,
          parent: parent);
      }).toList())
  ]);
});

final vBListView = dynamicTreeFactory(({data, parent}) {
  return div()([
    h1()("B title"),
    ul()(data.map((i) {
        return vBView(
          key: i.id,
          data: i,
          parent: parent);
      }).toList())
  ]);
});

vAListView gets data like List<A> and vBListView gets data List<B>. A and B are different classes.

And then I have AppView where I show the component according to its state:

class AppView extends Component {

  build() {
    var children;

    if (stateA) { 
      children = vAListView(data: data, parent: this);
    } else {
      children = vBListView(data: data, parent: this);
    }
    return root()(children);   
  }
}

And my problem is that when components are changed. There stays data from first component. If I have List<A> listA = [a1, a2] and List<B> listB = [b1, b2, b3] I get a1, a2, b3 in the second component (e.g. they are "interleaved").

I know that problem is that they have same keys, but it is different component with different type of data. Should I have something like "global" key counter?

I did not have time to test it in ReactJS but from what I understand is that if the component is different type, the do not do diffs but replace it.

Thank you!

localvoid commented 9 years ago

I've removed staticTreeFactory and dynamicTreeFactory in git:master, there are only Components and componentFactory right now. Components shouldn't have this problem.

I thought that it will be a good idea to create lightweight virtual dom nodes instead of Components, but they just add more confusion, they also have slightly different semantics for "properties".

Also, there weren't any benefits in using dynamicTreeFactory compared to simple functions. Static Trees are now replaced by Components that have all properties immutable=true.

Transformer that is implemented in git:master can now automatically generate optimized update methods. For example, if all properties are immutable, Component will never update its subtree. If all properties have equalCheck=true, it will check all properties for equality using operator==, and if one of the properties is changed it will update subtree. By default, all properties that have basic types (int, double, num, String) have implicit equalCheck=true.

Janamou commented 9 years ago

Thank you for quick response but with Components I am having problem that they are not replaced.

AppView class - changes components according to state (which should be changed after onclick):

class AppView extends Component {

 void init() {
    element.onClick.listen((e) {
      invalidate();
    });
  }

  build() {
    var children;

    if (first) {
      children = views.vHeaderView();
      first = false;
    } else {
      children = views.vFooterView();
    }
    return root()(children);   
  }
}

This code does not work:

final vHeaderView = componentFactory(HeaderView);
class HeaderView extends Component {

  build() {
    return root()(h1()("hello header"));
  }
}

final vFooterView = componentFactory(FooterView);
class FooterView extends Component {

  build() {
    return root()(div()("hello footer"));
  }
}

The build() method of AppView class is called again, children is vFooterView() but the vHeaderView is not replaced with it.

When I change the code to this, it works:

vHeaderView() {
  return h1()("hello header");
}

vFooterView() {
  return div()("hello footer");
}

But this approach has same problem like dynamicTreeFactory -> works only for simple things (like this) but components with same structure - lists are "interleaved".

Am I doing something badly with Component class?

Thank you :)

localvoid commented 9 years ago

You are doing everything right, it is a bug with "dynamic(mirror-based)" version of Virtual Nodes for Components with implicit keys.

Thank you for reporting this bug :)

localvoid commented 9 years ago

It should work now with git:master version.

Janamou commented 9 years ago

Boris, you are awesome! Now it works, thank you very much :)

Btw, why is not actual version on pub.dartlang.org: https://pub.dartlang.org/packages/liquid? I don't usually check github if there is newer version but install packages directly from pub or through Dart editor.

localvoid commented 9 years ago

I want to check that I didn't make any huge mistakes with the current API before 0.3 release, so I am working on basic set of "material design" components https://github.com/localvoid/liquid-material It is highly experimental, I want to try the idea of moving css and svg icons into dart source code and generate this assets in runtime, this way it will be possible to use dart "tree shaking" to automatically remove all unused assets, "deferred loading" to split content into different parts, etc.

I think that I'll release 0.3 to pub in a week or two.