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

Experimental @bind support [Discussion] #31

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

rcstuber
Copy link

@rcstuber rcstuber commented Jun 29, 2021

This PR implements a first draft of a potential built-in data-binding bridge for Domkit components, as mentioned in: #20

EDIT:

I updated the the implementation to do only the following:

  1. Detect all node attributes with code containing @bind
  2. Find all identifiers and field accessors within these expressions
  3. Extract those and wrap each one in a domkit.Macro.bindVar() macro call. This is done in order to delay replacement to the stage, where full type information is available.
  4. Wrap assignment to the Domkit component's property in a callback (__onVarChanged)
  5. Pass all expressions from bindVar to a dynamic function processBind for user land to implement.

Users will have to implement domkit.Macro.processBind and pass back an expression doing the actual data-binding logic. This logic calls __onVarChanged() in order to trigger a re-evaluation of the entire attribute expression and have the new value be assigned to the component's property.

Pros

  • Allows for values to be associated with @bind expressions and trigger a re-assignment. This is more in-line with common event driven data-models. Unlike per-expression based registration/callbacks as registerCheckRebuild does.
  • Makes no assumption on how the actual data-binding looks like and leaves this up to users to implement

Please feel free to make suggestions.

@rcstuber
Copy link
Author

rcstuber commented Jul 1, 2021

EDIT: I believe the current rebuild-system could be nicely merged with my suggested bind logic, if vars change blocks get reevaluated, rather than having two separate approaches

Something along these lines:

if( m.condition != null ) {
    var binding = remapBind(m.condition.cond);
    if(binding.isBound) {
	return macro {
		function __onVarChanged() {
			if( ${m.condition.cond} ) $b{exprs};
		}
		$b{binding.exprs};
		$e{
	            if(binding.isBound)
		            macro registerBind(__onVarChanged, $v{binding.name})
	            else macro {}
		}
		__onVarChanged();
	};
    } else {
        return macro if( ${m.condition.cond} ) $b{exprs};
    }
  }

rcstuber added 2 commits July 1, 2021 20:51
…ocessBind` macro function)

- moved bind macro call outside of re-evaluation callback
@rcstuber rcstuber mentioned this pull request Jul 1, 2021
@ncannasse
Copy link
Member

It's a bit difficult to analyze without reading the source code.
Could you post a domkit sample and how it's been expanded through macros ? (can be pseudo code just showing the principles)

@rcstuber
Copy link
Author

rcstuber commented Jul 2, 2021

This is only one possible approach. But the idea is always: Give each var in an attribute expression the possibility to trigger a re-evaluation (usually if the value of that field changed in a data model)

Edit: Updated the sample a bit

class UserModel {
    var firstName(default,set):String = "John";

    var lastName = "Wayne"; // same logic as firstName

    // setters are auto-generated
    function set_firstName(v) {
      firstName = v;
      triggerChange('firstName');
      return v;
    }

    public function onChange(fieldName:String, callback:Void->Void) {
        ... // register listener
    }
}

class DomkitComp {

    var SRC = <comp>
        <text id="label" text={@bind user.firstName + " " + user.lastName}
    </comp>;

    public function new(user:UserModel,?parent) {
      initComponent();
      trace(label.text); // "John Wayne"
      user.lastName = "McLane";
      trace(label.text); // "John McLane"
   }
}

// Compile Macro:
domkit.Macro.processBind = ( expr ) -> {
   // Pseudo-Code:
   if( expr.isFieldAccess(obj, field) && obj.isOfType(UserModel)) {
      return macro {
         obj.onChange(field, __onVarChanged); // "__onVarChanged" is defined before and triggers re-evaluation of the attribute expression and assignment to the component property
      }
  }
  return null; // if it's not something we are interested in, leave blank
}

Sorry for the formatting, typed this from my phone ;-)

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

Successfully merging this pull request may close these issues.

2 participants