Malange is supposed to be a template language that is not just a generic template language, but can act like a front-end template language, e.g. Svelte.
The goal of Malange is:
May be integrated with Pubian.
Malange, just like Svelte, supports the injection of variables from the script to the HTML.
[/script:::
name = "Davy Jones"
/]
<p>Hello there, my name is {name}</p>
You use the curly braces to inject variables.
Injecting return value of a function is also supported.
[/script:::
def return_something():
return "Davy Jones"
/]
<!-- You must call the function. -->
<p>Hello there, my name is {return_something()}</p>
Note that Malange can provide the event
object (for event management),
you need to provide a dollar sign before injection for event
to be delivered.
<p>Hello there, my name is ${return_something(event)}
To support reactivity (e.g. Svelte reactivity), you use the dollar sign. Any changes in the variable will be updated automatically. To use it, you must append the sign to the beginning of the variable during declaration only.
[/script:::
$data = "1 + 2 + 3"
increment = 3
def add_number():
data += f"{increment+1}"
/]
<p>{data}</p>
<button on:click={add_number}>Add number</button>
So if the button is clicked, the paragraph element will automatically update. Simple, right?
And yes, this also applies to function.
A way to represent a HTML element is through binding.
[/script:::
something variable_name.x
/]
<element bind:{variable_name}></element>
Malange provides 'browser' library that can be imported, this allows you an access to the Document interface.
[/script:::
# Root represents the entire element of the file.
from browser import root
def do_something():
...
root.on_load(do_something())
/]
Block is another feature of Malange that allows you to render the HTML elements in a 'more flexible' way.
[/script:::
from my_custom_library import get_score
exam_result = get_score(class='xi-a', exam='mid-semester-exam',
lesson='social-studies', retry=false)
]
[/for (name, score) in exam_result
<p>{name}, your score is {score}.</p>
/]
The block started with the [/
, folllowed by the block type, then the attribute
(e.g. (name, score) in exam_result
) plus ':::', followed by the HTML content, then after that
it ends with /]
sign. This particular block will loop over exam_result
to inject
name
and score
to a paragraph element, then it will render each paragraph element.
If exam_result
length is 10 items, there will be ten <p>
elements.
Note that it is not possible to declare a block within a block.
There are several types of block:
SCRIPT BLOCK:
Script block is used to inject Python code. All contents inside the script block will be treated as Python code.
[/script:::
contents (if src argument is provided, the content will be ignored)
/]
Several attributes exist:
[/script backend=(plot, data):::
...
/]
The backend attribute allows you to access exported modules from the backend with ease.
And if you want to seperate the script, you can do this:
[/script src=/path/to/the/script.py backend=(plot, data):::/]
FOR BLOCK:
This is useful for rendering multiple items whose data is sourced from a list or a dictionary.
[/for i in a_list:::
<p>{i}</p> # variable i is only available within this block.
/]
SWITCH BLOCK:
Switch block is useful for conditional rendering.
[/switch i:::
[/case 0:::
a
/]
[/case 1:::
b
/]
[/case::: <!--For "else" condition.-->
c
/]
/]
IF BLOCK:
Similar to switch block.
[/if i < j:::
a
[/elif i == j:::
b
/]
[/else:::
c
/]
/]
Accessing Python libraries in the backend is difficult due to
some libraries being able to work only on the backend (e.g. sys
).
And as such, the only available library is the needed library for
frontend. For the rest, it has to be done by sending the data to
the backend.
Let's say you want to render a matplotlib into a HTML canvas element.
The normal process will goes like this:
In Malange, it is much simpler.
import matplotlib.pyplot as plt
from malange.api.backend import Manager
manager = Manager(name='python_file')
def create_plot(data):
# Create a plot.
plt.figure()
plt.plot(data)
plt.title("A Plot")
# Save the plot to a variable.
buf = io.BytesIO()
plt.savefig(buf, format='png')
buf.seek(0)
plt.close()
# Encode and return.
return base64.b64encode(buf.getvalue()).decode('utf-8')
# Use the function to create the image data.
data = [1, 2, 3, 4, 5]
image_data = create_plot(data)
# Export the image data.
manager.export_module(image_data)
Then on Malange:
[/script:::
include python_file
from malange import on_load
from browser import Image
base64Image = python_file.image; // The Base64 string
img = Image();
img.src = `data:image/png;base64,${base64Image}`;
img.on_load(render_canvas);
def render_canvas():
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
/]
<canvas bind:{canvas}></canvas>
Malange will be divided into several components: