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

Add "value( v : object )" in AttrEngine #15

Closed
davedawkins opened this issue Feb 28, 2021 · 6 comments
Closed

Add "value( v : object )" in AttrEngine #15

davedawkins opened this issue Feb 28, 2021 · 6 comments
Assignees

Comments

@davedawkins
Copy link
Collaborator

I can work around it with a specialization if I have to, but frameworks can do things like cache a reference to the JS value that was set (as Sutil does, and possibly Svelte). Having to stringify the value loses information that's useful for binding functionality.

@davedawkins davedawkins self-assigned this Feb 28, 2021
@alfonsogarciacaro
Copy link
Owner

Yes, I was thinking about this. At the end I used string to avoid upcasting by mistake, but I guess we'll need the flexibility of obj. Are you thinking in some specific members or the general MakeAttr helper?

@davedawkins
Copy link
Collaborator Author

davedawkins commented Mar 1, 2021 via email

@davedawkins
Copy link
Collaborator Author

My workaround is to overload my derived AttrEngine as follows, and my immediate requirement is satisified

type SutilAttrEngine(helper) =
    inherit AttrEngine<NodeFactory>(helper)

    member _.disabled<'T> (value: IObservable<'T>) = bindAttrIn "disabled" value

    member _.value(value:obj)   = attr("value",value)
    member _.value(value:int)   = attr("value",value)
    member _.value(value:float) = attr("value",value)
    member _.value(value:bool)  = attr("value",value)

@davedawkins
Copy link
Collaborator Author

I think the engine is justified in converting everything to string (since HTML attributes are string based), but I would like to see it performed in such a way that we can capture the original (typed) value in the DSL implementation before this happens, and control how it is performed. For example, we currently have

        member _.foo( value : int ) = h.MakeAttr( "foo", Util.AsString(value) )

if it was more like this

        member _.foo( value : int ) = h.MakeAttr( "foo", value )

with overloads in the Helper class like this:

type AttrHelper<'Node> =
    abstract MakeAttr: key: string * value: string -> 'Node
    abstract MakeAttr: key: string * value: int -> 'Node
    abstract MakeAttr: key: string * value: float -> 'Node
    abstract MakeAttr: key: string * value: bool -> 'Node

then we have given the implementer complete control over how attrName:string * 'T is converted to 'Node

@alfonsogarciacaro
Copy link
Owner

alfonsogarciacaro commented Mar 10, 2021

Hi David! Again, thanks a lot for the discussion and the comments, I've thinking (and coding) through this. Probably not exactly what you wanted 😅 but I've made the following changes:

  • I've removed the non-string overloads for value. I agree this is a special case and the engine shouldn't try to swallow the non-string arguments and leave this for the specific implementation.
  • I've removed the float overloads when there was an alternative with ICssUnit for consistency with the cleanup made in the CssEngine.
  • I've changed the AttrHelper interface to function arguments, as this is the recommended way to pass behaviour in F#. This way, if in the future we need to provide further customization we can do it with an optional argument to avoid a breaking change. Now I need to do the same for the other engines 😅

So the question is, why have I not added function arguments to convert int and float attributes? (Boolean attributes are already a special case because they have different syntax in HTML.) I actually did it, but I realized there were several conflicting cases so I was worried this could cause more confusion.

For example, baseFrequency has multiple overloads: int/float/int * int/float * float. With the extra configuration maybe you could intercept the cases with one argument, but for two arguments (horizontal and vertical frequencies), AttrEngine will already convert them to string and concat them with ", ", would that be expected? Also, the ICssUnit overloads usually have an int alternative, would it be ok to intercept int values while css units get automatically converted to string?

So that's basically the reason I refrained from adding extra arguments for making attributes from ints or floats. Besides the value attribute, would you still need to be able to intercept calls to, say, baseFrequency(float) or cx(int), even when missing baseFrequency(float, float) and cx(ICssUnit)?

@davedawkins
Copy link
Collaborator Author

Again, wow. Thank you. I will be checking this out.

I had actually convinced myself during discussion that value shouldn't be treated differently in the engine, but allow implementations to add any speciality they require (to any attribute).

I think though (I need to actually look at the code) that you've given all options here, so again, thank you.

Incredible response.

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