Best method to use external templates with knockout - templates

I have built a html page with some internal templates. See url jsfiddle:
http://jsfiddle.net/hoven002/jQTDH/
What is the best method to make the templates external and how?
Regards,
Kenneth

The best method, in my opinion, is to use this plugin: https://github.com/ifandelse/Knockout.js-External-Template-Engine.
It enables a new template engine that will pull templates from external files. It has some configuration options as well to determine where the templates are pulled from.

I've written a function that loads templates, without any dependency but jQuery. You must mark each <script> tags you want to be dynamically loaded with the attribute data-template-src and put there the path of your HTML file. Following is the code and an example.
Warning: since it uses AJAX to load templates, it needs an HTTP server (it won't work locally with file:// protocol)
/main.js
// Loads all the templates defined with the tag data-template-src.
// Returns a promise that fulfills when all the loads are done.
function loadTemplates() {
var templateLoads = [];
$('[data-template-src]').each(function () {
var templateElement = $(this);
var templateUrl = templateElement.attr('data-template-src');
// Load the template into the element and push the promise to do that into the templateLoads array
templateLoads.push(
$.get(templateUrl)
.done(function (data) {
templateElement.html(data);
})
);
});
return $.when.apply($, templateLoads);
}
// When all templates are loaded, apply bindings
loadTemplates().done(function () {
ko.applyBindings({}, document.getElementById("htmlDoc"));
});
/index.html
<html id="htmlDoc">
<body>
<div data-bind="template: { name: 'exampleTemplate' }"></div>
<script type="text/html" id="exampleTemplate" data-template-src="/assets/exampleTemplate.html"></script>
<script src="/jquery.js"></script>
<script src="/knockout.js"></script>
<script src="/main.js"></script>
</body>
</html>
/assets/exampleTemplate.html
<h1>Hello world</h1>
<div data-bind="template: { name: 'exampleSubTemplate' }"></div>
/assets/exampleSubTemplate.html
<h2>How do you do?</h2>

Related

How to use Livewire component inside of javascript?

Looking at docs https://laravel-livewire.com/docs/2.x/events#from-js
working with Livewire component inside of javascript
<script>
Livewire.emit('postAdded')
</script>
I do not see what Livewire is ? Declared var ? How can I get it.
I need from JS code get value of some var and run component method.
How can I do it?
Modified Block # 2:
Defining in template of my component :
<div class="admin_page_container" id="facilities_admin_page_container">
...
In method of my alpinejs component I do
function adminFacilitiesComponent() {
return {
getSubmitLabel: function (component) {
const doc = document.getElementById("facilities_admin_page_container");
console.log('doc::')
console.log(doc)
var updateMode = window.livewire.find(doc.getAttribute("wire:updateMode"))
// But I got error : index.js:31 Uncaught (in promise) TypeError: Cannot read property '$wire' of undefined
// I see content of doc in browser's console : https://prnt.sc/1sdu4f1
console.log('updateMode::')
console.log(updateMode)
In my component I have defined :
namespace App\Http\Livewire\Admin;
...
class Facilities extends Component
{
...
public $updateMode = 'browse';
I just try to get value of $updateMode in JS getSubmitLabel function...
Thanks!
To answer your question where the livewire global object is comming from: you injected livewire scripts in the body of your layout:
<html>
<head>
...
#livewireStyles
</head>
<body>
...
#livewireScripts
</body>
</html>
https://laravel-livewire.com/docs/2.x/installation
The livewire global object is available via window.Livewire. The method you are looking for is probably
Livewire.emitTo(componentName, eventName, ...params)
Make sure that the livewire object is actually available when you are calling this method.
For all available methods see https://laravel-livewire.com/docs/2.x/reference
To get a component in javascript try:
const doc = document.getElementById("myComponent");
window.livewire.find(doc.getAttribute("wire:id"))
Livewire recommends that you use AlpineJS for most of your JavaScript needs, but it does support using tags directly inside your component's view.
<!DOCTYPE html>
<html>
<head>
#livewireStyles
</head>
<body>
<p>This is My Component Call </p>
#livewire('blog-component')
#livewireScripts
<script>
Livewire.emit('postAdded') //also write Javascript Hear
document.addEventListener('livewire:load', function () {
// Your JS here.
})
</script>
Reference
https://laravel-livewire.com/docs/2.x/inline-scripts
https://laravel-livewire.com/docs/2.x/reference

JSRender: Render another template from converter or custom tag

I am getting HTML content from a backend service request that might include something (non-JSRender) like this:
<div>{{comp:someComponentId}}</div>
Or we can receive it as:
<div>{{comp someComponentId}}</div>
I want to leverage this syntax to use a converter or a tag in JSRender to effectively convert this to:
<div>{{include tmpl="someComponentId"/}}</div>
Whether the tag/converter does the rendering, or if it can iteratively create the include to have JSRender evaluate it. How do I achieve this?
Does this do what you want?
<script id="myTmpl" type="text/x-jsrender">
{{comp someid/}}
</script>
<script id="id1" type="text/x-jsrender">
what {{:foo}}
</script>
<div id="page"></div>
<script>
$.views.tags("comp", {
render: function(id) {
return $("#"+id).render(this.tagCtx.view.data);
}
});
var myTmpl = $.templates("#myTmpl"),
data = { someid: "id1", foo: "FOO" },
html = myTmpl(data);
$("#page").html(html);
</script>

Can't get to add JS to front via $this->load->view

I'm developing a module for OpenCart 3.x and I'm trying to insert some JavaScript code into website's front via $this->load->view() method but cannot get it to work, as the JS code doesn't appear in the DOM.
Here's an excerpt of my code:
/catalog/controller/extension/module/mymodule.php
class ControllerExtensionModuleMyModule extends Controller {
public function index() {
$this->load->language('extension/module/mymodule');
$this->load->model('checkout/order');
$this->load->model('setting/setting');
$this->load->model('design/layout');
$this->load->model('catalog/category');
$this->load->model('catalog/manufacturer');
$this->load->model('catalog/product');
$this->load->model('catalog/information');
$data['js_output'] = "Some JS output";
return $this->load->view('extension/module/mymodule', $data);
}
}
catalog/view/theme/default/template/extension/module/mymodule.twig
<script type="text/javascript">
console.log('This is working!");
</script>
Am I missing something?
Add an external javascript file via controller like this:
$this->document->addScript('catalog/view/javascript/my-external-script.js');
But if it's a block of javascript code, you don't need to edit controller file, just add it to your view file (twig or tpl):
<script type="text/javascript">
console.log("This is working!");
</script>
Finally you may need to clear caches (vqmod, ocmod, twig etc ...).
EDIT
If you want to prepare your javascript code in the controller, follow this:
controller
$data['js_output'] =
'<script type="text/javascript">
console.log("This is working!");
</script>';
Twig
{{ js_output }}
I have finally fixed the issue. It was a problem with the install() method not correctly inserting and loading the design/layout ($this->model_design_layout->getLayouts()).
If your module needs to insert a layout into the layout_module table, make sure you have the correct layout_id, code(which must be your module name), position (i.e: 'content_bottom') and sort_order(which often times is > 90).

Play Framework 1.2.5 - adding to <script> tag

In my play framework app, I have a main.html, which at the very bottom includes this javascript tag:
<script>
$(function() {
// add js here
#{scripts /}
});
</script>
Now I would like to be able to add arbitrary javascript from other templates, which is then collected and added to the function body, so for example in view1.html, I'd like to add the function doCharts() to be called on jQuery's DOMReady:
#{append: 'scripts'}
doCharts();
#{/append}
This should then produce this output:
<script>
$(function() {
// add js here
doCharts();
});
</script>
Is this possible?
Yes this is possible
In your main file, after your script tag put
#{get 'moreScripts' /}
and in your other html files use
#{set 'moreScripts'}
<script type="text/javascript">
// put your script here
</script>
#{/set}
if you want to put another script in the same page you can put a get into the set like this
#{set 'moreScripts'}
#{get 'moreScripts' /}
<script type="text/javascript">
// put your script here
</script>
#{/set}

Using an external Template in KnockoutJS

is it possible to use an external Template in KnockoutJS like this?
<script type="text/html" id="a_template" src="templates/a_template.html">
</script>
I've tried this solution but didn't get it working.
You can use jquery to dynamically load html into a script element, and then execute knockout based on that.
<script type="text/html" id="template_holder"></script>
<script type="text/javascript">
$('#template_holder').load('templates/a_template.html', function() {
alert('Load was performed.');
//knockout binding goes here
});</script>
Your knockout binding must be done in the callback function though, otherwise there's a chance that you'll be trying to bind before the page has loaded
UPDATE Here's an example I've coded on jsfiddle to demonstrate dynamic loading: http://jsfiddle.net/soniiic/2HxPp/
You could also look at:
https://github.com/ifandelse/Knockout.js-External-Template-Engine
You can also use this Template bootstrapper for KO
Bootstrapper
https://github.com/AndersMalmgren/Knockout.Bootstrap
MVC WebAPI Demo
https://github.com/AndersMalmgren/Knockout.Bootstrap.Demo
It uses a Convention over configuration approuch using this lib
https://github.com/AndersMalmgren/Knockout.BindingConventions
Meaning it will automatically understand that MyViewModel should be matched to MyView
Its also prepared to work nicely in a SPA
Disclaimer: I'm the author behind the 3 libs mentioned above
Here's a little function building off of soniiic's answer:
function loadExternalKnockoutTemplates(callback) {
var sel = 'script[src][type="text/html"]:not([loaded])';
$toload = $(sel);
function oncomplete() {
this.attr('loaded', true);
var $not_loaded = $(sel);
if(!$not_loaded.length) {
callback();
}
}
_.each($toload, function(elem) {
var $elem = $(elem);
$elem.load($elem.attr('src'), _.bind(oncomplete, $elem));
});
}
This'll automatically load all knockout templates on your document, provided their src is set and their type is "text/html". Pass in a callback to be notified when all templates loaded. No idea what happens if any of them fails.
Example usage:
<head>
<script type="text/html" src="kot/template1.html" id="template1"></script>
</head>
<body>
<script>
$(function() {
loadExternalKnockoutTemplates(function() {
// Put your ready code here, like
ko.applyBindings();
});
});
function loadExternalKnockoutTemplates(callback) {
var sel = 'script[src][type="text/html"]:not([loaded])';
$toload = $(sel);
function oncomplete() {
this.attr('loaded', true);
var $not_loaded = $(sel);
if(!$not_loaded.length) {
callback();
}
}
_.each($toload, function(elem) {
var $elem = $(elem);
$elem.load($elem.attr('src'), _.bind(oncomplete, $elem));
});
}
</script>
</body>