ATV By Example
A is for Action
The most basic thing ATV can do is attach a listener (action) to an element in the DOM. In this case, we wait for a button click and then change the button text with an incrementing count.
The callback recieves the element of the button as its first argument.
Please note, this example shows a global counter. See "Nesting" section for how to isolate your UI state.

HTML

<div data-atv-controller="simple">
  <button data-atv-simple-action="click">
    Count 0
  </button>
</div>    
      

JavaScript

// app/javascripts/controllers/simple_atv.js
function activate() {
  let counter = 0;

  return {
    click: function(actor) {
      counter += 1;
      actor.innerText = `Count ${counter}`;
    }
  };
}

export { activate };
      
T is for Target
An ATV action can result in a change to a target element within the scope of the controller. Here is the classic example to show a button affecting a target: It lets you type a greeting, and when you press the button, the “hello” controller responds by placing your input into a greeting div on the page.
The receiving `div` is the target.

HTML

<div data-atv-controller="hello">
  <input data-atv-hello-target="name" type="text">

  <button data-atv-hello-action="click->greet">
    Greet
  </button>

  <span data-atv-hello-target="output">
  </span>
</div>      
      

JavaScript

// app/javascripts/controllers/hello_atv.js
function activate(targets) {
  return {
    greet: function() {
      targets.output.textContent = 
        `Hello, ${targets.name.value}!`;
    }
  };
}

export { activate };
      
V is for Value
Sometimes you might want to provide parameters or data to the controller at rendering time. In this case, you can pass in values (a JSON encoded hash) and use them inside the action callbacks as follows:

HTML

  <div data-atv-controller="config" 
       data-atv-config-values='{"answer":"My favorite color is blue!"}'>
    <button data-atv-config-action="click->reveal">
      Reveal the mystery answer
    </button>
    <span data-atv-config-target="output">
    </span>
  </div>
      
      

JavaScript

// config_atv.js
function activate(targets, values) {
  return {
    reveal: function() {
      targets.output.textContent = values.answer;
    }
  };
}

export { activate };
      
Nesting
ATVs can be nested! However, thanks to ES6 modules being global, you must store any state in a closure. If a function is returned instead of a regular object of callbacks, the provided function must return the same structure as the non-nesting (global) version above.

HTML

<div data-atv-controller="nesting">
  <button data-atv-nesting-action="click">
    Count 0
  </button>
  <div data-atv-controller="nesting">
    <button data-atv-nesting-action="click">
      Count 0
    </button>
  </div>  
</div>    
      

JavaScript

// app/javascripts/controllers/nesting_atv.js
function activate() {
  return function() {
    let counter = 0;

    return {
      click: function(actor) {
        counter += 1;
        actor.innerText = `Count ${counter}`;
      }
    };
  };
}

export { activate };
      
Multiple Actions
Your DOM elements can have more than one action.

HTML

<div data-atv-controller="multiple">
  <button data-atv-multiple-actions='["click", "click->clack"]' style="background-color: green;">
    Count 0
  </button>
</div>    
      

JavaScript

// app/javascripts/controllers/multiple_atv.js
function activate() {
  let counter = 0;

  return {
    click: function(actor) {
      counter += 1;
      actor.innerText = `Count ${counter}`;
    },
    clack: function(actor) {
      actor.styles.backgroundColor = "bluegreen".replace(actor.styles.backgroundColor, "");
    }
  };
}

export { activate };
      
Multiple Targets
Your DOM elements can have more than one target.

HTML

<div data-atv-controller="multiplier">
  <input type="number" data-atv-multiplier-target="factor"></input> * 
  <input type="number" data-atv-multiplier-target="factor"></input> = 
  <span data-atv-multiplier-target="product"></span>
  <button data-atv-multiplier-action="click->multiply">
    Calculate
  </button>
</div>    
      

JavaScript

// app/javascripts/controllers/multiplier_atv.js
function activate(targets) {
  return {
    multiply: function(actor) {
      targets.product.innerText = 
        targets.allFactor.reduce((accum, factor) => accum * factor.value, 1);
    }
  };
}
      
* =