Component JavaScript

Verze:

05. 01. 2026

Zodpovědná osoba:

Dominik Šlechta

Component JS MUST be in {block script} within core.latte. Include at page end using {include script from ...}. General JS belongs in resources/js/.

JavaScript in Components

  • Rule: When a component requires JavaScript, it MUST be placed in a {block script} within core.latte.

  • Reason: This keeps JavaScript logic encapsulated with its component, allows passing Latte variables to JavaScript, and enables explicit inclusion where needed on the page.

Example (core.latte with script block):

{**
 * Map Component
 *}
{default $latitude = 50.0755}
{default $longitude = 14.4378}
{default $zoom = 13}
{default $markers = []}
{default $uniqueId = 'default'}

{block core}
<div id="map-{$uniqueId}" class="map-component"></div>
{/block}

{block script}
<script>
(function() {
	const mapConfig = {
		container: 'map-{$uniqueId}',
		center: [{$latitude}, {$longitude}],
		zoom: {$zoom},
		markers: {$markers|json}
	};
	
	document.addEventListener('DOMContentLoaded', function() {
		initMap(mapConfig);
	});
})();
</script>
{/block}

Including Component Scripts

  • Rule: Script blocks SHOULD be included at the end of the page, after the HTML content.

Example (Page template):

{layout '@layout.latte'}

{block content}
	<h1>Contact</h1>
	
	{include core from 'partials/components/contact-map/core.latte',
		latitude => $company->lat,
		longitude => $company->lng,
		uniqueId => 'contact-map'
	}
{/block}

{block scripts}
	{include script from 'partials/components/contact-map/core.latte',
		latitude => $company->lat,
		longitude => $company->lng,
		uniqueId => 'contact-map'
	}
{/block}

Avoiding Duplicate Scripts

  • Rule: When a component may be included multiple times on a page, the script MUST handle this gracefully.

Example (Idempotent initialization):

{block script}
<script>
(function() {
	// Guard against multiple executions
	const instanceId = 'newsletter-{$uniqueId}';
	if (window['initialized_' + instanceId]) return;
	window['initialized_' + instanceId] = true;
	
	// Component initialization...
	const form = document.getElementById(instanceId);
	form.addEventListener('submit', handleSubmit);
})();
</script>
{/block}

Data Attributes Alternative

  • Rule: For components used multiple times with shared logic, data-* attributes MAY be used to pass data, with a single initialization script.

Example:

{block core}
<div class="slider" 
	data-autoplay="{$autoplay ? 'true' : 'false'}"
	data-interval="{$interval}">
	...
</div>
{/block}

{block script}
<script>
(function() {
	if (window.sliderInitialized) return;
	window.sliderInitialized = true;
	
	document.querySelectorAll('.slider').forEach(function(el) {
		const config = {
			autoplay: el.dataset.autoplay === 'true',
			interval: parseInt(el.dataset.interval, 10)
		};
		new Slider(el, config);
	});
})();
</script>
{/block}

General JavaScript Location

  • Rule: Reusable JavaScript that is NOT component-specific MUST be placed in resources/js/ directory, not in Latte templates.

Summary:

  • Component JS MUST be in {block script} within core.latte.
  • Include scripts at page end using {include script from ...}.
  • Scripts MUST handle multiple instances gracefully.
  • General/reusable JS belongs in resources/js/, not in components.