I'm building a simple web site in Dart Web UI. Each page has a header (with site navigation) and a footer. I've used components for the header and footer, and each page looks something like this:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<link rel="import" href="header.html">
<link rel="import" href="footer.html">
</head>
<body>
<header-component></header-component>
Page content...
<footer-component></footer-component>
</body>
</html>
This works well, but the components aren't inserted to the HTML itself but loaded dynamically from Dart (or JavaScript) code. Is there some way to have the Web UI compiler insert the header and footer to the HTML file itself so that they would be visible to search engines and to users who have JavaScript disabled?
There isn't a direct way to do this.
This is typically a server-side task: the server takes care to generate the required HTML.
Web components are all about client side, so they work on what's already delivered to the browser.
However, build.dart scripts is executed each time a file in your project changes so you can extend the script to get what you want. I don't think this is a good approach, but it solves your problem.
First add the following placeholder to the target html file (in my case web/webuitest.html):
<header></header>
Now add a header.html file to your project with some content:
THIS IS A HEADER
Now extend the build.dart script so it will check if the header.html was modified, and if it was, it will update webuitest.html:
// if build.dart arguments contain header.html in the list of changed files
if (new Options().arguments.contains('--changed=web/header.html')) {
// read the target file
var content = new File('web/webuitest.html').readAsStringSync();
// read the header
var hdr = new File('web/header.html').readAsStringSync();
// now replace the placeholder with the header
// NOTE: use (.|[\r\n])* to match the newline, as multiLine switch doesn't work as I expect
content = content.replaceAll(
new RegExp("<header>(.|[\r\n])*</header>", multiLine:true),
'<header>${hdr}</header>');
// rewrite the target file with modified content
new File('web/webuitest.html').writeAsStringSync(content);
}
One consequence of this approach is that rewriting the target will trigger build.dart once again, so output files will be built twice, but that's not a big issue.
Of course, this can be made much better, and someone could even wrap it into a library.
Currently, no, it's not possible. What you want is server-side rendering of those templates so that you can serve them directly to the client when they request your pages (including search spiders).
You might want to keep track of this issue however: https://github.com/dart-lang/web-ui/issues/107?source=c
When it's finished things are looking better.
Related
I tried to set up an Orbeon form with a tree (xf:select1 appearance="tree"). Using Orbeon Demo form builder I created a page with tree and it is working in Demo environment. The same XForms code in my local Tomcat environment (latest Orbeon CE) is not working - the tree is not rendered at all.
If I check working demo HTML, then there are some additinal JS after extension functions (after sections /*! Extension 'jquery.fancytree.wide.min.js' */, etc.) and there is minimized code line a.declareCompanion("fr|tree-select1",b); which I believe registers the component. The same form code my in local Tomcat instance does not generate this block in JS files.
In Orbeon source code is file TreeSelect1.scale which I believe is converted to JS and then included in rendered HTML JS files.
Also in tree-select1.xbl strange I comment:
NOTE: When using this component outside of Form Runner, the supporting JavaScript must be explicitly included.
after:
<xbl:script src="/xbl/orbeon/tree-select1/fancytree/jquery-ui.min.js"/>
<xbl:script src="/xbl/orbeon/tree-select1/fancytree/jquery.fancytree-all.min.js"/>
What must be done to be able to render the tree?
The JavaScript for the component is not included by default. You can work around this with this:
<xh:script
type="text/javascript"
src="/apps/fr/resources/scalajs/orbeon-form-runner.js"/>
And then, on your main XForms model, put the xxf:assets.baseline.excludes attribute like this:
<xf:model
xxf:assets.baseline.excludes="/ops/javascript/scalajs/orbeon-xforms.js">
Regarding your other question about which JavaScript files are under xbl vs. not: some JavaScript files are written by hand, and are available as separate assets.
But code for other components like the tree is written in Scala and compiled with Scala.js. The resulting JavaScript for all such code is optimized and available in orbeon-xforms.js, orbeon-form-runner.js, and orbeon-form-builder.js depending on the environment. Only one of those 3 files must be included, hence the use of the xxf:assets.baseline.excludes property.
I have an angular2 app running on top of .NET project. We access the app at http://example.com/index/app.
But immediately after the angular2 app starts running and appearing, our URL is rewrited in the browser as: http://example.com/#/dashboard for example.
Trying to go directly to http://example.com/index/app/#/dashboard redirects or more precisely rewrite our url to http://example.com/#/dashboard an displays the app.
How can I prevent this behavior to constantly have the full URI? http://example.com/index/app/#/dashboard Angular2 part being only the after # one.
Your base path is wrong. You have to add
<base href="~/index/app" />
to your html (index.html, index.cshtml, or _Layout.cshtml, depending on the way you have done it) document. Remove the ~ if you are not using Razor views.
Update based on comments
You'll have to define a section within your _Layout.cshtml which will render it on a per view basis.
in _Layout.cshtml:
<head>
...
#RenderSection("base", required: false)
</head>
in your Views/Index/App.cshtml:
#section base {
<base href="~/index/app"/>
}
When I create an MVC project that uses Razor, the following lines are generated:
#Scripts.Render("~/bundles/jquery")
#RenderSection("scripts", false)
What is the difference between these lines ? Is it the case that #Scripts.Render generates a <scripts> section and then #RenderSection("scripts", false) renders it ?
The first one renders out a bundle, which is a group of related Javascript files. For instance, you might want to bundle jQuery and jQuery UI together. Bundles also get the benefit of bundling and minification when a solution is compiled in release mode. ref: http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification
#RenderSection indicates that a page that uses the layout in question can inject markup in a particular spot in the layout. Sections are in effect a placeholder (and work much like the ContentPlaceHolder server control from web forms if you are familiar with that). That reference is probably right before the closing body tag, where it is believed by some to be the best spot to put scripts. You could have a section called scripts, or head, or footer, it is completely arbitrary and sections don't necessarily have anything to do with scripts at all. ref: http://weblogs.asp.net/scottgu/asp-net-mvc-3-layouts-and-sections-with-razor
These are completely different things.
The #Scripts.Render("~/bundles/jquery") is rendering a group of scripts for you, creating all the <script> tags. in the BundleConfig.cs file you are able to create bundles of scripts and css files.
One of its advantages allow you to group multiple files that are commonly used together. Instead of including each of these files explicitly you can include all of them using explicit Scripts.Render(groupName).
You can read more about Bundling and Minification here.
The #RenderSection("scripts", false) is about rendering a section in the view.
A section allow you to specify a region of content within a layout. It expects one parameter which is the name of the section. If you don’t provide that, an exception will be thrown.
Here is a good article explaining about Layouts, RenderBody, RenderSection and RenderPage in ASP.NET MVC.
The first parameter to the “RenderSection()” helper method specifies
the name of the section we want to render at that location in the
layout template. The second parameter is optional, and allows us to
define whether the section we are rendering is required or not. If a
section is “required”, then Razor will throw an error at runtime if
that section is not implemented within a view template that is based
on the layout file (which can make it easier to track down content
errors). If a section is not required, then its presence within a
view template is optional, and the above RenderSection() code will
render nothing at runtime if it isn’t defined.
I'm using CKEditor for the first time and trying to do something that I thought would be very simple to do but so far I've had no success.
Essentially I want to place the editor.js, config.js and styles.js in a scripts folder but want the "Skins" folder that contains the css and images to appear within a separate "Styles" folder.
The application consists of a simple view that displays the editor on load.
The code to display the editor is a follows:
angular.element(document).ready(function () {
CKEDITOR.config.contentsCss = '/Styles/CKEditor/';
CKEDITOR.replace('editor');
});
The HTML within my view is as follows:
#section scripts
{
<script src="~/Scripts/ckeditor.js" type="text/javascript"></script>
<script src="~/Scripts/angular.js"></script>
<script src="~/Scripts/Main.js" type="text/javascript"></script>
}
<h2>Index</h2>
<textarea id="editor" name="editor"></textarea>
This is an MVC application and the scripts are rendered at the end of the body within the layout view.
The editor will not display in any browser. As I understand it setting the contentsCss property should do the trick.
If I place the skins beneath my script folder it works fine. I can see in the generated source that it is adding a link to the header pointing to /Scripts/Skins/moono..., but I want it to add a reference to /Styles/Skins/moono...
Is what I am trying to do feasable and if so what am I missing here? I was expecting this to be simple.
As a work around I could just add some routing rules that redirects the relevant request to a different location, but I'd rather get to the bottom of the issue before I do this.
Further information:
My application is an ASP.net 4.5/MVC 4 app.
I'm referencing angular because I'll be using that once I've sorted this issue. I have tried removing all references to angular but the problem still persists.
I've tried setting the contentsCss property in the following ways:
Directly using CKEDITOR.config.contentsCss
Within the config.js file. The sample assigns an anonymous function to CKEDITOR.editorConfig and in there you can manipulate congif entries.
Passing a config parameter when calling the "replace" method on the CKEditor object.
I've tried manipulating the contentsCss property both before and after the call to replace.
I'm using the latest version of CKEditor (4.2)
Thanks to #Richard Deeming, I've found the answer.
I'm using the default moono style, so I needed to set the CKEDITOR.config.skin property as follows:
CKEDITOR.config.skin = 'moono,/Styles/CKEditor/Skins/moono/'
My final code now looks like this:
angular.element(document).ready(function () {
CKEDITOR.config.skin = 'moono,/Styles/CKEditor/Skins/moono/';
CKEDITOR.replace('editor');
});
You have to set the url to the actual folder containing the skin itself (I thought CKEditor might append skins/mooono itself but it doesn't).
I also found that you must include the final '/' from the URL.
Looking at the documentation, you need to specify the path as part of the skin name:
CKEDITOR.skinName = 'CKeditor,/Styles/CKeditor/';
So in the Assets folder there are two folders for javascripts and stylesheets.
So I added a spike.js and a CSS file of spikecss.css to those folders and wrote some Javascript and CSS code fin them.
Now I am in the index.html page and writing something like this? Which doesn't work. How do I wire all these thing together to work?
This is a test app mostly for the javascript side of the deal so I really don't have models, controllers,views at this point. I am drawing D3 chart in the JavaScript file so I just want to show them when I hit the webpage
Javascript file:
var data = [4, 8, 15, 16, 23, 42];
var chart = d3.select("body").append("div")
.attr("class", "chart");
chart.selectAll(chart)
.data(data)
.enter().append("div")
.style("width", function(d){return d*5 +"px";});
Index.html file:
<!DOCTYPE html>
<html>
<head>
<title>Here we go</title>
</head>
<body>
javascript_include_tag :defaults, "spike"
</body>
</html>
UPDATE
Based on Ryan's method used in this railscast, you can have a dynamic index.html.erb page with just these 2 steps (the original, slightly more complex answer can be seen below):
First, set your routes.rb file to look like this:
MyApplication::Application.routes.draw do
root to: 'application#index'
end
Next, move your /public/index.html file to /app/views/application/index.html.erb. Don't just copy it - move it. This means you should not have an index.html file in your /public directory.
Congratulations, you now have a dynamic index page that can handle ERB markup.
ORIGINAL ANSWER
The /public/index.html file is statically served, so you'll need to write it as if you weren't using Rails. Add your css and javascript locations to the <head> section, using <link> and <script> respectively as you would for any vanilla HTML page.
If you prefer to do this the Rails way, you'll need at the very minimum:
an entry in routes.rb
a corresponding empty controller
and an index.html file (or index.html.erb, index.html.haml, etc) at /app/views/some_name/ where some_name is the same as your controller name (minus the word 'controller').
You can then add your custom file names to your /app/assets/javascript/application.js and /app/assets/stylesheets/application.css manifests to have them pulled into the page.
Alternatively, you can edit your /app/views/layouts/application.html.erb template manually using embedded ruby. See here and here for documentation for how to do this.
Oh, one other thing - the javascript you wrote will execute before the DOM has loaded as it stands right now. To avoid this, you'll need to wrap it with something like:
$(function(){ /* your code here */ });