I have a GWT 2.7 application where a lot of the styling is done in a CSS file referenced in the module XML file. I'd like to migrate this file to GSS so I can use some of the new functionality like variables and functions.
The GSS Migration Guide has instructions on migrating to GSS, but it appears to apply only to styles used through CSSResource objects. My understanding is that the only class selectors that can be accessed through CSSResources are those for which one declares accessors, and that all these class selectors will be obfuscated. This won't work for me because the selectors I want to use are unobfuscated ones that have mainly been added through UIObject.addStyleName and addStyleDependentName. I also don't want to have to add an accessor to an interface every time I want to add a new style.
As for the other three ways of using CSS files in a GWT project:
Using a <link> tag in the host HTML page.
Using the <stylesheet> element in the module XML file.
Using an inline <ui:style> element in a UiBinder template.
The first two don't seem to support GSS: even if I specify a GSS file it simply gets served directly to the browser without processing.
Is my only choice, then, to migrate to UIBinder? And if so, what's the minimal way to do this? (My current HTML host page is just the default host page with some additional stuff in the <head> element, so I feel like this ought to be straightforward).
Using a <ui:style> is the same as using CssResource. The .ui.xml file will generate the required ClientBundle and CssResource files, and those in turn will convert the GSS to css.
When you refer to <ui:style> content in a .ui.xml file, you do so as if you were caling an accessor on the interface, as {style.myStyleName} - because you are calling the accessor, but it is generated automatically. This may limit what you can do in your <ui:style> tag slightly. On the other hand, once you write CSS, your IDE can almost certainly add the accessors for you automatically, and if it doesn't, the failed recompile will list them so you can add them.
If instead you simply put a string literal "my-class-name" in your Java or HTML, the compiler has no way of knowing that "my-class-name" is the same or different everywhere in your app, and when to use one CssResource versus another. It also cannot rewrite those strings, so your CSS would remain unoptimized. Plus, now you can check for usages of a given css class name accessor - if no one uses it, delete it, then delete the CSS that uses it, to keep your .gss files smaller, and your application smaller.
You are correct in that <link> tags in the html, and <stylesheet> tags in the .gwt.xml do not result in running GSS. You do not need to use UiBinder (and I'm not a fan personally, but some people like it), but you do need to use the accessors, or there is no point to GSS as it stands.
If you want to avoid obfuscation and renaming, consider just running the closure-stylesheets directly, or something like LESS or SASS on your plain CSS file, which have no GWT integration, and so will work just fine with your string literals already in use. This will result in bigger compiled outputs, and remove your ability to find and remove unused CSS in an easy way.
As I discovered later on, it turns out that you can inject a GSS (or CSS) stylesheet as a CSSResource without the selectors being obfuscated. Simply add the following annotation to the stylesheet:
#external '*';
This will mark all selectors in the file as external, which means 1) that the CSSResource subinterface used as the type for the stylesheet resource will no longer be required to implement accessor methods corresponding corresponding to the class selectors in the stylesheet and 2) that the selectors in the stylesheet will be unobfuscated, so for example if you have a class .foo in the stylesheet you'll be able to apply it to entities using UIObject.addStyleName("foo").
This way you can easily get some of the major benefits of GSS (variables, functions, etc.) and CSSResource (injection into the page rather than serving another file) without having to make any changes to your workflow.
Related
If we give cq:includeClientLib inside my component jsp and if we drag and drop the component twice on that page, will the clientlib gets loaded/included twice?
what will be the case if we do in Sightly way (data-sly-call="${clientlib.all # categories='somecategory'}") ?
And also what is the suggested method of including client libs, either create a clientlib specific to the component and load only for that component or include all the CSS and JS at a common clientlib and use it across?
No, the clientlib is only included once for a category.
This is by design as the HTL (and respective JSP tag) are evaluated during runtime and the processor keeps a map of categories that have already been included and does not include them again.
As #i.net mentioned, each category will only be included once. To answer your follow up question about the suggested method..
The best practice seems to be to define a client library for each component, which is then embedded into a "global" client library. That global client library will then be included within your page template.
/etc/designs/acme/clientlibs-all
categories=["acme-all"]
embed=[compA,compB]
/apps/acme/components/compA/clientlibs
categories=["compA"]
/apps/acme/components/compB/clientlibs
categories=["compB"]
The reason the global client library is located under /etc/designs is to prevent exposing /apps to the public. However, in AEM 6.3, you could make use of the allowProxy property to serve the code at /etc.designs/. This would then look like this:
/apps/acme/clientlibs/clientlibs-all
categories=["acme-all"]
embed=[compA,compB]
allowProxy=true
/apps/acme/components/compA/clientlibs
categories=["compA"]
/apps/acme/components/compB/clientlibs
categories=["compB"]
Adobe recently released a good tutorial of more recent best practices around client library structure: https://helpx.adobe.com/experience-manager/kt/sites/using/getting-started-wknd-tutorial-develop/part3.html
I would like to implement an extensible templating mechanism in AEM, so as to permit component users to control markup for individual projects (designs) without modifying the components' pre-defined JSPs.
I have extended the <cq:include> tag to permit this, by passing a template name, which is then retrieved from the current design, falling back to the default markup when an override does not exist in the design:
<ct:template name="listNav/prev" />
This should load the jsp script from [1], unless the location does not exist, defaulting to [2]:
/etc/designs/projectName/component_templates/listNav/prev.jsp
/etc/designs/component_templates/listNav/prev.jsp
When using the extended tag, I'm receiving the exception (yes, the file exists):
Caused by: org.apache.sling.api.SlingException: javax.servlet.ServletException: javax.servlet.jsp.JspException: Could not find script /etc/designs/component_templates/listNav/prev.jsp
This all works when the component_templates is under /apps. Is there any way to make this work? Is there a better approach? I'd prefer to keep the component_templates with the designs, if possible.
I don't think it is a good idea to put application script to etc. They should be under /apps.
But I think it could work, if you add /etc path to the "Resource Search Path" of this service:
system/console/configMgr/org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl
you can choose between different "designs" within the advanced page properties tab! afaik you should use this mechanism to declare different designts i.e. stylesheets etc. to your pages and of cause you don't need to modify anything at the jsp's to switch between different styles if implemented properly.
Have a look at this:
Adobe AEM Designs
Right now, I am using <%- assets.js() %> to include all the javascript files on all pages. So, it means all the functions will be initialized on all pages.
I am wondering how can I disable a javascript file on a specific web page? Or, if there is a way to include some of the javascript files on a specific web page but not all the javascript files.
The trouble with the auto-loading is that it's really hard to dictate the ordering of the source files in any meaningful way-- it really comes down to how you've structured the front-end.
In v0.8.x (the version you're working with, from what I can tell), you can use the config/assets.js to control the ordering of folders that get loaded in. This is not ideal, but is a decent workaround that my team used on several projects.
In v0.9, we've removed rigging/asset-rack in favor of tight integration Grunt, which has a large community and some really cool and well maintained packages for most types of asset bundling, etc.
In any case, here are the different approaches you can investigate for serving assets in the new version of Sails.js:
Treat it just like anything else
In your layout.ejs file, create <link /> and <script></script> tags to link in your css and javascript files like you would normally.
Use AMD (Require.JS)
I think lots of folks would say this is actually the best option. Require is a pretty powerful tool. And I'm mostly in agreement-- if you're working with front-end javascript that could be coming from anywhere, and is going to be extended by other developers who may be using a different framework, AMD is a great way to make sure you stay safe. If you're using Require, each js file is its own module, and declares its own dependencies, so asset dependency management becomes a thing of the past. Then in production mode, there are a couple of different options to compile and minify your CSS and JS. You can even dynamically load templates and CSS from JS with Require, which is pretty neat. AMD/RequireJS is a hands-down winner if you're interested in loading some or all of your assets asynchronously. It's also an all-client-side solution, which is pretty cool.
The only downside in my mind is the complexity. If you have control over the framework being used, you really shouldn't have to manually enter dependencies for each file-- it can figure that out itself (see https://github.com/balderdashy/mast/blob/2.x/lib/raise.js)
Use Grunt
When you make a new project with sails new foo in Sails v0.9, a file called Gruntfile.js is created. It has lots of stuff in it, a lot of which isn't being used by default. You can do almost anything with Grunt, but in particular, you'll want to look at how it's set up to copy files from assets/* to .tmp/public/.
sails new foo --linker (Sails v0.9 only)
Linker is a lot like what asset-rack/rigging does currently. It creates the same Gruntfile as #3 above, but utilizes more of the contents. It will auto-link files in the order you specify. Instead of view partials (e.g. <%= %>), the scriptlinker plugin allows you to customize the delimiters where js, css, and templates will be injected. By default, the Gruntfile is set up to use JST precompiled templates, but again, you can set it up however you like.
Hope that helps guys, and best of luck!
-Mike
PS- v0.9 is coming out very very soon, I've just been working through tests and issues to make sure we're 100% there. If you'd like to try it out, check out:
https://gist.github.com/mikermcneil/5930330
The lead for Sailsjs replied to this issue (though it was about selecting CSS files):
"For now, you can (a) bring in all styles all the time and make only the relevant ones apply (b) use another tool (like Grunt) to bundle assets like you would in a vanilla node.js project or (c) link the stylesheets manually (put them in your public folder)."
See: choosing assets sailsjs
Similar, more complex questions have been asked in the Google group:
https://groups.google.com/forum/#!topic/sailsjs/yt9EpJlfzXA
Considering the above, you may want to have a separate layout.ejs for each page. You can specify the layout.ejs you want for each page with
res.view({ layout: "different_layout" })
The layout.ejs would (a) not call assets.js() but have < script > for all the js files needed, or (b) call assets.js() to serve all the common js files in ./assets/js plus < script > to serve the page dependant ones residing elsewhere.
I have a wrapper around assets.js() that allows you to include all assets except for specified files. You can also use it to include only specific assets elsewhere. So you could load your common assets in layout and include other assets only on pages where they are required.
See my answer to How can I include javascript assets selectively in SailsJS?
I am developing a web app with GWT. I'd strongly prefer to code everything in Java instead of hardcoding anything in HTML and CSS. I'm also using GQuery (or GWTQuery) and I'm wondering if using compile time selectors with GWTQuery completely eliminate the need to hardcode any HTML or CSS at all - and have the speed and performance of a fully hardcoded app?
Let me explain with few words what gwt and gquery are.
1- GWT is just a compiler which gives you the tools to produce optimized javascript from java.
2- The js produced with GWT is mainly used to modify the DOM (apart from calculations, business code, ajax etc).
3- GWT additionally gives you a set of widgets, as an abstraction of the DOM, so as you can work with panels, buttons, trees, etc. instead of raw HTML. I think this is what you call hardcoded html.
4- But GQuery is a complement to GWT. It gives you a set of utilities taken from jQuery and ported to java to write less code, it is mainly oriented to manipulate the DOM (select nodes, modify, animate, etc) apart from other cool features like an easier ajax syntax, safe typing, promises, json/xml data-binding, etc.
Said that, don't think that the gwt widget abstraction will make you completely forget about dom and css. Sooner that later you will need to create your own widgets, or customize the current ones.
I think that the only way to forgot almost the DOM is to use a 3rd party widget library like gxt, mosaic, smart-gwt, etc. Because the GWT widgets are tough, they are designed to give the designer the option of easy stylizing them with css.
In the other hand, gwtquery will not help you to forget the DOM at all. It is designed to enhance static DOM elements which are in the page, enhance the DOM of gwt widgets, or create your own DOM structure based on html.
About gQuery compiled selectors, they are used in the same way dynamic selectors are: to select DOM elements, but with a much better performance because the compiler optimizes them. I think you misunderstood their objective, they don't give you any way to eliminate hardcode HTML.
UPDATE: To answer your last comment, it is NOT possible to generate static HTML or CSS with
GWT or GQUERY. They always produce static JAVASCRIPT code written to .js files (or written into javascript tags in .html files depending on the linker)
I'm trying to figure out exactly what css is included by the standard call to currentDesign.writeCssIncludes(pagecontext); found in headlibs.jsp. The documentation states simply that it is
Convenience method that writes the CSS include strings to the response.
Looking at what it seems to do, it will include /etc/designs/currentdesign.css which is built off the design components css, and /etc/designs/currentdesign/static.css, which is just a static file. But is this all that is will include?
In particular, what I'd like to do is include a clientLib-processed css file as part of my design. One way to do this is to manually build out the css include:
<link rel="stylesheet" href="<%= currentDesign.getPath() %>/myclientlib.css" />
But I'd prefer to let that get generated automatically, so that my designers have flexibility to structure the css files differently for different designs (i.e., for the "base" design they are fine with just a static.css file, but for the "fancy" design they want to use LESS css and break up the files more granularly). And it would be helpful to put design-specific css info with the components they affect, rather then needing to separate those.
You can use the <cq:includeClientLib> tag, combined with themes and/or categories, to mix and match bits of CSS.
But you may find it somewhat limiting; for instance, you can't specify a media attribute. If you need to do this, or your designers don't structure their CSS in a way that fits the themes/categories model, your fallback is the technique you've identified in your question, using <link> directly.
Update
An excellent question about themes! I have only seen them used in passing.
You can define a theme by just adding a new folder/node under /etc/designs/yourproject/clientlibs/themes, as a sibling to default.
You can pull in the clientlibs for a theme with the <cq:includeClientLibs> tag, perhaps under the control of some conditional logic. For instance, in one of my projects I have a theme called authoring which I only want to apply to the author instance; I pull it in with this code in headlibs.jsp:
<c:if test="${ (global['wcmmode'] eq 'EDIT') || (global['wcmmode'] eq 'PREVIEW') }">
<cq:includeClientLib theme="apps.myproject.authoring" />
</c:if>
I have not seen any documentation that would apply theme automatically to a particular subtree of the content tree, or based on the presence of a tag.
There is that cryptic statement "The theme name is extracted from the request." in the Adobe docs, which is backed up by this statement in the Sling docs, "ThemeResolverFilter Provides the Theme for the request. The theme is provided as a request attribute." So perhaps tacking &theme=apps.yourproject.foo onto a query string would apply that theme.
The list of CSS files is based on the property 'cq:designPath' of the page.