I know this question has been asked before but I'm wondering if something has changed with the advent of 1.0.
I don't want Meteor to automatically bundle together every single CSS file in my app. My admin pages are going to have a completely different CSS than my client-facing pages and using namespaces seems like a really over-complicated solution. How do I have Meteor load certain CSS files on certain pages and NOT load certain CSS files on certain pages?
The same question goes for JS files.
I know someone said this would be useful:
https://github.com/anticoders/meteor-modules
Any comments on this package for conditional CSS and JS?
You can just put your CSS files somewhere under /public and manually include them from your templates where required. Everything under /public will NOT get bundled, and the URL will have the /public removed e.g.
Create two files: your_meteor_project/public/one.css and ......./two.css. These will be available from your client at http://example.com/one.css (i.e. the "public" does not form part of the URL, it's like the document root for using meteor as a plain old web server).
meteor create cssSwitcher
cd cssSwitcher/
mkdir public
echo 'html, body { background-color: red; }' > public/one.css
echo 'html, body { background-color: blue; }' > public/two.css
Create a reference to a helper function "appropriateStylesheet" in the head of your HTML :
HTML template
<!-- add code to the <body> of the page -->
<body>
<h1>Hello!</h1>
{{> welcomePage}}
</body>
<!-- define a template called welcomePage -->
<template name="welcomePage">
<!-- add code to the <head> of the page -->
<head>
<title>My website!</title>
<link rel="stylesheet" href="/{{appropriateStylesheet}}" type="text/css" />
</head>
<p>Welcome to my website!</p>
<button id="red">Red</button>
<button id="blue">Blue</button>
</template>
Create a helper function.
JavaScript:
if (Meteor.isClient) {
Session.set("stylesheet","red.css");
Template.registerHelper("appropriateStylesheet", function() {
return Session.get("stylesheet");
});
Template.welcomePage.events({
'click #blue' : function() { Session.set("stylesheet","two.css"); },
'click #red' : function() { Session.set("stylesheet","one.css"); },
});
}
You can do exactly the same thing with JS files. Put them under /public and meteor ignores them.
Related
I'm trying out Google app scripts for the first time and I'm having a nightmare trying to get it to read my Stylesheet.
I've read dozens of pages and they all say the same thing, but it just doesn't work.
This is my code.gs :-
return HtmlService.createHtmlOutputFromFile('index');
}
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename)
.getContent();
}
This is my index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= include('Stylesheet'); ?>
</head>
<body>
Hello!!!!<br>
This is a test to see how Google scripts work<br>
</body>
</html>
This is my Stylesheet
<style>
html{
min-height:100%;
background-color:blue;
}
</style>
If I put the style tags inside the index file, it works fine, but when I try to include it, the include script just gets added to my page. I've tried using createTemplateFromFile as well, but it has the same result. It seems like index.html is ignoring the script identifier <?!
Please could someone tell me what I'm doing wrong as every page I've looked at says to do it this way!!!!!
Thanks
I assume it could be because your are not calling .evaluate()
return HtmlService.createTemplateFromFile('index').evaluate();
https://developers.google.com/apps-script/guides/html/best-practices#code.gs
Seems like it's been missed to add evaluate() function.
As per the HTML Service: Templated HTML , one should be using particular scriptlet as per the requirement.
I'd recommend a generalized solution like below:
function include(fileName, params) {
return render(fileName, params).getContent();
}
function render(path, params) {
let htmlBody = HtmlService.createTemplateFromFile(path);
// for prop drilling
htmlBody.allParams = {};
if (params) {
htmlBody.allParams = params;
Object.keys(params).forEach(key => {
htmlBody[key] = params[key];
});
}
return htmlBody.evaluate();
}
I am building a small module that replaces the default FileField in Silverstripe and changes it into a fancier upload (in the front end). I replace the class through the injector method in Yaml. This seems to be working all right.
However, I need to add some js and css that only has to be included when the specific field is used. I tried the forTemplate method and the constructor of the filefield class, but both do not result in the files being added to the html.
Is there a way to do this?
I am using SS 3.4
You can add requirements in the CMS by adding the following to your config.yml file, however this will add globally...
LeftAndMain:
extra_requirements_css:
- mymodule/css/mymodule.css
extra_requirements_javascript:
- mymodule/javascript/mymodule.js
Instead including the required source files just within the FormField constructor would include just when this field is shown...
Requirements::javascript("mymodule/javascript/mymodule.js");
Requirements::css('mymodule/css/mymodule.css');
You can also add code directly possibly including values from the PHP like this...
Requirements::customCSS('
#Form_FilterForm .field {
display:inline-block;
width:31%
}
');
Requirements::customScript('
jQuery(document).ready(function(){
//JS Code here
});
');
Or as above but it must be included in the header...
Requirements::insertHeadTags("
<style>
#Form_FilterForm .field {
display:inline-block;
width:31%
}
</style>
<script>
jQuery(document).ready(function(){
//JS Code here
});
</script>
");
I use DataTables in some specific tables and it needs at least 10 different js/css files to work in my situation. So i tried to put them in a bundle and called that bundle in that view. But no success.
My question is that: Are bundles only for entire site (loads in every page) or can i use some of them in specific views only?
BundleConfig
bundles.Add(new ScriptBundle("~/Content/DataTablesJS").Include("~/Content/DataTables/*.jss"));
bundles.Add(new StyleBundle("~/Content/DataTablesCSS").Include("~/Content/DataTables/*.css"));
View
#section Styles
{
#Scripts.Render("~/Content/DataTablesCSS")
}
#section Scripts
{
#Scripts.Render("~/Content/DataTablesJS")
<script src="~/Content/SayfaJSs/DataTables.jss" type="text/javascript"></script>
<script>
jQuery(document).ready(function() {
DataTables.init();
});
</script>
}
HTML Output
<script src="/Content/DataTablesCSS?v=z-Ctaq2TbplDFpORl0e9NGH8TjpB5hQ2cPam2OxmDEo1"></script>
<script src="/Content/DataTablesJS?v=2gXGKlcqr0bFFqv6Bbr9jB_7LVvvHDrghzBwHCgFJds1"></script>
In your layout you would have something like this:
#RenderSection("Scripts", required: false)
...Then in your view you would add your bundle reference like this:
#section Scripts {
#Scripts.Render("~/bundles/yourBundle")
}
I found the problem. It was using asterisk to get all scripts in folder. From asp.net website:
"Adding scripts by wildcard defaults to loading them in alphabetical order, which is typically not what you want. "
My scripts needs a special order. So i included them in order that i need:
bundles.Add(new StyleBundle("~/Content/DataTablesCSS")
.Include("~/Content/DataTables/dataTables.bootstrap.css",
"~/Content/DataTables/buttons.dataTables.min.css",
"~/Content/DataTables/select.dataTables.min.css"));
bundles.Add(new ScriptBundle("~/Content/DataTablesJS")
.Include("~/Content/DataTables/jquery.dataTables.min.js",
"~/Content/DataTables/dataTables.bootstrap.js",
"~/Content/DataTables/dataTables.buttons.min.js",
"~/Content/DataTables/dataTables.select.min.js",
"~/Content/DataTables/buttons.bootstrap.min.js",
"~/Content/DataTables/jszip.min.js",
"~/Content/DataTables/pdfmake.min.js",
"~/Content/DataTables/vfs_fonts.js",
"~/Content/DataTables/buttons.html5.min.js",
"~/Content/DataTables/buttons.print.min.js"));
I was hoping anyone could give some input on this,
I'm creating a meteor app in which I would like to use bootstrap to creating the admin environment, but have the visitor facing side using custom css. When I add the bootstrap package to my app using meteor it's available on every page, is there a way to restrict the loading of bootstrap to routes that are in '/admin' ?
When you add bootstrap package it's not possible. You can, however, add bootstrap csses to public directory and then load them in a header subtemplate that will only be rendered when you're in the dashboard.
EDIT
But then how would you go about creating seperate head templates?
Easy:
<head>
...
{{> adminHeader}}
...
</head>
<template name="adminHeader">
{{#if adminPage}}
... // Put links to bootstrap here
{{/if}}
</template>
Template.adminHeader.adminPage = function() {
return Session.get('adminPage');
}
Meteor.router.add({
'/admin': function() {
Session.set('adminPage', true);
...
}
});
DISCLAIMER: I am unsure of a 'meteor way' to do this, so here is how I would do it with plain JS.
jQuery
$("link[href='bootstrap.css']").remove();
JS - Credit to javascriptkit
function removejscssfile(filename, filetype){
var targetelement=(filetype=="js")? "script" : (filetype=="css")? "link" : "none" //determine element type to create nodelist from
var targetattr=(filetype=="js")? "src" : (filetype=="css")? "href" : "none" //determine corresponding attribute to test for
var allsuspects=document.getElementsByTagName(targetelement)
for (var i=allsuspects.length; i>=0; i--){ //search backwards within nodelist for matching elements to remove
if (allsuspects[i] && allsuspects[i].getAttribute(targetattr)!=null && allsuspects[i].getAttribute(targetattr).indexOf(filename)!=-1)
allsuspects[i].parentNode.removeChild(allsuspects[i]) //remove element by calling parentNode.removeChild()
}
}
removejscssfile("bootstrap.css", "css")
However, doing that would complete remove it from the page. I am not sure whether meteor would then try to readd it when a user goes to another page. If that does not automatically get readded, then you have an issue of bootstrap not being included when someone goes from the admin section to the main site, which would break the look of the site.
The way I would get around that would be to disable and enable the stylesheets:
Meteor.autorun(function(){
if(Session.get('nobootstrap')){
$("link[href='bootstrap.css']").disabled = true;
}else{
$("link[href='bootstrap.css']").disabled = false;
}
});
There my be other bootstrap resources which may need to be removed, take a look at what your page is loading.
To use jQuery in the same way but for the javascript files, remember to change link to script and href to src
From my tests, Meteor does not automatically re-add the files once they have been removed so you would need to find some way of re-adding them dynamically if you want the same user to be able to go back and forth between the main site and the admin site. Or simply if the http referrer to the main site is from the admin, force reload the page and then the bootstrap resources will load and everything will look pretty.
P.s. make sure you get the href correct for the jQuery version
If somebody is interested in including any js/css files, I've written a helper for it:
if (Meteor.isClient) {
// dynamic js / css include helper from public folder
Handlebars.registerHelper("INCLUDE_FILES", function(files) {
if (files != undefined) {
var array = files.split(',');
array.forEach(function(entity){
var regex = /(?:\.([^.]+))?$/;
var extension = regex.exec(entity)[1];
if(extension == "js"){
$('head').append('<script src="' + entity + '" data-dynamicJsCss type="text/javascript" ></script>');
} else if (extension == "css"){
$('head').append('<link href="' + entity + '" data-dynamicJsCss type="text/css" rel="stylesheet" />');
};
});
}
});
Router.onStop(function(){
$("[data-dynamicJsCss]").remove();
});
}
Then simply use:
{{INCLUDE_FILES '/css/html5reset.css, /js/test.js'}}
in any of your loaded templates :)
Example
-- begin: index.html --
<!doctype html>
<html>
<head>
<title>Index</title>
<script type="text/javascript" src="mootools.js"></script>
</head>
<body>
<iframe src="iframe.html" id="innerFrame">blah</iframe>
</body>
</html>
-- end: index.html --
-- begin: iframe.html --
<!doctype html>
<html>
<head>
<title>iFrame</title>
</head>
<body>
<form>
<input id="inputField" type="text" value="this is text." />
</form>
<script type="text/javascript">
$('inputField').set('value', 'updated text');
</script>
</body>
</html>
-- end: iframe.html --
Currently, $('inputField').set('value', 'updated text'); doesn't work :-\
Yes, assuming the iframe and it's parent window are on the same domain, it is possible to load the Mootools scripts once in the parent, and then programmatically extend the IFrame's window and document, instead of re-loading the script within the iframe. It is not the default behavior, as you've noticed, and probably for good reason - I'm guessing most people will tell you it's more trouble than it's worth.
In fact, the IFrame shortcut element constructor used to do that exact thing, but it was ultimately considered to be too much of a hack and not worth the effort to maintain as part of the framework long-term, so they dropped it - this why the documentation for IFrame is kind of odd ("IFrame Method: constructor, Creates an IFrame HTML Element and extends its window and document with MooTools.", and then right below after the example, "Notes: An IFrame's window and document will not be extended with MooTools methods.").
So, the most straightforward way to have $(..) useable in your iframe is just to have the iframe include the Mootools script. If you're feeling fancy, you could also have your parent window inject the Mootools script into the iframe's HEAD, for example:
index.html
<html>
<head>
<title>Parent</title>
<script type="text/javascript" src="mootools.js"></script>
</head>
<body>
<iframe id="innerFrame"></iframe>
<script type="text/javascript">
var mooFrame = new IFrame("innerFrame", {
src:"iframe.html",
events: {
load: function(){
var mooEl = new Element('script', {
type: 'text/javascript',
src: "mootools.js",
events: {
load: function(){
//passed to mooFrame by the iframe
this.pageReady();
}.bind(this)
}
});
this.contentDocument.head.appendChild(mooEl);
}
}
});
</script>
</body>
</html>
iframe.html
<html>
<head>
<title>Iframe</title>
</head>
<body>
<div id="iframe_element"></div>
<script type="text/javascript">
parent.mooFrame.pageReady = function(){
/* Put your iframe javascript in here */
$('iframe_element').set("text", "Fancy!");
};
</script>
</body>
</html>
Update (July 29th): I was fooling around with this idea again and realized there's a fairly obvious though pretty ham-fisted way to transfer Mootools functionality defined in the parent index.html window to the inner iframe: simply include the entire Mootools source into the parent window (remove the src attribute from the existing script element and add an id), and copy that newly enormous element's text into the new script node that gets injected into the head of the iframe. Inlining the Mootools code in the script element in this fashion gives you access to the contents of the element, which you don't get when the javascript is loaded from an external file via the src attribute.
Of course, this..concept is only relevant if the parent window and iframe are on the same-domain, (same as the code provided above).
The obvious drawback is that the Mootools source isn't cached. I'm not sure if there's a use-case where this method would be more optimal than just including mootools in both parent and iframe. In any event, change the index.html file to this:
<html>
<head>
<title>Parent</title>
<script type="text/javascript" id="mootools_js">
**COPY-PASTE THE CONTENTS OF mootools-core.js HERE**
</script>
</head>
<body>
<iframe id="innerFrame"></iframe>
<script type="text/javascript">
var mooFrame = new IFrame("innerFrame", {
src:"iframe.html",
events: {
load: function(){
var mooEl = new Element('script', {
id: 'mootools_iframe_core',
type: 'text/javascript',
html: $('mootools_js').innerHTML
});
this.contentDocument.head.appendChild(mooEl);
this.pageReady();
}
}
});
</script>
</body>
</html>
My previous answer offered two alternative ways of doing the task in question ("load Mootools in a parent frame and then re-use it in iframes"). The first method didn't "re-use" the Mootools functionality loaded into the parent frame, but was rather an alternative way to load the script in the inner iframe. The second method was just a hacky way of copying over the script by putting the entire mootools core source inline in a script element and then copying that element's content into a script element in the iframe's head (hardly optimal).
This following method does programatically extend the window and document objects of the inner iframe. Again, it is assumed that both the parent page and the iframe are on the same domain.
In my (brief and simple) testing, loading the source in both parent and iframe resulted in 72.1 KB transferred at around 130ms (to finish loading both the parent and iframe pages), while the page that loaded the source and then extended the iframe was 36.8 KB and took around 85ms to load both parent and iframe. (that's with gzip on the server...file size of uncompressed/unminified core source is around 134 kb).
For this method a few trivial additions/edits are made to the mootools core source. Download an uncompressed version of mootools-core-1.3.2.js, and rename it to 'mootools-core-init.js' (or whatever). The following steps assume that you checked all boxes on the core builder page except 'Include Compatibility'.
Add this to the top of the 'mootools-core-init.js' file (above the first self-calling anonymous function):
var initMootoolsCore = function(){
var window = this;
var document = this.document;
Add this to the very bottom of the core js file:
};
initMootoolsCore.call(window);
Do the following find/replace tasks:
1
Find:})();
Replace: }).call(this);
2
Find: if (Browser.Element) Element.prototype = Browser.Element.prototype;
Replace: if (this.Browser.Element) Element.prototype = this.Browser.Element.prototype;
3
Find: var IFrame = new Type
Replace: var IFrame = this.IFrame = new Type
4
Find: var Cookie = new Class
Replace: var Cookie = this.Cookie = new Class
(download | compressed version)
In your parent index.html file, put the following script element in the head
<script type="text/javascript" src="mootools-core-init.js"></script>
Finally, in your iframe.html file, put the following inline script element in the head to extend the iframe's window and document (it must be before any included or inline scripts that need to use Mootools):
<script type="text/javascript">parent.initMootoolsCore.call(window);</script>
No, the iframe.html is an independent page. It does not "inherit" anything from the previous page.