marko-js / marko

A declarative, HTML-based language that makes building web apps fun
https://markojs.com/
MIT License
13.25k stars 643 forks source link

Proposal: Replace HTML parser with a new parser that recognizes attribute types #90

Closed patrick-steele-idem closed 8 years ago

patrick-steele-idem commented 9 years ago

Currently, Marko is using an HTML parser. An HTML parser treats every attribute value as a String type regardless of how it is written.

For example, with an HTML parser the following lines are all equivalent:

<my-component foo=123/>
<my-component foo="123"/>
<my-component foo='123'/>

As a result of this limitation, Marko relies on custom tag schemas to associate a type with an attribute value or the developer must use the ${<javascript-expression} syntax. Tag schemas are great for documentation but they are a pain to maintain and they make usage of a custom tag ambiguous. Not to mention, the ${<javascript-expression} syntax can only be used with attributes that have a string type (not expression, boolean, etc.).

The author of the <my-component> tag can separately declare the type for an attribute and developer viewing a template wouldn't know the attribute type unless you consulted the tag schema: It would be great if the parser used by Marko treated all attribute values as JavaScript expressions. Spaces will end an attribute value unless the space is in a quoted string or within a parenthesis/square brackets/curly braces block. Finally, we would support ${<javascript-expression>} usage within ES6 Template strings (i.e. strings "quoted" using backticks). The following illustrates what is being proposed.

<my-component number=1/>
<my-component variable=name/>
<my-component complex-expression=1+2/>
<my-component complex-expression-with-spaces=(a + b)/>
<my-component simple-string="hello"/>
<my-component simple-string='hello'/>
<my-component dynamic-string=`hello ${name}`/>
<my-component string-concatenated=("hello "+name)/>
<my-component boolean=true/>
<my-component array=[1, 2, 3]/>
<my-component object={hello: 'world'}/>
<my-component function-call=data.foo()/>
<my-component complex-function-call=(data.foo() + data.bar())/>

Now that we are using a custom parser, we can also improve the syntax for conditionals and looping as @onemrkarthik suggested:

<div if(x > 5)>
   Do something
</div>

<ul>
   <li for(color in colors)>
       ${color}
   </li>
</ul>

<ul>
   <li for(color in colors; status-var=loop)>
       ${loop.getIndex+1}) ${color}
   </li>
</ul>

Here's what for-loops and conditionals would look like as tags:

<if(x > 5)>
    <div>
        Do something
    </div>
</if>

<ul>
    <for(color in colors)>
        <li>
            ${color}
        </li>
    </for>
</ul>

<ul>
    <for(color in colors; status-var=loop)>
        <li>
            ${loop.getIndex+1}) ${color}
        </li>
    </for>
</ul>

Thoughts? Concerns?

yomed commented 8 years ago

@patrick-steele-idem Sure, and it isn't necessarily bad to support multiple styles, it's just additional things to maintain, document, etc.

re: conditionals - I actually think that looks better. It is uglier, but it's standard js.

patrick-steele-idem commented 8 years ago

Thanks for the feedback @yomed. I'm leaning towards standard JS as well.

mlrawlings commented 8 years ago

Regarding conditionals, depending on how a falsy value is handled for the class attribute, the following would work... and it looks quite nice.

<div class=(data.isActive && 'active')>
patrick-steele-idem commented 8 years ago

I agree @mlrawlings. That's definitely allowed in Marko v3 and would work as expected. Thanks for pointing that out. I'm definitely looking forward to the new Marko v3 syntax :)

patrick-steele-idem commented 8 years ago

This feature has been implemented.