Returning razor-parsed Javascript as a ViewResult from a controller - asp.net

I've successfully created an mvc/razor web application that returns css files that have been parsed by razor. Each time there's a background-image I have a razor snippet that writes the URL prefix to the image file name. The CSS now looks like this:
body { background-image: url(#LookupUrl.Image("background.gif")); }
Css files now work fine and I've moved onto trying to get javascript .js files to function the same way but these aren't playing ball.
The code is identical to the css code and it successfully finds the .js file, but razor seems to parse it differently. Here's an example js file:
function testFunction() { alert('test function hit!'); }
testFunction();
Razor seems to think it's code that it should compile, and gives the error:
Compiler Error Message: JS1135: Variable 'alert' has not been declared
> Source Error:
>
> Line 1: function testFunction() {
> Line 2: alert('test function
> hit!'); Line 3: } Line 4:
> testFunction();
After renaming the same file to .css it works fine.
Is there a way of getting razor to function with .js files in the same way as it does for .css?
Here's how I registered the file handlers for razor:
RazorCodeLanguage.Languages.Add("js", new CSharpRazorCodeLanguage());
RazorCodeLanguage.Languages.Add("css", new CSharpRazorCodeLanguage());
WebPageHttpHandler.RegisterExtension(".js");
WebPageHttpHandler.RegisterExtension(".css");
The build provider is registered in PreApplicationStart via the method Haacked outlines in his blog post.
Do I need to remove a handler that mvc adds for .js files?
UPDATE 2 days on
While I got working what I wanted to get working, I would not recommend this method to others. Using Razor to parse css/javascript is flawed without the use of <text><text/> - it's the simplicity of razor using the # ampersand that messes it up. Consider the CSS3 #font-face. Razor hits the # and thinks it should use it as a function. The same thing can happen with javascript, and happened with Jquery 1.5.1.
Instead, I'll probably go back to aspx webforms for dynamic css/javascript, where there's less chance of the <% %> code blocks appearing naturally.

I couldn't understand why CSS worked while JS didn't, especially after the copy+pasted JS code worked inside the CSS file.
I used the find/replace dialogue within visual studio on the System.Web.WebPages.Razor source to search for the string '.js' within the project. There was nothing helpful there so I then went to the System.Web.WebPages project. It found a match in System.Web.WebPages.Util, which is a static class with a few helper methods.
One of those methods is 'EnsureValidPageType' and within there is a try/catch block. Inside the 'catch' block is a comment:
// If the path uses an extension registered with codedom, such as Foo.js,
// then an unfriendly compilation error might get thrown by the underlying compiler.
// Check if this is the case and throw a simpler error.
It made me believe .js has got some special built-in handler with it.
I googled for a bit, couldn't find anything, then looked in the web.config that's within \Windows\Microsoft.NET\Framework64{version}\Config.
In there is a buildProvider mapping for the extension .js to
System.Web.Compilation.ForceCopyBuildProvider
After removing this buildprovider in the website's web.config, .js files get compiled and work as they should!
I'm still not too sure what the ForceCopyBuildProvider does or is for but I wonder if it's for visual studio. Various extensions have different Copy/Ignore build providers registered.
Once again apologies for answering my own question but I hope the comprehensive(waffley) answer might help others out.

You could try using the special <text> node to indicate to the Razor parser to treat the content literally:
<text>
function testFunction() { alert('test function hit!'); }
testFunction();
</text>

The default Razor parser uses the HtmlMarkupParser to handle the markup components of your template. There isn't currently any alternative parsers that support other markup languages (which you would need to treat the javascript code language as). If you did create a new markup parser, I would imagine it would be quite difficult to separate the code and markup (i.e. the C# and the Javascript).
What you could do, is use the <text></text> wrapping elements to enforce the parser switches to markup mode when that section of the template is reached, e.g.
<text>function testFunction() { alert('test function hit!'); }</text>
It's not pretty, but it should do the trick.

Related

Next JS Version 12+: Static Site Generation

I'm using Next.js version 12.0.4 on a new project, having used Next.js version 10 on a prior one.
Has something changed with Static Site Rendering at build time? It's rendering all my output pages when I do an npm run build (which in turn executes "next build and next export") with html files that include a ton of .js files and no native text "content" which I'd expect.
That is, the output doesn't have any of the standard HTML <h1>, <h2> etc. in it, just a bunch of javascript for the client to hydrate.
Prior versions of Next.js (or perhaps it was my configuration?) seemed to render pure, finalized HTML just fine.
I'm trying to render a simple About page (no dynamic routes, no dynamic props for this route) and, while it properly renders a page in the "/about/index.html" output location, that page has a bunch of .js files and a JSON payload. That page does indeed display properly, but I'd really like the output in the "out" directory to be actual .html files with HTML pre-rendered, for SEO and other reasons.
In my next.config.js, I've specified:
module.exports = {
exportPathMap: function () {
return {
"/": { page: "/" },
"/about": { page: "/about" },
};
},
};
I've also specified getStaticProps on the about page conponent (about.tsx). (I'm using typescript if that matters.)
The rendered /about/index.html file has a ton of .js includes and no real HTML "content".
Have I missed a configuration setting? What can I do to make it render pure HTML where I'd like?
AHA! Ok, so this error was of course a coding error on my side.
In _app.tsx, I had a wrapper for Authentication that I had written. By (bad) design, it was deliberately NOT rendering children for it if the user wasn't authenticated. Therefore, the pre-renderer wouldn't render the "regular" html elements, because the pre-renderer of course does not sign in.
If this same problem happens to you, make sure you're not wrapping all views up in some provider element which conditionally renders children.

Meteor: [Load Order] Make JavaScript file load after HTML file is loaded?

Load Order Issues
I am having trouble making Meteor load my JavaScript after my HTML file fully loads when I go to localhost:3000. The problem is that my JavaScript keeps loading before my HTML file, and makes the page look unloaded when I use stuff like alert(); or prompt();. I've tried a lot of solutions such as naming my JavaScript file as main.js and putting my HTML file in a deeper directory and using <script> tags. I have also read the documentation concerning this: http://docs.meteor.com/#/full/structuringyourapp Solutions I've tired based off the documentation such as putting files in client/lib , client/compatibility , and lib have proven to no avail. I also tired Meteor.startupand I placed the file for it in the client folder. (The code inside it):
Meteor.startup( function () {
$.get("client/lib/testproject.html")
$.getScript("client/testproject.js");
});
The above sort of solved my problem, but it loaded the JavaScript file two times. The first time was before the HTML loaded and the second time was after the HTML loaded. I don't know a way to prevent the first JS load from happening when using Meteor.startup, so any solutions for that are also appreciated.
The JavaScript file's code I am referring to is simple. (In it's entirety):
prompt("Hello World!");
myList = ["apples", "oranges", "bananas"];
myList.forEach(function(value, index) {
alert('I have ' + value + ' for dinner.');
});
Summary
To summarize my problem:
My Problem:
Go to localhost
JavaScript loads first
HTML loads second
What I Need:
Go to localhost
HTML loads first
JavaScript loads second
The Question: How can I make my JavaScript load only after when my HTML is loaded? And how can I restructure my folder, file-names, and/or code to make it behave as I want it to in this case?
Since the code posted is extremely simple to reproduce I kindly ask that you
run your own solution with a setup similar to what I have and not something that uses a million packages since that is unnecessary for my case, on Meteor, before responding to this.
I'm on Meteor 1.1.0.2
Here is a link to my folder structure with included HTML code along with filenames I used: http://i.imgur.com/24z6bXF.png
I think you missed a decisive information : you should wrap your Javascript code into a Template.yourTemplate.rendered=function () {} function.
That is the meteor way to ensure that your related html code is properly rendered first.
First of all, Meteor will always repackage your files and load them automatically in a specific order (Meteor structuring your app). First files in client/compatibility then client/lib and then the others JS files.
You should also rewrite your code so it does not get executed immediately at load time, like wrapping everything in a function. And then, you should call this code when the DOM is loaded, which does not necessarily mean in Meteor.startup but also in onRendered callbacks in your templates.

ASP powered CSS file and bundling

For a while now I have adopted the practice of writing my CSS in an .aspx page and serving that page as CSS. Primarily this is because a project I was involved on had a development 'CDN' before images were finally hosted on the actual CDN, so when it came to background images I needed a way of changing the URL through the web.config. I also get a few other perks like variables. A small sample:
#id
{
color: '<%= PrimaryColor %>';
background-image: url('<%= PrependCdnUrl("myimage.jpg") %>');
}
Since working on an MVC project I'd now like to incorporate CSS bundling into the original project (a webforms project). The problem I have, an .aspx page is not allowed to be bundled up, I get errors similar to:
(1,1): run-time error CSS1019: Unexpected token, found '<'
(1,2): run-time error CSS1019: Unexpected token, found '%'
...
Does anyone know how I could get this to work?
Short answer: It is not possible.
Long answer: It is possible through some hacks.
Actually there are 2 possibilities that pop into my mind that would accomplish this (they are not tested, not sure if they work out finally tough):
In your IIS you can define which filetype gets handled by which interpreter. You can try to define .css files to use the same interpreter as ASP.NET pages. (not sure if this works)
You can create an .aspx file instead of a .css file where you output the css code you need dynamically. Set the MIME type of the return stream from this dynamic css aspx page to the MIME type of CSS. Then reference your aspx page as css file

Meteor+Blade template variables catch 22

I'm trying to use a variable in my Blade template, but I always get
ReferenceError: files is not defined
My understanding is that the proper way to pass a variable to a template is something like this (client/ceres.js):
Meteor.startup(function() {
Files = new Meteor.Collection('files');
Template['files'].files = function() {
return Files.find();
}
});
(Copying from the "todos" example)
And then I should be able to use it in my template, views/files.blade:
ul
foreach files as file
li= file.filename
But I guess the variable is passed to the template too late? But if I take my JS out of Meteor.js then Template isn't defined.
So I don't get it. Either my template doesn't exist, or the variable doesn't exist, and it always crashes. How do I pass a simple variable along?
Same error with this:
ul
- for(var i=0; i<files.length; ++i)
li= files[i].filename
This is a known issue with Meteor that is actively being worked on.
The problem is that Meteor prevents smart packages from specifying the load order of files. See issue here.
Because of this issue, it is possible that your client-side JavaScript will run before the templates are loaded. (There is a hack in Meteor that ensures Handlebars templates load before your custom code) For example, Template.foo.helperName = function() { ... } will fail if Template.foo has not yet been defined.
Check the generated HTML (view source) for the initial page load to see if your client-side JavaScript code is loading before the template is defined. If so, you may get an Error like:
TypeError: Cannot set property 'helperName' of undefined`
To workaround this issue, try putting your client-side code in a folder with a different name. I believe that Meteor currently sorts files alphabetically when determining the load order. See the troubleshooting section on this page for more information.
A similar workaround is to utilize Meteor.startup when adding view helpers to your views. That is, you can wrap your Template.foo.helperName = ... stuff in a Meteor.startup call. If you are using a body.blade template, though, you can end up with the opposite problem (i.e. the "catch 22") in which your body.blade template starts rendering before view helpers get setup. In this case, you can get errors since those helpers/variables are not yet defined. The solution here is to avoid using body.blade templates and only render the initial template once all view helpers have been loaded (i.e. at the end of your Meteor.startup routine).
At any rate, all of these workarounds are rather lame. :( But, alas! These issues should be fixed soon.
As soon as Meteor fixes the issue described above, I will modify the Blade smart package to enforce the load order of compiled templates. My apologies for the confusion.
Turns out you can't include files that use Template variables either. i.e., you can't use the include directive in Blade at all if you want to use variables in your template that haven't been initialized by Meteor yet -- you have to insert your template via jQuery/JS after the DOM has loaded. Example:
views/body.blade:
.container
h1 Page Title
#content
views/files.blade:
ul
foreach files as file
- console.log(file);
li= file.filename
client/main.js:
Files = new Meteor.Collection('files');
Template.files.files = function() {
return Files.find();
};
$(function() {
$('#content').html(Meteor.render(Template.files));
});

Adding JQuery to meteor and writing it without errors

I added the JQuery list package to meteor and it recognizes it. But when I write JQuery code inline in <script></script> tags in the apps main html file it does not recognize it ( but I don't get an error). When I write JQuery code in my meteor app .js file I get an error. So I am confused as to how one is suppose to write with javascript or added library packages (like JQuery) once they are added. Thank you.
You need to put general javascript in a container to include it in a specific Meteor template.
For general onLoad scripts that you might be used to, you can encapsulate that code inside a function once the template is rendered
Example:
Template.*templatename*.rendered = function()
{
//do this only on template load
if(!this._rendered) {this._rendered = true;console.log('Template onLoad');}
//everything outside if is done every time the template is re-drawn (meteor sends an update)
}

Resources