-
Notifications
You must be signed in to change notification settings - Fork 9.8k
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
Template based components #16308
Comments
Specifically, this means we need some way to signal that a given part of the child content within a component should get compiled as an Example:
I'm not sure how general we want to make this feature. Possibly less general than indicated in the sketch above. @rynowak would be the expert on what's reasonable to implement here. All the complexity is at design/compile time. It doesn't require any runtime support that I can think of. |
Hi tried to accomplish this by my own passing an IEnumerable as a parameter, I was able to produce a grid but i was not able to update it when the parameter change. This is the code:
|
@SteveSandersonMS, are I am working with XAML a lot and when I started prototyping with Blazor I quickly felt the need for this feature. Do you think XAML's property element syntax could be reasonable in this scenario? While quite verbose, it expresses clearly that we are assigning properties here instead of adding child components to
|
My personal view is that Razor should stick as close as possible to HTML idioms, rather than drifting towards the idioms we see in XAML. That would mean looking more like the hierarchy of elements you see in |
totally in support of this. if we can make the list of item "ObservableCollection" rather than a "IEnumerable" that would be a bonus. moving towards xaml like pattrens gives us a proper template system, which html lacks, all of popular frameworks in web ui have a template system, which makes them very extensible, obviously blazor should have one as well, and instead of making a new one it would be great to have one which is already loved by MS and many .Net devs as well, technically it will still be html/css based so all html developers will still be attracted towards the technology because it wont be xaml ( dont knw why html guyx hate xaml though ) but it will only follow xaml pattrens. |
@DamianEdwards I fully agree with you regarding "Razor is HTML". However, attribute values in HTML are always specified as, well, attributes in the opening tag. There is no way to (and no need to) express complex attribute values like templates as child elements. For example you can't write
So I think what we are aiming for technically just cannot be expressed in pure HTML.
It is totally possible that I am misunderstanding something here. If so, please let me know. |
@SvenEV I dont think xaml syntax will alianate anyone, if u notice, the template syntax for angular, vue, react and million other frameworks, all have different way and different syntax to do stuff, still all of them are loved, NO? similarly blazor can have its own syntax, the only pro in its case will be we wont be inventing a new syntax just bringing over already popular syntax to this framework ( hence less learning curve for xaml devs ) but as far as web devs are concered they will be learning blazor templates anyway whether blazor follow xaml or not, learning curve for web devs remain the same, the only way web devs stay away from this drawback is if blazor uses straight up html, which I dont think will be as extensible as a template model. :) |
so a totally new template syntax gives learning curve to every developer. but a xaml like syntax solves atleast problem of half developers. but xaml is also a lot like html, both are markup afterall. once u learn it, it will be piece of cake, good thing abt xaml way is, u dnt have to worry abt css at all, all styling is done with straight up properties on the controls. and even comples styling also done with same syntax, but on the other hand html and css are totally different languages with different syntax and structure of doing things. |
But why do we even need templates? I mean when you can do foreach right there in Razor. Seems much easier to me than templating. |
@MihaMarkic templates standardize everything, it increases re-use, and most importantly it hides away all the complex code written for one control and u can just use that wrap control with 2 lines of code and focus more on ur business logic instead of re inventing the wheel. |
Well, I don't know about this. IMO foreach is a template, just a more powerful one and you can place a "foreach" template in a different component and call it from anywhere with those two lines of code. |
foreach is just a loop and yes for a simpler control we can do this, like for a basic ListView ( table ) we can simply make a control which takes some input, does the foreachon it and spits out a table, and then we just need to put one tag <CustomTable Input=@"ListData"/> something like this and then we will be able to produce a table, also if we want a customized listview , with very modern design, highly interactive etc, then we can make another component control with foreach, and a lot of cutom css, and lot of blazor code in one file, maybe multiple files, and wrap all of that in 1 component, and then again use all of that just with 1 tag. you see where I am getting with this? no matter how much code a control has, all of that can be wrapped into 1 tag :) @MihaMarkic |
It's not just about rendering multiple items with the same template. It's also needed to make multiple parts of a component customizable. Currently, a component can only have one such "hole" in its layout that can be filled with
Now imagine you want to write a component with multiple such "holes" like a dialog that lets you customize the content as well as the header, or a button where you can customize the inner content but also specify templates for the context menu and the tooltip maybe. So it would be nice if you could write something like the following (whatever the syntax will be then):
|
@SvenEV Isn't that just having multiple holes and not strictly requiring a template? |
yes tht is good example of another usecase, every part should be customizeable. |
@MihaMarkic It's true, we are talking about two different things here:
I think the first is a requirement for the second and I hope that both features will be implemented at some point. That would enable us to build some powerful component libraries. |
Which problem is this trying to solve? I'm just wondering since I haven't seen this kind of thing in other SPA frameworks. Does it conflict with a component based structure? Also, does it go against normal web development practices just to force a more "app-like" structure? |
@awulkan it doesnt go against normal component structure, but yeah it does go slightly towards app-dev like structure, but it doesn't mean it goes away from web development, bcz all popular frameworks are also doing the same thing, the whole purpose of SPA concept was this. SPA was created to make web development more like app development. and now every framework is following spa. the next step of spa is now pwa. u knw wht a pwa is right? it introduces some major features from a native apps like notifications and offline sync, so ultimately web dev as a whole is trying to follow native app development. |
@awulkan Since you seem to be familiar with Vue, I just looked into the Vue docs to see if there's something similar. There is and it's called "Slots", in particular "Named Slots" and "Scoped Slots" - that's exactly what this issue is about. It's the same concept. Maybe this helps you understand the issue? |
@SvenEV Okey I kinda see what you mean then. It's a pretty niche feature as far as I'm concerned. Thanks for clarifying. Will it be using this draft as a model? |
Seems Vue.js slots is similar to XAML data templating.
With templating you could override that to say, show instead a list of image tags using the passed
This would greatly increase reusability of components as they can be dynamic. 3rd party component vendors can be more creative on authoring components that way. |
yes exactly with data template binding gets simpler and observable |
We very much hope that Blazor components will get a good support for nesting and templating. We develop UI components such as data grids and the typical markup to configure a widget can be as twiggy as this: <TabPanel>
<Grid>
<DataSource>
<More about data source />
</DataSource>
<Column />
<Column>
<Lookup>
<DataSource>
. . .
<CellTemplate>
arbitrary html
</CellTemplate>
</Column>
<DetailTemplate>
<div>arbitrary content</div>
<ThirdPartyComponent />
<Grid>
detail grid configuration with own templates (and there may be more levels)
</Grid>
</DetailTemplate>
</Grid>
</TabPanel> For such a hierarchy, a developer would want to have accurate context awareness and intellisense. |
@AlekseyMartynov can we have xaml like styling + animations as well in the long run ? |
I'm so glad this idea is getting traction. Since MS pioneered the idea of templatable controls with Silverlight, WPF and UWP it makes sense to make this a part of Blazor. One question would be data/code contexts. When the cshtml template references code in the bound C# object that is probably not going to be in the same context as the larger component. So would not some kind of dynamic data binding be required? Or would a template have a strict TargetType requirement so the parser knows how to resolve references to data bound object members? On that note, what about dynamic data binding and dependency properties? As far as I can tell there would be nothing preventing this right now. Or is reflection going to be problematic if the C# or IL winds up getting compiled to wasm rather than interpreted by mono? |
Implementing what @AlekseyMartynov is suggesting (or a variation of some sorts) is in my opinion critical to the success of Blazor. In practice, many developers writing enterprise applications depend on a rich set of easy-to-use 3rd party components and I'm hoping that this project will be very soon at the point when different vendors can start developing Blazor components. |
In my project Xamzor I have implemented some limited templating support like this:
where |
Maybe ember.js contextual components can provide some inspiration: https://www.mutuallyhuman.com/blog/2016/09/23/creating-an-accordion-with-contextual-components-in-ember |
@SvenEV I agree that in case of simple templates inline templates would be helpful but we still should have option for separate component templates like you implemented in ur project, because that helps us in separation of concerns and re usability of the template :) |
What if "Template" is a reserved/special keyword for any templatable Blazor component? When used as a tag, it can contain the template codes. When used as an attribute, it can specify a named template, which can contain the template codes. |
so, let's imagine a component like: ComponentTemplateTest.cshtml
we can use it as
we need an attribute like
it would be enough? something is missing? |
@uazo Just note, that without Property Element Syntax it is imposible to distinguish between |
@danieldegtyarev I think that we are talking here about two different things... my goal is only template for components for now... take a look at https://github.com/uazo/Blazor/tree/dev-experimental-templated-component |
"Template" used as a tag is declarative and naturally inline:
"Template" used as a tag to assign a "named template". Internally, the named template will have access to its parent component, so that something like
"Template" used as an attribute to assign a "named template". Internally, the named template will have access to its parent component, so that something like
The "Template" used either as tag or as attribute should result to the same compilation, such that the component would render the provided template info, which simply overrides the component's default render. A "named template" is a re-usable template. Creating a "named template" is almost like creating a component. However, the big difference is that a named template can only be used through a component's support for "Template". A template can contain components, but checks need to be put in to prevent potential infinite loops when, for example, a component that uses a template that uses the same component is coded... design/compile-time error perhaps? Questions: Can a named template inherit from another template? Can a named template be restricted to use only with specific component type(s)? |
I think you guys should go for the HTML syntax as much as possible, don't introduce new stuff. No Xamarin xaml syntax or ugly asp.net webforms components(asp repeater and so on). |
I agree with @GoranHalvarsson . Templating in Blazor should be only the implementation of generic RenderFragment, with same use case (first example in @etmendz post) . For that, I've updated my branch, now with full support for intellisense in visual studio (params and allowed child). can someone give me some feedback? |
@GoranHalvarsson I agree. The MVP can be for Blazor components to have basic template support. In this sense, a "Template" can contain HTML, component, content and Razor codes. At its most basic, Blazor components+templates should still be recognizably HTML+Razor codes. Declarative syntax fits this bill, which can be A next phase item can be Blazor's APIs/extensibility libraries that enable component creators to promote their own brand -- ex.: HTML helper-like syntax (Progress Telerik/Kendo UI) or Xamarin/XAML-like syntax (ooui.wasm, Platform.Uno) can instead be 3rd party/vendor-level deliveries. Within Blazor, template programmability and compilation flexibility can be worked on closely with component creator partners to address specific issues/needs/requirements. There is a suggestion in this thread about re-useable templates. Re-useable templates or "named templates" can promote code re-use. Although, honestly, I don't see much value in it and can be low priority. More likely, if it's meant for re-use, then perhaps it should be a component? ;-) @uazo IMHO, based on your examples, passing params to I also have doubts about letting components support multiple template properties. In this sense, making it too freestyle. I think Blazor should have a simpler and more standard way of supporting component templates, perhaps via ITemplate interface and some compiler tweaks here and there. |
@SteveSandersonMS for https://github.com/aspnet/Blazor/issues/404#issuecomment-377229910 MyListWithHeader.cshtml
example.cshtml
with full support for intellisense! |
@uazo Very very good! There is only one thing: lets remove |
uhm .. but if are you thinking of a wired default name (like |
Looking back, a default name would not be fine for nested templates... |
Lets think. Template is code generation with one parameter. Template has only access to this item. So if we 'reserve' a variable |
BTW, looks like this template- |
let's have
will be renderer as
with
|
Yes, sure, you have to resolve it during codegen. |
I do not think it is possible.
I do not think it is necessary to introduce anything else. |
Sorry to butt in here because it sounds like a great conversation - but - Wouldn't this be solved with a Data Context concept like WPF/UWP? Then in your template you'd always be referencing |
@peter0302 I personally like this idea... |
The challenge does remain being strongly typed. Of course this is addressed in XAML by the binding being done at run time. The data template here would probably have to support typing through generics. But definitely doable. Then again I would support a fully dynamic runtime binding mechanism also. |
You could go for stating the data type somehow via declaration. Somewhat similar to |
@uazo My feedback is that it looks very good! Here are my thoughts...
|
It might be a bit risky to discuss implementation when I am new to the framework but… if you go with having a separate parameter attribute for assigning templates as discussed under point 1 above, then you could add a Boolean flag (default true) to it to say if a default value (if not explicitly given) should be applied. Then you can solve point 3 above with introducing an interface like IDefaultTemplateProvider that one can use to provide definitions for the template-property-tags separately. Then those can be discovered via reflection in the same way you discover IComponent(s) and use them in cases where the use-default flag is set, and the user doesn’t provide one. You could allow several template components implementing the interface but throw an unambiguous exception if a property tag is found multiple times. In that way you can provide standardized templates to a generic component specific to an app that are used multiple times and get the resolution at design-time, perhaps? |
I think describing my learning experience around this gives you a good background…, and I have some thoughts to discuss… I want to make model-driven configuration of components through meta-data and resource files for retrieving static translated text, validation and the like. I was thinking this mechanism of templating could be the thing to make it happen. To evaluate it I made an interface
So, in this way the InputField component has information on how to retrieve meta-data, etc and can feed that into a model that is rendered in a template you provide. It works very well. In XAML it is the only mechanism to render things when there is a surrounding behavior implemented in a component, like in a list-view then you need to provide an item-template. In Blazor it is not necessary since you may use code with a loop to have that surrounding behavior instead. I then realized that I could have done the same thing here. I could have retrieved a model from meta-data in code instead and feed that into a normal component. Maybe obvious to some… so templates is actually not absolutely necessary for my use-case. But it got me question what it is I really want. Maybe it is more along the lines of injecting attributes and content into existing elements? And maybe doing so by being able to define a custom attribute like:
To test if it is even possible to add attribute nodes into an existing element I made a function (returns empty string for that content node) after the open tag I guess this render continuation/extension function can be hooked in on several places. To get you thinking… maybe having ability to inject a “RenderElementExtensionHandler” and if a component has such property then a call to it is compiled into the position when all attributes have been added and after close in the generated code on those elements that have custom attributes, like For in this case. Sorry if it got a bit lengthy… |
Why not render props? <List RenderHeader={(text) => (<h4>{text}</h4>)}>
{({ item, index }) => (
<li value={index} class={item.Type}>
{item.Content}
</li>)}
</List> |
Done: |
For example, you should be able to create a Grid or ListView component that binds to each item in a collection using a define template.
The text was updated successfully, but these errors were encountered: