Commit Graph

30 Commits

Author SHA1 Message Date
Ryan Nowak 6f383a0f0f Adds support for 'Context' parameters on components (#1470)
Adds support for 'Context' parameters on components

This change allows you to set the parameter name of a parameterized child content by using the `Context` attribute on the component. The `Context` attribute will be defined (and shown by completion) when the component has one or more declared parameterized (`RenderFragment<>`) child content parameters.

This is nice for cases where you are using implicit child content:

```
<ol>
  <Repeater Items="People" Context="person">
    <li>@person.FirstName</li>
  </Repeater>
</ol>
```

 or, when you have multiple child content elements and want them all to have the same parameter name:

 ```
<MyComponent Items="People" Context="person">
    <ChildContent1><div>@person.FirstName</div></ChildContent1>
    <ChildContent2><div>@person.LastName</div></ChildContent2>
</Repeater>
```

The parameter name can be overridden by using the `Context` parameter on the child content element:

 ```
<MyComponent Items="People" Context="person">
    <ChildContent1 Context="item"><div>@item.FirstName</div></ChildContent1>
    <ChildContent2><div>@person.LastName</div></ChildContent2>
</Repeater>
```

If the component defines a `Context` parameter already then we won't synthesize one - your component's parameter will work exactly as it did before this feature.
2018-09-24 12:22:16 -07:00
Ryan Nowak 100382bf71 Add Type Inference for Generic-Typed Components
This change allows you to use generic-typed components without
explicitly specify type arguments.

**Before:**
```
<Grid Items="@MyItems" TItem="Person">
...
</Grid>
```

**After:**
```
<Grid Items="@MyItems">
...
</Grid>
```

Where possible, the type of the component will be inferred based on the
values passed to component parameters. This won't work like magic, you
have to specify parameters that provide type arguments for all of the
component's type parameters.

This is a best effort system, and it's largely based on the limitations
and behaviours of generic type inference in C#. We think it will work
well with most Blazor features and for most reasonable components. You
should not forget it's easy to specify type arguments, because you may
still have to in some cases.

In particular you may notice issues if you are trying to use a generic
typed component where all of the parameters are delegates or templates.
Type inference for delegates/lambdas in C# is based on the context. Any
time you combine generics and delegates it's easy to get into a scenario
where the compiler won't infer the correct type, or will give up.

----
The type inference feature works by generating a 'thunk' method in
*caller* that can act as a site for type inference (C# does not support
inference on constructors).

For our grid example above, the non-inferenced code will look something
like:
```
builder.OpenComponent<Grid<Person>>(0);
builder.AddAttribute(1, "Items", MyItems);
builder.CloseComponent();
```

Note that we can only write the type `Grid<Person>` because you wrote
`Person` in your code. What you type in the `TItem` attribute value gets
inserted into the generated code such that it fills in the generic type
parameter.

On the other hand, if you want is to infer the type, we have to do some
compiler magic. That looks like:
```
__Blazor.(long generated name).TypeInference.CreateGrid_0();
...

// elsewhere in the file
internal static class TypeInference
{
    public static void CreateGrid_0<TItem>(RenderTreeBuilder builder,
    int seq, int __seq0, global::System.Collections.Generic.List<TItem>
    __arg0)
        {
	        builder.OpenComponent<Grid<TItem>>(seq);
		        builder.AddAttribute(__seq0, "Items", __arg0);
			        builder.CloseComponent();
				    }
				    }
				    ```

				    This allows us to rely on the C#
				    compiler for itype inference.
2018-09-20 12:19:26 -07:00
Ryan Nowak 354752cf16 Add support for generic-typed components (#1417)
* Add the ability to discover generic types/properties

* Add @typeparam directive

Adds the ability to declare a generic-typed component using Razor.

* Add a 'type parameter' property to generic components

* Adds the ability to consume generic-typed components
2018-09-16 14:01:15 -07:00
Ryan Nowak d4cbb86f46 Add Support for Templated Components (#1404)
* Test namespace cleanup

* Add recognication for RenderFragment in tag helpers

* Remove dead code from node writers

* refactor type check

* Continue to treat child content as a delegate in codegen

* Add extension to enumerate child content

* Reorganize code generation tests

These were growing a bit disorganized, and weren't really result in good
code reuse.

* fix test base class

* Add some child-content tests

* Add an explicit node for ChildContent

Adds a strongly typed node to represent a 'ChildContent' and what it
contains. This allows us to simplify the code generation path,
detect/processes more issues in IR passes, and will be essential for
supporting multiple child content.

* Ignore ChildContent in components when it's just whitespace

* Add diagnostic for duplicate child content

* Add support for explicit child content elements

Precursor to support for multiple child content items

* Add support for multiple child-content elements

* Change delegate signature for RenderFragment<T>

* Clean up Tag Helper constants

* Allow RenderFragment<T> as a child content

* Allow renaming the template parameter

* Improve error message for invalid child content

* Add diagnostic for repeated child content parameter names
2018-09-10 18:59:51 -07:00
Ryan Nowak c97cb8c18b Add support for Razor templates
Adds support for Razor templates and RenderFragment<T>.

Razor templates are a little-known Razor feature that looks like:
```
@<tag>....<tag>
```

It's so little known that it's not even covered in our docs, but it's
been around for many many years. This features hasn't been implemented
until now for Blazor, and this feature brings it back as a build
building block for templated components (more to come).

In Blazor land a template like:
```
@{ RenderFragment<Person> template = @<div>@item.Name</div>; }
```

complies to code like:
```
RenderFragment<Person> template = (__builder, item) =>
{
    __builder.OpenElement(...);
    ...
    __builder.CloseElement(...);
}
```

Since the declaration always has a generic type parameter inside, it
needs to be in a context where the type is known.. ie: not with `var`.

See tests for ways to consume templates.

NOTE: There are the following caveats for templates
- Templates require a single root element.
- Templates don't work in the `@functions { }` block

These limitations are baked into the core of Razor and will take a while
for us to address (v3.0).
2018-08-31 19:10:42 -07:00
Ryan Nowak fd5426943f Merge sibling nodes during markup block rewrite
This change adds the ability to merge sibling nodes when possible during
markup block rewriting. We retain that invariant that each markup block
is a valid chunk of markup containing properly nested tags.

We still haven't done any work to remove whitespace yet, so most of the
cases where this comes into play right now will merge an element with
its surrounding whitespace.
2018-08-13 11:17:11 -07:00
Ryan Nowak a05cb42845 Reenable markup blocks (#1286)
* Reenable HtmlBlock unit tests

* Add E2E tests for HTML Block cases

* Remove harded GenerateBaselines=true

* Fix #1193

This commit addresses the root cause of #1193. When we merge HTML
text nodes into HTML blocks we need to re-encode any HTML entities that
were encoded eariler.

I did a bit of a deep dive on how HTML encoding is handled in Blazor and
I think this is the best strategy. I think it's valuable that the
BrowserRenderer uses document.createTextNode, which will always encode
the text - this handles dynamic content. We want to keep this in place
to avoid HTML injection attacks.

* Fix #1265 Reenable MarkupBlock

* test cleanup
2018-08-10 16:29:39 -07:00
Steve Sanderson 4b861929be Temporarily disable HtmlBlockPass for 0.5.1. Will be re-enabled later. 2018-07-27 10:11:31 -07:00
Ryan Nowak 8b3f26b962 Fix #1169 ignore DOCTYPE
This change will cause the compiler to ignore <!DOCTYPE ...>
declarations in Blazor components. We don't think there's much useful
Blazor can do with doctype, since we don't generate textual output for
the browser the parse. The sanest thing to do for now is just to skip
over it.
2018-07-23 17:42:47 -07:00
Ryan Nowak 8f072a0711 Add HTML Block rewriting (#1146)
* Add HTML Block rewriter

* Baseline updates

* test gaps

* Update some unit tests to represent same behavior as before

* Define Markup frame type. Tests for rendering markup frames into render tree.

* Support markup frames during diffing (retain, insert, update, delete)

* Support markup blocks on WebAssembly

* Support rendering markup frames with server-side execution too

* Support markup blocks with multiple top-level nodes. Support updating markup dynamically.

* Define MarkupString type as a way to insert dynamic markup without needing custom RenderFragment code

* Remove comment

* CR: Better null value handling
2018-07-23 18:18:07 +01:00
Ryan Nowak 45544858a3 Fix #954 - use weak typing for 'event handlers'
This change introduces a mechanism for bypassing type checking and then
uses for the 'event handlers'. The event handler tag helpers have some
ideosyncratic behaviors and rely on overloading at the render tree
builder level.
2018-07-05 09:15:01 -07:00
Ryan Nowak cb186f494a Add type checking for component parameters
Introduces a new primitive used by the compiler for type checking. Type
checking applies to component parameters when setting the value directly
and when using bind. This is nice because it also adds error checking
for bind.
2018-06-05 19:58:50 -07:00
Steve Sanderson 7f3ba97fb7 Handle ChildContent that starts with an HTML element. Fixes #875 2018-05-23 15:57:48 +01:00
Ryan Nowak e801707706 Add regression test for 609 2018-05-22 13:58:04 -07:00
Ryan Nowak c6148b6fe6 Fix #784 - allow complex content for HTML
The logic that binds event handlers was interfering with the code that
prevents component properties from receiving complex content.

This check was a little overzealous.
2018-05-22 11:58:46 -07:00
Ryan Nowak e4cf7a6d3e Fix #773 (for real this time)
The problem is that the new HTML rewrite pass was traversing into
attributes of all kinds and would turn any HTML content inside those
attributes into elements where possible. The solution is to not do
that.
2018-05-14 10:47:36 -07:00
Ryan Nowak e1c2efb5ce Add tests for #773
This doesn't seem to repro anymore after fixing #772. Adding tests
anyway.
2018-05-08 13:38:23 -07:00
Ryan Nowak 3f5d25d314 Fix 773
We weren't handling a few cases that can occur during typing correctly.
Our passes that look at the content of attributes need to be prepared
for it to be empty in cases where the attribute has been partially
typed in the editor.

I added a smoke test for this that attempts to simulate typing and found
another issue to fix.

The end result of this is that the design for this kind of code is
simpler and takes a more 'brute-force' approach to understanding
attributes. I think this is a good change based on the problems with how
this code has been written today, there are too many possible cases to
try and have the code express and document them all.
2018-05-08 12:39:18 -07:00
Steve Sanderson 37788f3c9d In Blazor cshtml, auto-reference Microsoft.AspNetCore.Blazor and .Components (#751)
* In Blazor cshtml files, auto-import Microsoft.AspNetCore.Blazor and Microsoft.AspNetCore.Blazor.Components. Fixes #749

* Remove redundant @using directives from tests

* Update assertion in test

* Update all affected baselines
2018-05-05 17:55:08 +01:00
Ryan Nowak b390ae0c1c Rewrite of HTML handling for Blazor
This change replaces the parsing of HTML that we perform during the code
generation phase, which parsing of HTML during the IR lowering phase.
The main benefit of this change is that the structure of the HTML is
reflected in the IR tree, allowing us to do more more advance
transformations.

As an example, see how the the handling of `<script>` tags is now a
separate pass.

As an aside from this I also redesigned the structure of component IR
nodes to match the new HTML element nodes. Passes are now more easily
aware of the nodes they are expected to handle and are more easily aware
of the difference between a component and element. This still isn't as
clean as I would like, but I think it's a reasonable improvement.

Another benefit of this is that the code generation is much simpler and
requires much less bookkeeping and statefulness.
2018-05-03 21:56:03 -07:00
Steve Sanderson f61ed4df4f For checkboxes, bind to 'checked'. Fix special property handling in BrowserRenderer.ts. Fixes #659 and #703
* Stop the value<-->checked conversions for checkboxes. Just use native 'checked' property. Fixes #703

* Properly handle removal of 'checked' and 'value' attributes

* E2E coverage for removing 'value'
2018-05-01 16:40:07 +01:00
Steve Sanderson 4033560734 Support 'ref' syntax for capturing references to elements and components (#685) 2018-04-27 17:41:21 +01:00
Ryan Nowak ed06d7b12e Rough cut at async events 2018-04-26 13:31:28 -07:00
Steve Sanderson 60dcc6e568 In Razor compilation, trim leading and trailing whitespace nodes (#586)
* In Razor compilation, trim leading and trailing whitespace

* Update all unit tests to account for whitespace trimming

* Recognize that TagHelperIntermediateNode produces output too

* Skip TrimWhitespacePass during first phase of two-phase compile

* Skip TrimWhitespacePass during design-time builds

* Update baselines after rebase
2018-04-24 21:44:30 +01:00
Ryan Nowak c152ed9e2a Update baseline linendings 2018-04-23 21:48:13 -07:00
Ryan Nowak f5dc6df13d Fix #597 Allow multiple tokens in attributes
The issue here is that we were missing tests for the design time code
path. We have tests that the bind-... cases work at runtime but were
missing coverage for the editor.

I took the most relevant set of the tests for running bind-... code and
added them to the tests for codegen.
2018-04-19 16:14:10 -04:00
Steve Sanderson ce3c79e051 Reject script tags in components, but allow overriding. Fixes #552 (#553) 2018-04-12 16:28:31 +01:00
Ryan Nowak 8485e2ea10 Add support for Action event handlers
This change adds `Action` to the set of types that have an overload on
RenderTreeBuilder. Additionally, we special case `Action` in the runtime
because passing the event args via DynamicInvoke() would throw.

Finally, reverted some of the clutter introduced by the first pass of
the event handler feature.
2018-04-11 07:36:05 -07:00
Ryan Nowak c3366bc956 Add event handlers as tag helpers
This change adds support for mapping DOM event handlers as tag helpers
that function in a bi-modal way.

This is a new first-class feature for DOM events, and replaces a few
workarounds like using `@onclick(...)` or `click=@{ ... }`. I haven't
removed those things yet, this is a first pass to get the new support
in, we'll remove those things when we're totally satisfied.

When used with a string like `<button onclick="foo" />` the result is
a simple HTML attribute .

But when used with an implicit expression like
`<button onclick="@Foo" />` or
`<button onclick="@(x => Clicked = true)" />` a C# function is bound to
the click event from the DOM.
2018-04-06 13:00:04 -07:00
Ryan Nowak 707e781e5d Add Razor baseline test infrastructure
Ports somee infrastructure and converts Razor code generation tests to use
it. This makes it much easier to make cross cutting changes to code
generation and see the effect.

Use build /p:GenerateBaselines=true to update all of the generated code
in place or when adding new tests. Generally if tests are failing, the
easiest thing to do is to update the baselines and do a git diff to see
what the deltas are.

The changes to the tests here are to use the new baseline infrastructure
and to rename classes/methods to result in shorter file paths.
2018-04-04 08:05:59 -07:00