Use Links.Scripts when setting up new bundles...

Developer
May 6, 2013 at 5:57 PM
Would like to be able to do this with T4MVC.
        bundles.Add(new ScriptBundle(Links.Bundles.Scripts.KendoUi).Include(
                    Links.Scripts.kendo._2012_3_1114.kendo_web_min_js,
                    Links.Scripts.kendo._2012_3_1114.kendo_aspnetmvc_min_js));
This does not work because T4MVC resolves the url from "~/scripts" to "/Scripts" and the bundles require the squiggle.

Any ideas how we can easily solve this?
Coordinator
May 6, 2013 at 7:17 PM
T4MVC.tt.hooks.t4 has code that you can customize. By default, it calls VirtualPathUtility.ToAbsolute() to get rid of the ~, but you could remove that.
Developer
May 6, 2013 at 8:19 PM
Well, the problem is that you may still be referencing Links.xxxx from other places (not in bundles)....and that would then break that code.
Coordinator
May 7, 2013 at 1:45 AM
Edited Sep 30, 2013 at 5:30 PM
Well, in the end, we're talking about two different kinds of links:
  1. Links for client consumption. Can never have ~ syntax
  2. Links to use in server side APIs, which work best with ~ syntax
A given constant can't be both at once, so maybe we need two different collections of link constants. Or if the server use is not common, I guess you could just call VirtualPathUtility.ToAppRelative.
Developer
May 7, 2013 at 3:21 AM
On the server, the bundle requires the "~" for a virtual path.
Pretty much everywhere else it is used does not require that virtual path.

I suppose I could add the ~ to the T4MVC constant....just seems a bit contrived....
Sep 5, 2013 at 5:42 PM
Is this still not possible without overriding the default hooks?
If not, could you note on /documentation in the bundle section that you can't use the T4MVC Links constants in the .Include() statements
Sep 23, 2013 at 9:25 AM
Hi All,

How about generating a new Assets static class to hold constants for the asset paths. For example my css bundle would change from this:
bundles.Add(new StyleBundle(Links.Bundles.Css.Styles).Include(
                "~/css/reset.min.css",
                "~/css/main.css",
                "~/css/webkit.css",
                "~/css/jquery-ui.css"
            ));
to this:
bundles.Add(new StyleBundle(Links.Bundles.Css.Styles).Include(
                Links.Bundles.Css.Assets.reset_min_css,
                Links.Bundles.Css.Assets.main_css,
                Links.Bundles.Css.Assets.webkit_css,
                Links.Bundles.Css.Assets.jquery_ui_css
            ));
David - if you are happy with this, I'll send a PR.

Kevin.
Coordinator
Sep 23, 2013 at 10:44 PM
To make sure I understand correctly, you're suggesting that for every Links.Sub1.Sub2.foo_txt link constant, there is a second Links.Sub1.Sub2.Assets.foo_txt constant that has the ~ link instead of the full link?

That might work. Though not sure I like the term 'Assets' here, as it doesn't make it clear that it's a server link vs a client link.
Sep 23, 2013 at 10:56 PM

Would an extension method work better?

Coordinator
Sep 23, 2013 at 11:02 PM
Can you be more specific on how you would see that working with an extension method?

Or maybe it's worth asking: is it too difficult for people to just write VirtualPathUtility.ToAppRelative("Links.Sub1.Sub2.foo_txt") to get the server link behavior? At least I think that would work.
Sep 24, 2013 at 9:33 AM
Edited Oct 14, 2013 at 4:46 PM
David - essentially yes, except I've only included .js and .css files, and I've put the bundle constants / server links in Links.Bundles.

Using your example, but with a css file instead, we would have this:
Links.Sub1.Sub2.foo_css                 // Already in T4MVC for use in Url.Content() etc.
Links.Bundles.Sub1.Sub2.Assets.foo_css  // New for use when defining bundles.
I used Assets as the links refer to to the script and css assets which are registered in the bundle. I also wanted them to be nested a level down as we already allow for the bundle name to defined as a constant and I wanted to differentiate between bundle names and the assets which are available to register in them. I'm happy to use a different name if we can think of something more appropriate.

I have some working code in my fork if you want to take a look. I still need to tidy up the formatting of the generated code. I also added a sample BundleConfig.cs to show how it can be used.

@qntmfred - I think I would prefer something like Links.Bundles.Sub1.Sub2.Assets.foo_css as it more in keeping with the rest of the T4MVC syntax.
Coordinator
Sep 30, 2013 at 7:39 PM
Sorry for the slow response. Looking at this, a though occurs to me: could we just define a Bundle extension method that works with the existing T4MVC constants?

e.g. something like this:
    public static Bundle IncludeT4MVC(this Bundle bundle, params string[] virtualPaths)
    {
        bundle.Include(virtualPaths.Select(path => VirtualPathUtility.ToAppRelative(path)).ToArray());
        return bundle;
    }
Which would then let us write:
        bundles.Add(new StyleBundle("~/Content/css").IncludeT4MVC(Links.Content.Site_css));
Developer
Sep 30, 2013 at 7:41 PM
Now that makes good sense to me!
Oct 1, 2013 at 8:35 AM
That works for me too. I'll send a PR when I have some time. It may be a week or two.
Oct 3, 2013 at 9:14 AM
So I made a start on this today, but in order to add the extension method to T4MVCExtensions, we need to take a dependency on the Microsoft.AspNet.Web.Optimization NuGet package.

I'm not sure this is something we should do. Maybe we need a new T4MVCExtensions.Web.Optimization project and NuGet package just for this, or is this feature a non-starter?

Any thoughts?
Developer
Oct 3, 2013 at 12:50 PM
Perhaps this feature is not worth this?
I use the bundles, but others may not - think it is a neat little feature, but the dependency may be too much?

I think the best solution would be to include the code in T4MVC.tt.hooks.t4, having it commented out by default. Document it and put comments in the code for what it is for and how to use it. That gives the feature and requires no dependency, etc.

Here is the code that would be put into the T4MVCHelpers class in that file:
    public static System.Web.Optimization.Bundle IncludeT4MVC(this System.Web.Optimization.Bundle bundle, params string[] virtualPaths)
    {
        bundle.Include(virtualPaths.Select(path => VirtualPathUtility.ToAppRelative(path)).ToArray());
        return bundle;
    }
Developer
Oct 3, 2013 at 1:14 PM
Looks like none of the above solutions will actually work. Additionally there are some things I do not like about it.

The Links.xxx constants do the following:

1) It does its' own 'production' check - the bundler will do this.
2) It does its' own file exists check - the bundler will do this.
3) It adds the 'timestamp' thing to the url - this is just not necessary at all with items that are bundled, but you still may want it for other assets. Plus the bundler is looking for a file it can check on - and if you add this parameter, likely it will not find the file.

So, at the end of the day, we need a set of real constants that just return the filename (relative?) and have no other processing done on them.

Here is an example of the code that is currently generated:
    public static class Content {
        private const string URLPATH = "~/Content";
        public static string Url() { return T4MVCHelpers.ProcessVirtualPath(URLPATH); }
        public static string Url(string fileName) { return T4MVCHelpers.ProcessVirtualPath(URLPATH + "/" + fileName); }
        public static readonly string colorbox_css = T4MVCHelpers.IsProduction() && T4Extensions.FileExists(URLPATH + "/colorbox.min.css") ? Url("colorbox.min.css")+"?"+T4MVCHelpers.TimestampString(URLPATH + "/colorbox.min.css") : Url("colorbox.css")+"?"+T4MVCHelpers.TimestampString(URLPATH + "/colorbox.css");
Maybe we just need something like this:
    public static class Content {
        private const string URLPATH = "~/Content";
        public static string Url() { return T4MVCHelpers.ProcessVirtualPath(URLPATH); }
        public static string Url(string fileName) { return T4MVCHelpers.ProcessVirtualPath(URLPATH + "/" + fileName); }
        public static readonly string colorbox_css = T4MVCHelpers.IsProduction() && T4Extensions.FileExists(URLPATH + "/colorbox.min.css") ? Url("colorbox.min.css")+"?"+T4MVCHelpers.TimestampString(URLPATH + "/colorbox.min.css") : Url("colorbox.css")+"?"+T4MVCHelpers.TimestampString(URLPATH + "/colorbox.css");
        public static class RawRelative
        {
            public static readonly string colorbox_css = URLPATH + "/colorbox.css";
        }
Thoughts?
Oct 3, 2013 at 5:04 PM
Edited Oct 14, 2013 at 4:46 PM
It looks like we are back to generating constants which can be used with the optimization framework.

I think there is value in having them, as I am not a fan of magic strings. So the question is where to put them:
Links.Bundles.Content.Assets.foo_css
or
Links.Content.RawRelative.Content.foo_css
or somewhere else?

I prefer the first option as it keeps constants in the namespace we already have for the bundle names. See my sample BundleConfig.cs for an example.
Oct 21, 2013 at 4:54 PM
David / Wayne, anymore thoughts on this?
Oct 21, 2013 at 7:19 PM
Thanks David, this solved my use case perfectly. I used to have a manual links generator for CSS and JS files and have now replaced it with T4MVC. Great job on this package!
Coordinator
Oct 23, 2013 at 5:56 PM
Sorry, I haven't had much time to look at this. But I'm open to generating extra path that are meant for server use (~/ paths), as they are clearly different beasts from paths that are for client use.
Dec 12, 2013 at 3:10 PM
I finally got round to finishing this. PR here
Developer
Dec 12, 2013 at 3:13 PM
Thanks Kevin. Once David approves I will try it out!
Coordinator
Dec 12, 2013 at 5:31 PM
Wayne, any chance you can give this a try before we get it in, to get a chance to catch some issues early? I haven't looked at it yet.
Developer
Feb 1 at 5:36 AM
I am a delinquent...I have not had any time to look at this either....