Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rebuild / Bind #20

Closed
ncannasse opened this issue Aug 28, 2020 · 6 comments
Closed

Rebuild / Bind #20

ncannasse opened this issue Aug 28, 2020 · 6 comments

Comments

@ncannasse
Copy link
Member

In order to allow for UI self-rebuild and automatic update within DomkitML declaration, we will introduce new metadata:

<flow class="icons" if( @rebuild isMine() )>
  for( i in @rebuild getIcons() )
     <icon(i) value={@bind i.progress}/>
</flow>

Gets translated to:

<flow class="icons" if( registerCheckRebuild(()->isMine()) )>
  for( i in registerCheckRebuild(() -> getIcons()) ) {
     <icon(i) id="__tmp"/>
     registerBind(() __tmp.value = i.progress);
  }
</flow>

registerCheckRebuild can then be implemented on the framework base class to perform regular checks and rebuild the UI if data has changed, and registerBind for updating values regularly.

We also need something like:

@rebuild var p = getStruct();
for( i in @rebuild p.icons ) {
}

That translates to:

var p;
registerCheckRebuild(() -> { p = getStruct(); true; });
for( i in registerCheckRebuild(() -> p.icons) ) {
}

So some vars can be updated before following rebuilds take place.

@rcstuber
Copy link

rcstuber commented Jun 24, 2021

What's the status of this? I see there is an implementation for @rebuild, but @bind logic is still missing. Any suggestions how to handle data-binding with DomKit?

EDIT: I'm currently working on an implementation for @Bind, which supports both component-centric "global" binding as well as binding to single members of data-models within assignments, which expose themselves as "bind-functions" in that they accept a callback of signature Void->Void – for triggering a rebuild – and return the members current value immediately. I'm evaluating possible edge cases before opening a PR for discussion

@ncannasse
Copy link
Member Author

ncannasse commented Jul 1, 2021

Sorry for the answer delay ;)
You're correct that @rebuild is correctly implemented and @bind is not.

The code I'm using @rebuild is the following:

@:uiInitFunction(rebuild) @:uiNoComponent
class BaseComponent extends h2d.Object {
	var bindList : Array<Void->Void> = [];

	function registerCheckRebuild<T>( f : Void -> T ) : T {
		var value = f();
		registerBind(() -> {
			var value2 = f();
			var arr2 = Std.downcast((value2:Dynamic),Array);
			if( arr2 != null ) {
				var arr1 = Std.downcast((value:Dynamic),Array);
				if( arr1 == null || arr1.length != arr2.length ) {
					rebuild();
					return;
				}
				for( i in 0...arr1.length )
					if( arr1[i] != arr2[i] ) {
						rebuild();
						return;
					}
			} else {
				if( value != value2 ) {
					rebuild();
					return;
				}
			}
		});
		return value;
	}

	function registerBind( f : Void -> Void ) {
		bindList.push(f);
		f();
	}

	override function sync(ctx) {
		var cur = bindList;
		for( f in cur ) {
			f();
			if( bindList != cur ) {
				dom.applyStyle(ui.style, true);
				break; // was rebuilt
			}
		}
		super.sync(ctx);
	}

	public function rebuild() {
		bindList = [];
		removeChildren();
        }
}

The idea is that each app/framework can decide itself what it means by "data has been modified and needs rebuild", thus not requiring domkit to make strong assumptions on specific things.

@rcstuber
Copy link

rcstuber commented Jul 1, 2021

Thanks, finally I get a clearer picture of how this should work. I see a few issues with this for my use-case though:

  • Requires polling for changes, potentially checking a lot of unnecessary expressions before finding a change
  • Rebuilds the entire component, even if only parts of it require changes
  • Re-renders entire lists if items get added/removed, potentially causing flickering or lag

Please have a look at my PR for @bind logic (#31). It allows for expressions to only re-evaluate if a variable's value within it has changed, being more of an event-driven solution. I made it so that it also makes no assumptions of how the actual binding looks like and thus is very data-layer agnostic.

Maybe something similar could be added for binding if-conditions & for-loops; along-side the current approach that is.

@ncannasse
Copy link
Member Author

  • Polling : sure, but I'm not sure how we can do that without polling
  • Entire rebuild : please note that it's only the case for @rebuild, and this should only be used when you have an if or for loop in domkit that changes its content
  • there should not be any flickering, and we do use rebuilds in our games without perceptive lag

@rcstuber
Copy link

rcstuber commented Jul 3, 2021

  • Polling : sure, but I'm not sure how we can do that without polling

Could be done like my @bind implementation, where if parts of an expression change the entire expression is re-evaluated (event-driven)

  • Entire rebuild : please note that it's only the case for @rebuild, and this should only be used when you have an if or for loop in domkit that changes its content

Yes, I understand that only @rebuild annotated expressions are affected, but a component may have several of those and the entire component gets rebuild if any of those change, because there is no reference as to which sub-tree the expression refers to

  • there should not be any flickering, and we do use rebuilds in our games without perceptive lag

I have some lists where the dom.active property (and styling) gets lost if an entire list gets re-rendered if any element changes

I saw that the UI in Northgard and Darksburg are looking fine ;-) But UI in my game is currently a flickering nightmare due to the lack of data-binding and minimalistic updates, because I have a lot going on in terms of realtime data

@ncannasse
Copy link
Member Author

I added @Bind support

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants