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));
});
Related
I am in the process of adding a copyright line to the footer of a website that I have been working on and I cannot find the best way to get the current dynamically into the footer so I never have to set it again. I have tried multiple things, including a global setting that can be accessed from anywhere, but nothing has worked.
Any feedback would be appreciated. Thanks.
Write an ApostropheCMS nunjucks helper function. See those docs for the general issues around this. Your specific function could look like:
self.addHelpers({
thisYear: function() {
return new Date().getFullYear();
}
});
If you put that in construct of a module of your own, let's say it's called helpers, then you can call it in Nunjucks as {{ apos.helpers.thisYear() }}.
These are very handy, just remember they cannot do any async work.
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.
I have Meteor application that uses Iron-Router in which the rendered screen is dynamic based on the URL.
I have two parameters from the URL, station and screen.
In the routing code:
this.render(stationParam+screenParam, {to: 'content'});
However, I want to be able to check if the template stationParam+screenParam exists.
Is this possible?
All templates are stored as a field of a global object, Template. The question then is: How to check if this global object has a given field?
You have multiple ways to do it. My personal favourite using underscore:
if (_.has(Template, stationParam + screenParam)) { /* ... */ }
Underscore is included in Meteor. If you want to use it in a package, don't forget the api.use('underscore') in your describe callback.
See also: Determining if a javascript object has a given property
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)
}
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.