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

Make ResourcesTagHelper extensible #16033

Open
sarahelsaig opened this issue May 11, 2024 · 4 comments
Open

Make ResourcesTagHelper extensible #16033

sarahelsaig opened this issue May 11, 2024 · 4 comments
Milestone

Comments

@sarahelsaig
Copy link
Contributor

Is your feature request related to a problem? Please describe.

Right now you can define any custom resource type in the resource manager and this is pretty cool. For example in Lombiq.VueJs we use it to define Vue SFC resources and JS module resources. But there is a huge pain point: the <resources /> tag helper doesn't support any other resource type besides the built-in ones, so if you add a custom resource type you can require it but it won't be rendered.

This leads to a lot of added complexity and additional failure points, because you have to display the resources in weird ways, such as using widgets, custom themes or by injecting the render into the Content zone (since other zones are not guaranteed to exist depending on the theme).

Describe the solution you'd like

To avoid the above mentioned complexity, I propose adding the following interface:

public interface IResourcesTagHelperProcessor
{
    Task ProcessAsync(ResourcesTagHelperProcessorContext context);
}

public record ResourcesTagHelperProcessorContext(
    TagHelperContext TagHelperContext,
    TagHelperOutput Output,
    ResourceType Type);

This would be invoked for each implementation at the end of ResourcesTagHelper.ProcessAsync. This way anyone can add a custom resource types and render logic without having to do workarounds and it will seamlessly work with all existing styles that use the <resources /> tag helper.

@hishamco
Copy link
Member

Please show us an example of a custom resource in which the built-in APIs don't fit nicely

@sarahelsaig
Copy link
Contributor Author

Earlier I've implemented JS module support for which I introduced script-module resource type. This resource type is different from the built-in script because modules can use the import JS keyword to reference other module files by name so they don't have to be bundled or all included in the calling HTML. This requires an import map that pairs the module names to their paths. So when requiring a script-module, their dependencies should go into a <script type="importmap"> instead (see import here and matching resource definition here). This is currently implemented using an MVC filter and it turns out that doesn't work in some scenarios and isn't very elegant anyway. Being able to amend this in the <resources /> tag helper would be much safer.

Note that I know it works because I've already implemented a simplified version of this in a client project using a dirty workaround. If you are interested, the trick is that I create a new class with the same [HtmlTargetElement("resources", Attributes = nameof(Type))] attribute that also contains the expanded logic, then include @addTagHelper *, MyClientProject.Theme in the theme's _ViewImports.cshtml file as the very first @addTagHelper entry. This overrides the built-in tag helper, but only works in custom themes and it's an unnecessary hassle. My proposed solution would work for the built-in themes as well and would not require special actions from the consuming code when developing a module.

@sebastienros
Copy link
Member

Do you think we'll have to remove the ResourceType enum to use a string instead? The only drawback would be the loss of intellisense.

@sebastienros sebastienros added this to the 2.x milestone May 16, 2024
@sarahelsaig
Copy link
Contributor Author

No, the ResourceType enum can stay. It's not actually related to the resource type I meant. I wish it was called something like ResourceTagType or ResourceRenderLocation instead, because the role it serves it's more similar to ResourceLocation than to ResourceDefinition.Type so the name is a bit confusing.

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

3 participants