ViewComponent and @Section #2910
Comments
@grahamehorner can you explain a bit more what scenario you have? Does the ViewComponent have its own layout page and view, and an |
I have the need to use a view component that has a view component layout and uses a view model; the layout includes a @section for scripts; at present the view component layout is rendering but fails to include the @section script at the end of the rendered page/output; this results in the view component not having the required functionality when rendered to the client. |
I have forked the code and updated the default.cshtml in the Views\Shared\Components\Tags folder to show what I believe to be an issue; as the @section scripts fails to render in the pages that make use of the ViewComponent. |
please see https://github.com/grahamehorner/Mvc |
@grahamehorner nothing shows up as changed in that forked repo. What branches should we compare? Might work better to post a standalone example. |
@dougbu apologies the sync failed and I had not realised; the simple update should now be invisble |
@grahamehorner thanks I've confirmed the behaviour you've described. The The most obvious issue here is views which have The remainder appears to be By Design...If you added a layout for your view component, it could render the With respect to defining a section in a view component that the main page's layout renders, note view components (and partials and templates...) should work in any page. Defining a section in any of these views with the expectation its name is known to the page or page layout creates an unnatural coupling. |
why would a View or a ViewComponent ever have a Layout of null? unless explicitly set? shouldn't they inherit from the _ViewStart.cshtml layout or the layout of the parent/hosting cshtml? I understand the 'unnatural coupling' comment and agree; but how could we achieve the following:- |
or should the ViewComponent be able to be supplied a options object for the View where it can then use this to inject style/script/html or hook into dom elements of the View ? |
I needed something similar and made: https://github.com/pebezo/Portal The idea is that you can send content from a view or partial view (not ViewComponent , but somewhat similar) to a particular spot in the layout. The implementation is rather hacky; it works, but you have to worry about the order in which you place the in/out points. To make it work properly, Razor would have to support at least two runs: one pre-render run where you could register what you want and the actual render. I brought up this idea over jabber, but it was shut down by @davidfowl because it would be too much like the page life cycle from WebForms. Since the view can now be sent over the wire before the entire page is rendered, the two-runs over the view may not be possible. |
I'm thinking about the possible creation of a ViewComponentScriptInject and/or ViewComponentStyleInjection middleware; this would allow ViewComponent to have custom attributes eg. ViewCompnentScriptAttribute/ViewCompnentStyleAttribute that are used to registered style and script that needs to be injected into the rendered output at a given location. |
@dougbu I know the current behaviour is by design, and workarounds do exist, but this is a particularly irksome design which, to my nose, smells of implementation details dictating the functional design. Any chances for this to be implemented? |
@leus we haven't seen what @grahamehorner put together. |
By the way - a feature needed for this is the ability to specify if a |
@leus if you need conditional execution of |
That sounds like a good workaround. I'm just wondering if this feature is added, what should be the default behaviour for view components. <div class="mycomp">My Component</div>
@section scripts {
<script src="ginormous-javascript-file.js"></script>
<script>
$(function() {
$(".mycomp").someVeryExpensiveJavascriptFunction();
});
</script>
} Would adding something like |
I think we should have a RazorTagHelper that removes the scripts from the initial render/output, buffering them The session script cache would prevent the same script from getting injected multiple times |
I have actually finished implementing the resource manager, which can be used in any ASP.NET Core/MVC app. Projects: Here is a view with all tag helpers you can use to define required resources from a view: You can also directly use the service from any code path to do the same: And inject the results of what was required during a request like this: Creating an attribute should be quite trivial, look at the Tag Helpers implementation for some example. |
@sebastienros - thank you for the above. I am using something similar based on this: dotnet/AspNetCore.Docs#687 (comment) Could you clarify how this works when registering resources within ViewComponents? For example if you have several instances of the same ViewComponent within a single View, and you register a resource within that ViewComponent - does that work - and do you end up with multiple resources being output in the layout in the order they were registered, or just a single one - or can you control that? Also, if VC is invoked during RenderBody() - can it still add resources retrospectively into the head? If you have any ViewComponent's using this system that would be good to look at! For example, a VC that needs to ensure that a meta tag is included in the current pages Head - you would probably only want to register that once, but how do you deal with that if there can be multiple instances of that VC on the page? The way I have been dealing with this at the moment is by doing something like this: In my View I call the VC once, with an argument to tell it to render in a special mode, which basically
Then for actually displaying each instance of the VC in the View:
But the consequences of this is that it requires the consumer of the VC to remember to place these additional 1 time invokes on the page, before then seperately invoking again to display each instance. I'd prefer a solution where this was all contained within the single VC invoke, and not left to the consumer to worry about. Thanks for sharing your work with this so far! |
Currently you can call this from a View Component's view only, as it doesn't target view components specifically. It also lets you target Head and Foot for scripts, CSS, custom inline scripts, custom link tags, and handles script versions. |
@sebastienros - Thanks - sounds very good. I will give it a whirl! - Can it also make my tea? :-) |
@dazinator Would you please post an example of how to use this invoke and the full namespace of RenderType? thanks |
@sebastienros I am new to MVC 6, could you show me how to use script section on the view component that is used multi times on the home view. |
@benzhi2011 - Perhaps you didn't understand so please, allow me to elaborate. Rather than including script tags in the View for your ViewComponent i.e:
Move your script tags into a seperate view file for your ViewComponent:
And have just your content in the other View file:
You now have two seperate View files for your VC, one containing your actual content, and one that just contains the script includes. Then in your page, when you Inside your ViewComponent's Invoke() method, inspect the argument you passed it, and return the right view. Now back in your page, in the scripts section of your page, you can invoke your ViewComponent with the argument that makes it render the script includes into that section of the view (i.e the scripts section where you want them). Elsewhere on your page, where you want the actual VC content to be displayed, you can Invoke it so that it displays the View that just displays the content. You can then invoke that multiple times on your page wherever you want your VC to be displayed. This isn't ideal, but it's a workaround I am using for the present. |
Thank you for your concrete explanation @dazinator |
I agree with others that without SOME kind of inherent support for adding JS and CSS files from within ViewControllers they're a halfbaked concept. I can't believe that this isn't on the roadmap. |
Closing because there are no plans at this time to implement this in MVC. |
@Eilon - by the reaction to your message I beleive it is clear that the community needs this. Could you ellaborate the reasoning for not adding this to the pipeline and simply closing the case? The current response to this issue is not aligned with the "customer obsession" culture that Satya is building at Microsoft. Thanks! |
@mxa0079 I totally agree that there's a community need, and it was mentioned earlier that a solution already exists via some components that @sebastienros built for Orchard Core: #2910 (comment) We've gotten plenty of flack before (and deservedly so!) for trying to replicate perfectly good community solutions. This is a case where we've stayed back because the community has what we believe to be a great solution for resource (CSS+JS) management. |
The orchard links above 404 for me.. did a quick search for orchard core resource management: yielded another link that 404's! https://github.com/OrchardCMS/Orchard2/tree/master/src/OrchardCore/Orchard.ResourceManagement If relying on a community maintained solution to fill such a fundamental gap in the framework (lets face it, this makes or breaks the real world usefulness of view components and was raised as a problem from day one) is your chosen path forward - my personal opinion of that doesnt matter.. but may I ask there is some easy to find documentation for it, similar to the rest of the mvc docs? Only because it seems that this problem is forced onto anyone who uses ViewComponents, and could leave them thumbling in the dark, especially if they are new to asp.net core mvc, and they can't find any clear direction on the docs site for recommended ways to solve this problem. |
Sorry, last week we renamed all the packages in preparation for a beta release on nuget.org, and I didn't remember these links were here. And yes, I intend to publish an article on how to use it, and also for some other important packages. The location remains to be defined. In the meantime, you can find the package on this myget feed: https://www.myget.org/feed/Packages/orchardcore-preview OrchardCore.ResourceManagement And some instructions: https://orchardcore.readthedocs.io/en/latest/OrchardCore.Modules/OrchardCore.Resources/README/#usage |
For those who just stumpled here (aka TL;DR) you just have to define a layout on the viewcomponent. Example: _LayoutViewComponent.cshtml:
YourViewComponentView.cshtml
|
That will work but since ViewComponent is being loaded inside a view and that view is probably using _Layout.cshtml which is already adding jquery.js, bootstrap.js, and site.js; this will cause you to duplicate the script files. |
I'm just catching up on this long thread. I think I'm with the majority on this one as well. The component architecture should basically be a self-contained app for separation of concerns. HTML, MVC markup, JS and CSS all contained within the component definition. I'm not so much worried about duplication of scripts or styles. That can be handled by a good layout of your application and/or using a script dependency framework like require.js if you are unsure about what scripts are being loaded. My main concern is just ability to inject a section block upstream to a layout which should be native to the ViewComponent and shouldn't be community driven otherwise I would recommend removing ViewComponents entirely from the MS code-base as a half working implementation isn't helpful. If MS isn't looking to provide native support for this then I would rather see a full implementation from the community side vs. using the community to augment deficiencies in the core functionality. If you have the structure of (outer-most HTML to inner-most) Layout -> View -> ViewComponent, I think it is typical for the Layout to render a section at the end of the head to inject styles from the view and render a section at the end of the body to inject scripts from the view. At the very least I would think the ViewComponent should be able to inject its section definitions up 2 levels to that Layout. Obviously there is some mechanism there already to inject the HTML markup from a ViewComponent into the parent View so could it just inject the other sections at that time as well? |
@grahamehorner Did you find a solution for this? |
@gilm0079 I opted to use a TagHelper extending the partial to have an optional flag |
Imagine that in a VC, besides using the currently available And to replicate gilm0079's comment - the pipeline obviously exists that connects the top-most layout to the bottom-most VC. So instead of passing just the immediate parent layout, if the top-most layout was also passed down to all VCs (possibly nested), that's what the Probably an overkill to have a mechanism that would let you refer to ANY layout "above you" in the hierarchy. "Just a simple |
Why is this issue been closed? I do not see any "official solution", only incredible workarounds that only make projects more and more intricate |
@alkex992, it boggles the mind to think that the workarounds on this thread are "acceptable". You are absolutely correct and I can't find another issue or thread regarding an open request for this basic and necessary feature. |
@clockwiseq it really limits the usefulness of ViewComponent. Better not use it. |
Exactly @dodyg , it has left me with very limited options as to how to create reusable components. |
This issue is three years old. I don't think we'll see some official movement on this topic. The alternative is to fork the Razor template engine and figure this out ourselves. It will make an interesting side project just to see what's possible. |
Is anyone interested to hook up for some pair programming to give this a shot ? |
The
@section
scripts don't render in a ViewComponent, view component should have the ability to include scripts within the@section
unlike partital views as components may be reused across many views and the component is responsible for it's own functionality.The text was updated successfully, but these errors were encountered: