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

support HTML-like syntax #207

Open
slimsag opened this issue Jun 24, 2018 · 7 comments
Open

support HTML-like syntax #207

slimsag opened this issue Jun 24, 2018 · 7 comments

Comments

@slimsag
Copy link
Member

slimsag commented Jun 24, 2018

Forewarning: This is just a thought I've had for a long time, but hadn't put into words. This is not a statement of "I intend to do this" but rather a place to discuss this topic.


While I am personally a fan of elem.Div et al., I notice that others are very much fans of HTML-like syntax akin to jsx and have gone out of their way to create "JSX for Go" e.g. https://github.com/8byt/gox

The primary drawback of putting HTML syntax inside of a language that did not originally support it, is that you lose all tooling because it is fundamentally a drastic syntax change. You would lose gofmt, goimports, syntax highlighting in your editor, and many others. In JavaScript, tooling had to adapt to support this syntax.

I've long had an idea for how we could solve this without losing any existing Go tooling. The idea is quite simple: just place the HTML in a Go string literal. For an example of what this would look like, consider this code:

// Render implements the vecty.Component interface.
func (p *PageView) Render() vecty.ComponentOrHTML {
	return elem.Body(
		// Display a textarea on the right-hand side of the page.
		elem.Div(
			vecty.Markup(
				vecty.Style("float", "right"),
			),
			elem.TextArea(
				vecty.Markup(
					vecty.Style("font-family", "monospace"),
					vecty.Property("rows", 14),
					vecty.Property("cols", 70),

					// When input is typed into the textarea, update the local
					// component state and rerender.
					event.Input(func(e *vecty.Event) {
						p.Input = e.Target.Get("value").String()
						vecty.Rerender(p)
					}),
				),
				vecty.Text(p.Input), // initial textarea text.
			),
		),

		// Render the markdown.
		&Markdown{Input: p.Input},
	)
}

With this new syntax, it would become:

// Render implements the vecty.Component interface.
func (p *PageView) Render() string {
    return `<body>
        {/* Display a textarea on the right-hand side of the page. */}
        <div style="float: right">
            <textarea style="font-family: monospace" rows=14 cols=70 onInput={func(e *vecty.Event) {
                // When input is typed into the textarea, update the local
                // component state and rerender.
                p.Input = e.Target.Get("value").String()
                vecty.Rerender(p)
            }}>
                {p.Input /* initial textarea text. */}
            </textarea>
        </div>
	{/* Render the markdown. */}
        <Markdown Input={p.Input} />
    </body>`
}

The benefit of such an approach is that:

  1. Users get Go/HTML-like syntax.
  2. Since it is just a string literal, all existing Go tooling continues to work with it.
  3. A Vecty tool could additionally perform type-checking for such code outside of runtime (e.g. by scanning the Go AST and finding the string literals). This would be an 'additive' approach.
  4. At runtime, Vecty would translate such HTML strings directly into plain Vecty code. The only cost would be the translation, which could be precompiled at build time if the runtime cost was large (for extremely large applications).

The questions that doing this would raise:

  1. More complexity. Is it actually worth it?
  2. Would we support solely this syntax, or also the elem.Div etc. syntax?
  3. Users of this syntax would want formatting, editor highlighting, etc. for such syntax regardless -- so we'd be on the hook for providing that long-term anyway.
  4. Probably many more things I haven't thought of.
@myitcv
Copy link

myitcv commented Jun 25, 2018

@slimsag if it's of any interest, with myitcv.io/react I do something similar, e.g.:

https://github.com/myitcv/react/blob/57b167914d27bc71eafc5447e5bf54dacb2a22a4/examples/sites/latency/latency.go#L82-L90

It's a very basic implementation for now and doesn't do any parsing of template fields (unlike your proposal).

Further enhancements that I had in mind include:

  • supporting template fields for attribute values
  • doing the parsing/template construction at compile-time (because I need to type-check my attribute values)
  • supporting something like https://github.com/8byt/gox, again at compile time

@ghost
Copy link

ghost commented Jun 25, 2018

This is brilliant. I have been using vecty only a bit and ended up having to use something custom specifically because some front end people spat the dummy about it.
But having a HTML template as a string would really solve the issue for me.

Dave has a HTML to vecty generator.

https://github.com/dave/html2vecty

Would this be useful ?

@progrium
Copy link

progrium commented Oct 3, 2018

Yes, I've wanted a templating alternative to elem.Div et al. Not just HTML, though, actual templating. But I've never been happy with */template or many of the alternatives. Plus they just produce a bunch of bytes. My approach was to take the Vue.js template syntax, which embeds structure/conditions/etc in HTML (so it's still valid HTML -- I've loved this approach since Python's Genshi), but parsed into Vecty objects similar to html2vecty there.

Work in progress:
https://github.com/progrium/prototypes/blob/master/wasm/main.go#L80-L92

Video about it:
https://www.youtube.com/watch?v=7svQTQklPds

(UPDATE) Better video about it:
https://www.youtube.com/watch?v=hQ3pwaPjjEM

No reason whatever this is has to be builtin to Vecty, too.

@mewmew
Copy link
Contributor

mewmew commented Dec 21, 2018

Hi @slimsag and everyone!

I just found out about Vecty today through the WebGL Go advent post.

After trying to grasp the main ideas behind the code and design of Vecty, my understanding of it is that Vecty is essentially an intermediate representation (IR) for constructing, manipulating and accessing the DOM tree, with event handlers and type safety that are usually not present when writing HTML/JS/CSS.

This is an attractive feature of Vecty.

The other obvious part of Vecty that I except to be quite intricate is the tracking of what components require rerendering after updates, analogous to a scene graph I'd imagine but I haven't looked at any details. I looked at the docfest but the rendering docs are still a TODO ;)

I tried to compile and play around with the Whisper Simulator by @divan who wrote the WebGL Go advent article, and as it so happens, one of the dependencies were not yet published so the build failed (divan/whispervis#1).

The great benefit of having the code base use the elem way of constructing the Vecty IR is that it way a trivial matter to identify at compile time what parts of the code I could comment out to simply not include the simulator component (that had not yet been published). Within a few minutes, the build passed, and I felt a sense of confidence that it would now work, even before rendering the page, the kind of confidence that type safe languages instill.

After commenting out the various parts of the code that had to do with the simulator component, and rebuilding with gopherjs build, it just worked!

2018-12-21-022859_1120x815_scrot

In large part this is a huge tribute to and speaks well for the compositionability of Vecty.

Taking a step back and reconsidering the scenario if the render components had used strings instead of the Vecty IR (i.e. the elem approach of DOM construction), then solving this at compile time would have been far more difficult. I would probably still have received compile time errors for other parts of the code (outside of Render) that referenced the simulator, but after having commented out those, there would still be references to the simulator component that the compiler would not be able to detect (Edit: I know @slimsag proposed an additional set of tooling to identify and report errors for these cases).

For these reason, and more generally for my personal belief that constructing a first-class IR representation is of huge value! Then, you can have more human readable, quicker to write representations that may be converted to this IR representation (using e.g. html2vecty).

It's funny though, I kind of consider the HTML code when written in Vecty elem AST/IR style to be more readable, as it's easier to see where one component starts and ends, and how they interact. I may be in a minority though, as most front-end developers probably have seen so much HTML code that they do this parsing automatically in their mind.

In either case, I wanted to raise this observation, and also present some view from someone new to Vecty. Perhaps not the kind of user you would target for, but as Vecty currently stands, I very much consider it a huge improvement over regular ways of developing front-end code. (and in my mind, the proposal to change Render to return a string is a regression in this regard).

Wish you all happy continued exploration, and thanks for working on this! I do believe Vecty has the potential to make a well-needed breakthrough in how we develop front-end code. In large part, as it's so easy to mix front-end and back-end code in the same development environment.

Following the predetermined way of other frameworks is good in the sense of more easily converting previous user bases. However, to creating something new and quite unique and hopefully influential, stepping outside of that comfort zone is great and you are already well headed down that path. Continue and see where it leads! :)

Cheers,
Robin

Edit: and just to show how effortless it was to remove the component related to the simulator, I uploaded the full patch required as a Gist https://gist.github.com/mewmew/b8d7ac088471ffa9b4655d3416437f9c (applied on divan/whispervis@c395bc3)

@divan
Copy link

divan commented Dec 21, 2018

@mewmew, just to note, it was totally my fault – I just forgot to make repo public, fixed now – but I'm glad it contributed to some fruitful discussion and ideas.

@zevdg
Copy link

zevdg commented Jan 14, 2019

I think I like this proposal, but we should be careful about the distinction between properties and attributes. This is particularly important in the context of custom elements. See all the talk of elements vs attributes in https://custom-elements-everywhere.com/. In the old syntax, it was explicit and in the new syntax, it appears to be implicit. Lit-html recently put some thought into this during their own syntax overhaul. The relevant part of the conclusion of that discussion was

Automatic property setting

This is pretty well ruled out, but I haven't discussed why in this repo yet. There are two big problems here:

  1. We greatly prefer explicit over implicit. I think reading templates and knowing whether an attribute or property is set without having to know about the implementation of the element is useful. Sometimes the automatic choice won't be correct, and then you need syntax to force either attributes or properties, and then you have two ways to do both - automatic and explicit. I think all together it ends up being confusing. I know Preact does this, but that's mostly because they don't have control of syntax in JSX to be explicit.
  2. We want to be compatible with upgrades. Pre-upgrade an element won't have many properties defined, so they'd be set as attributes, and then hopefully pulled in correctly by the element when it does upgrade. This won't work correctly for complex objects though.

Source: lit/lit#399 (comment)

I'm not saying that an implicit property/attribute heuristic is necessarily a bad thing, but in general, Go prefers to be explicit, so going in the other direction merits careful consideration.

@julienrbrt
Copy link

I personally really like the elem.Div syntax and al. This makes it look like Flutter and I find, make it easier to build component/widgets.

If you start to add HTML in Go files, you still lose a part of the Go tooling and that will add a need for VSCode, IntelliJ plugins and complicate the all thing. This has been a determining factor for me to chose between vecty and vugu.

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

No branches or pull requests

7 participants