on off switch
View Source
    Using the `@render` attribute here will re-render this element each time `render()` is called 
    Using `@render.eval` allows us to use JavaScript expressions in the template
    <button @render.eval @click="this.toggle">{this.isOn ? "ON" : "OFF"}</button>

<script type="module">
    import { registerControllers, Controller } from '/static/js/binder/binder.js';

    class OnOffSwitch extends Controller {
        // The `init` method is called when the controller is first created
        // Use this to handle your setup logic
        init() {
            this.isOn = true;
        // This is the click handler for the button
        // We manually trigger a render here to update the button text
        toggle() {
            this.isOn = !this.isOn;

click counter

Clicks: {this.data.clicks}

View Source
    <p @render>Clicks: {this.data.clicks}</p>
    <button @click="this.count()">Click me!</button>

<script type="module">
    import { registerControllers, Controller } from '/static/js/binder/binder.js';

    class ClickCounter extends Controller {
        init() {
            this.data.clicks = 0;

        count() {


currency picker

Chosen currency: {this.data.currency}

View Source
<!-- HTML -->
<currency-picker :currency="GBP">
    <select @bind.render="this.data.currency">
        <option disabled selected>Please select a currency</option>
        <option value="AUD">Australian Dollars</option>
        <option selected value="GBP">British Pounds</option>
        <option value="EUR">Euros</option>
        <option value="USD">US Dollars</option>

    <h4 @render>Chosen currency: {this.data.currency}</h4>

<!-- JavaScript -->
<script type="module">
    // This example uses the core Controller component
    import { registerControllers, Controller } from '/static/js/binder/binder.js';

    class CurrencyPicker extends Controller {
        init() {
            this.data.currency = this.args.currency;

magic 8 ball


View Source
    <button @render @click="this.render">{this.prompt}</button>
    <p @render.eval>{this.randomAnswer()}</p>

<script type="module">
    import { registerControllers, Controller } from '/static/js/binder/binder.js';

    class Magic8Ball extends Controller {
        init() {
            this.prompt = 'Ask the magic 8 ball';

            this.answers = [
                "It is certain.",
                "It is decidedly so.",
                "Without a doubt.",
                "Yes definitely.",
                "You may rely on",
                "As I see it, yes.",
                "Most likely.",
                "Outlook good.",

                "Signs point to yes.",
                "Reply hazy, try again.",
                "Ask again later.",
                "Better not tell you now.",
                "Cannot predict now.",
                "Concentrate and ask again.",

                "Don't count on it.",
                "My reply is no.",
                "My sources say no.",
                "Outlook not so good.",
                "Very doubtful.",

        randomAnswer() {
            return this.answers[Math.floor(Math.random() * this.answers.length)]


Core Controllers

dynamic frame

The line below is another page loaded within a div, click here to load the page directly!

View Source
<!-- HTML -->

    <p>The line below is another page loaded within a div, click <a href='/current_date'>here</a> to load the page directly!</p>

    <div id="mount-here"></div>

<button id="refresh-dynamic-frame">Force Refresh</button>

<!-- JavaScript -->
<script type="module">
    import { registerControllers } from '/static/js/binder/binder.js';
    import { DynamicFrame } from '/static/js/binder/core/dynamic_frame.js';


    document.querySelector("#refresh-dynamic-frame").addEventListener("click", () => {