Current I did the dustjs in client javascript as below
<!DOCTYPE html>
<html>
<head>
<script src="lib/dust-full-0.3.0.min.js" type="text/javascript"></script>
<script src="vendor/jquery-1.7.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function(){
// JSON response from server
var json_object = { "profile_skill": "Profile Skill",
"skills": [
{ "name": "JavaScript" },
{ "name": "Ruby" },
{ "name": "Java" }
]
}
// render method
dustRender = function(){
// Get the dust html template to dust compile
var dust_tag = $('#page').html() ;
var compiled = dust.compile(dust_tag, "tmp_skill");
//load templates
dust.loadSource(compiled);
//Renders the named template and calls callback on completion. context may be a plain object or an instance of dust.Context.
dust.render("tmp_skill", json_object, function(err, html_out) {
//HTML output
$('#page').html(html_out);
console.log(html_out);
});
}();
});
</script>
</head>
<body>
<h1>Dust templates in the browser</h1>
<div id="page">
{profile_skill}
<ul> {#skills}
<li> {name} </li>
{/skills}
</ul>
</div>
</body>
</html>
But in my page view source I can see the above code instead of html tag output. And also I want know how to integrate dustjs in php code.
Don't just put your template inside the php. Do it properly and define the template as a string or separate html file.
var templateName = "myTemplate";
var model = { "profile_skill": "Profile Skill",
"skills": [
{ "name": "JavaScript" },
{ "name": "Ruby" },
{ "name": "Java" }
]
};
dust.onLoad = function(templateName, callback) {
// this will load a template with [name].html in the folder templates
// callback is an internal dust.js function
$.get(["templates/", ".html"].join(templateName), function(data) {
callback(undefined, data);
}, "html");
};
// dust just takes the template name as first parameter
// if no template of this name is defined it will attempt to load
// and compile it if needed
// override dust's onLoad function to implement your loading
dust.render(templateName, model, function(err, out){
$('#page').html(out);
});
Inside my template.html
{profile_skill}
<ul> {#skills}
<li> {name} </li>
{/skills}
</ul>
Of course the point is that compiling your templates always speeds up delivery and rendering. However, since you deliver the template together with the rest of your page, calling loadSource and compile is just not necessary. Instead dust will try to load a temploate all by itself if you tell it to do so.
From the documentation:
Loading
(...)
By default Dust returns a "template not found" error when a named template cannot be located in the cache. Override onLoad to specify a fallback loading mechanism (e.g., to load templates from the filesystem or a database).
Internally dust will call the loadSource and compile methods itself if it has to. In my example above I included a possible soulution to override dust.onLoad. Of course you could also simply return a the html contents of a DOM node there.
dust.onLoad = function(templateName, callback) {
callback(undefined, $("skill template").hmtml());
}
And to answer your last question:
And also I want know how to integrate dustjs in php code.
You can't. Unless you send the template to client to render there or you have a JavaScript interpreter on your backend to render the templates you can't use it.
As Torsten Walter has mentioned, you cannot see the html source in your page, if you are compiling/rendering in browser. If you do the compiling and rendering in the server side, html source will contain the final HTML code. For achieving this, you can use nodejs or Rhino server as mentioned in Linkedin blog: http://engineering.linkedin.com/frontend/leaving-jsps-dust-moving-linkedin-dustjs-client-side-templates
This might help you to compile the dust templates using PHP,
https://github.com/saravmajestic/myphp/tree/master/dustcompiler
This utility is only for compiling the dust templates before rendering in the page, which will avoid compiling time in your browser. You can load the compiled templates as a JS file in your page, which can be minified/ aggregated with other JS files/templates.
Hope this helps!!!
Related
I want to mix basic HTML generated by Php and VueJS components without having to use VueJS all the way down to the lowest dom leaf.
The parent layout has the Vue app applied all the headers navs assides etc are Vue components and the main content on most pages are still plain HTML generated by PHP.
I want to upgrade small parts of the main content with Vue components but I cant use them once HTML has been used:
Php script generates the entire dom
<div id="app"> VueJS instance mounts here.
<Cats>This component works perfectly here.
<div id="main Content">More HTML content generated by PHP.
<Cats> Cats does nothing here.
<Cats> works perfectly fine in the upper DOM but after rendering some basic HTML the app will no longer populate components further down.
Php can render JS in a script tag but it's unable to use any imports.
Is there any way to get the Vue instance to treat all <Cats> tags as the Cats component regardless of where it is written on the page?
So far I have tried:
async components.
portals using mounting portals.
creating another Vue instance and mounting it to another ID.
Any ideas?
EDIT: The things I tried previously may be been blocked by the mishmash of multiple UI modernization attempts in this monolith I have inherited.
So I would not rule those out for others facing this situation.
If you are dynamically passing the template which is coming from an API or from any other source into your Vue instance. There is a way to access that via v-html attribute but before using that please have a look at note below.
Note that the contents are inserted as plain HTML - they will not be compiled as Vue templates.
Hence, As you are trying to bind/render the Component itself and it's not a plain HTML. So it will not processed by Vue's template compiler.
Possible solution is that you can get a component name and props from your PHP API instead of whole HTML template.
You can also achieve the same by using Vue compile option. Here is the demo :
Vue.component('Cats', {
props: ['msg'],
template: '<h3>{{ msg }}</h3>'
});
// Template coming from PHP
const template = `<div id="main Content"><Cats msg="Meow... Meow..."></div>`
var app = new Vue({
el: '#app',
data: {
compiled: null
},
mounted() {
setTimeout(() => {
this.compiled = Vue.compile(template);
})
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<Cats msg="Meow...">
</Cats>
<component :is="compiled"></component>
</div>
In the end, I had to manually add a JS file to the build configs containing a global window.ExtraVue with a setup function like so:
import Cats from 'files/Cats.vue';
window.ExtraVue = {
setup: function(data) {
new Vue({
el: '#extra-vue',
data: data,
components: {
Cats
},
template: '<Cats/>'
})
}
};
The setup function is then called at the very end of the PHP script.
<script>window.ExtraVue.setup(<?=json_encode($array)?>)</script>
The setup function can now create a new Vue instance for each component that needs to be out on its own.
Ideally, it would be great if VueJS could just replace matching tags with components.
Before I started with Joomla I developed a html page that uses jqplot, and that pages worked/works fine.
Now I want to include jqplot in a custom joomla (3.3) component that I'm developing, but when calling the component (by means of main menu item) no chart is shown.
UPDATE DEFAULT.PHP (JOOMLA) CODE FURTHER TO COMMENT:
<?php
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
$document = JFactory::getDocument();
//add jqplot libraries
JHtml::_('jquery.framework');
$document->addScript(JPATH_ROOT.'/media/system/js/jquery.jqplot.min.js');
$document->addStyleSheet(JPATH_ROOT.'/media/system/js/jquery.jqplot.min.css');
$document->addScript(JPATH_ROOT.'/media/system/js/jqplot.barRenderer.min.js');
$document->addScript(JPATH_ROOT.'/media/system/js/jqplot.categoryAxisRenderer.min.js');
$document->addScript(JPATH_ROOT.'/media/system/js/jqplot.pointLabels.min.js');
$document->addScript(JPATH_ROOT.'/media/system/js/jqplot.enhancedLegendRenderer.js');
$document->addScript(JPATH_ROOT.'/media/system/js/weqlib.js');
?>
<head>
<script type="text/javascript">
jQuery(document).ready(function(){
var plot1 = jQuery.jqplot ('chart1', [[3,7,9,1,4,6,8,2,5]]); //copied from example at http://www.jqplot.com/tests/line-charts.php
}); //$(document).ready
</script>
</head>
<!--<h1><?php echo $this->msg; ?></h1>-->
<h1>Prosumer Dashboard </h1>
<div id="chart1" style="width:600px; height:250px;"> </div>
I think the way I call the libabries is wrong (I know for sure the jqplot function call is ok, as I also copied this from my old html file).
Any idea what I'm doing wrong and how to fix this?
You should add javascript to the head of the page in the following way...
<?php
$document = JFactory::getDocument();
$document->addScript('/media/system/js/sample.js');
?>
Your script currently will be attempting to load the javascript in relation to the page's URL, not in relation to your component's URL, so it will not be finding them.
Also, Joomla 3 comes with JQuery - load it like this (possibly in your template rather than your component)...
JHtml::_('jquery.framework');
Note that this runs in no conflict mode, so you have to replace any '$' in your jquery scripts with the 'jQuery'. You can also make it run in conflict mode...
JHtml::_('jquery.framework', false);
However, this may cause problems if your site has any other libraries running.
Situation:
A PHP Templating system that is responsible for building a pages HTML.
Javascript core functions are all in external files
Each Template has some default Javascript Functions that need to be called on a per/template basis
When A page is rendered, I next need to call a set of Javascript functions: i.e.
<script type="text/javascript">
$(document).ready(function{
API.loadThis(); // all these Javascript functions are in an external JS file
API.loadThat();
API.buildDateSelector("#idForSelector");
// etc
});
</script>
Up until now I have just appended that as text to each of the HTML templates. However, in the case of a Page that consists of multiple smaller Templates (each possibly containing their own initialization Javascript), then I have multiple bits of inline Javascript thrown all over my webpage.
My question is:
how should I properly organize everything so that I can easily "register" or "trigger" some default Javascript to be called upon page load?
Or is appending each block of Javascript to each template (like above) appropriate?
What I would do is quite similar to #Chris. However, I'd suggest a few minor changes:
Add a parameter to the addJS function which indicates the position on the page. By default you should have support for at least head and foot (head would place it in the head, foot would place it right before the closing </body>).
public function addJS($string, $position = 'head') {
if (!is_array($this->js[$position])) {
$this->js[$position] = array($string);
} elseif (!in_array($string, $this->js[$position])) {
$this->js[$position][] = $string;
}
}
Then, include tokens in the template to indicate the positions:
{{js_head}}
</head>
<body>
<!--content here-->
{{js_foot}}
</body>
Then, when rendering, just do something like:
$js = $this->js;
$positions = preg_replace_callback(
'/{{js_(inline_)?([a-zA-Z0-9]+)}}/',
function ($match) use ($js) {
if (isset($js[$match[2]])) {
$js = implode("\n", $js[$match[2]]);
if ($match[1] == 'inline') {
return $js;
} else {
return '<script type="text/javascript">'.$js.'</script>';
}
return '';
},
$templateBody
);
Now, the real benefit is that your templates can cleanly and trivially define their own positions for re-used and commonly used bits:
$this->addJS('return this.form.submit();', 'submit_form');
$html = '
<input type="text" onblur="{{js_inline_submit_form}}" />
<button name="submit" onclick="{{js_inline_submit_form}}" />
';
It can be quite useful since now you're not duplicating the JS calls everywhere in your code. Plus, it'll reduce the overhead of wrapping each output in <script> tags (since it wraps the entire position in the tags, rather than each piece of content)...
This would allow you to then take all of the non-inline JS and compile a series of files at run-time to send to the browser to take care of caching. It adds the benefit of being able to keep your JS close to your views (for maintainability) yet still serve cached JS and not have to re-send it every time...
public function buildJSCache($position) {
if (!isset($this->js[$position]) || empty($this->js[$position])) {
return '';
}
$file = implode($this->js[$position]);
$name = 'js/'.md5($file) .'.js';
if (!file_exists($name)) {
file_put_contents($name, $file);
}
return $name;
}
Then, in your template code, just do:
$replace = $this->buildJSCache('head');
if ($replace) {
$replace = '<script type="text/javascript" src="'.$filename.'"></script>';
}
$template = str_replace('{{js_head}}', $replace, $template);
You get the double-win of maintainability and speed to the user (you could even minify it if you wanted).
Note: all of this code is demonstration only, if you were to use it in production, I'd clean it up and think it out a bit further...
That's my $0.02 at least...
There are a lot of ways to do this. I suggest that you get some open source frameworks, look at how they do things and decide on your preferences. Some techniques are considered "best practice", but a lot of a framework's structure boils down to developer preference.
I have a framework I use for my own projects that is similar to what you describe, each of the smaller templates is termed as a "component". Each component is able to add any amount of javascript to itself using an addJS() method, likewise for css and html. A top-level controller loops through a given page's content (in terms of components). This give me a chance to have all the css, javascript, and html loaded ahead of time. Then, I can output them in whatever order I see fit.
So:
page controller handles request
inits one or more components
component load method populates html, javascript, and css class properties (arrays of file names, string for html)
for itself
components has its own set of templates, js, css
outputs site-wide templates
includes all component css within page header
iterates through components, outputs component html, layout stuff
outputs component JS for page footer
I have a components folder, within it are folders for each component. Within each component folder is a php file (that component's handler), optionally one or more css/js files. This keeps everything organized nicely.
I'm loading some content (NOT THE HTML headers or anything, just HTML formatted content from a PHP) via jquery .load() into a div.
It works, perfectly for everything BUT some Flash-based amCharts (www.amcharts.com) dynamically loaded with amCharts PHP, using swfObject. The file, loaded seperately, works and loads the Flash charts. When being loaded in using .load() the file seems to be loaded, but scripts not executed and swfObject not enacted.
Now, i'm not sure what code to give you here, so ill just give some stuff around it...
So, in header of main parent is the code listening for the nav click to load the content (which is our amcharts):-
$('a.leftNav').click(function() {
page = $(this).attr('page');
metID = $(this).attr('metID');
if($("#mainRight").is(":visible")) { $('#mainRight').hide(200); }
switch(page) {
case 'metrics': $("#mainRight").load("content.php?load=mets", { metID: '5000227' }); break;
}
$('#mainRight').show(300);
});
So that works. Loads the correct page and works executing the PHP as proved by going to the page content.php?load=mets. The code on that page generated by the PHP is:-
<div class="amChart" id="chart_views_div">
Chart loading ...
That should be the exchanged swfObject code, which I know (i've outputted to a text file the PHP's code generated) is working.
What happens with .load() and loaded swfObject and JS generally?
You need to call a function to initialise the swfObject replacement when jquery .load() has completed as i believe that swfObject would usually initialise on window.load.
something like this:
$("#mainRight").load("content.php?load=mets", function() {
// initialise swfObject here e.g.
swfobject.embedSWF("myContent.swf", "chart_views_div", "300", "120", "9.0.0");
});
Am Charts still uses swfobject 1.5
Swfobject 2.x offers much more IMHO amcharts should update to use the new version.
2.x has createswf which should be used in this case
If you look at the source of this page http://kingston.talking-newspapers.co.uk/ you will see a large amount of inline javascript near the top.
I don't really want all this extra stuff floating around in my page source, I'd much rather get it off into a script tag, and then I can minify it and all sorts.
If I call it as a php file, this SHOULD work in theory, I just end the js file extension with php instead, and in the header I put the following:
header("Content-type:application/x-javascript");
but... a lot of the php variables used to generate the playlist within the javascript are setup at the beginning of the main index.php file, and in calling this php-generated js playlist file like this, it seems to evaluate it entirely separately, so it's full of errors.
The only way round it I can think of is to have the page write a file, then immediately read it in. The other thing is, the playlist is likely to change often and dynamically, so I think I need to get minify to NOT cache it?
I made the solution by following this tutorial, which redirects the generate inline script to a file, then immediately reads that file in.
http://my.opera.com/zomg/blog/2007/10/03/how-to-easily-redirect-php-output-to-a-file
So now my page looks like:
<?php
require("./filewriter.php");
$obfw = new OB_FileWriter('jplay_gen_playlist.js');
$obfw->start();
require($includesdir . "jplayerscript.php");
$obfw->end();
?>
<script type="text/javascript" src="jplay_gen_playlist.js"></script>
et voila! All nicely external, can be minified, cached etc.
You can do it in two ways. First would be set up the variable inline and then include the script:
<script type="text/javascript">
var myPlayList = [
{
name: "Introduction and guidance on usage",
mp3:"http://www.talking-newspapers.co.uk/find/soundfiles/TnHomePageIntro.mp3",
ogg:"http://www.talking-newspapers.co.uk/find/soundfiles/kingstonkt9.ogg"
}
...
</script>
<script type="text/javascript" src="myinclude.js"></script>
The other would be to have your included .js file a simple library of functions which you include at the top of the page and then call from some inline javascript:
<script type="text/javascript" src="myinclude.js"></script>
....
<script type="text/javascript">
$(function() {
var myPlayList = [ ... ];
startPlaylist(myPlayList);
});
</script>
I would personally choose the second method. You shouldn't need to generate any of the script dynamically (as far as I can see, it can all be hard-coded except for the playlist, right?) Any other things you need to pass to the script could still be passed in by your startPlaylist() method call anyway.