← Apps with Vue

Events and inputs

Run a function of your own when something is clicked, join a box to data with v-model, and change a ref's .value in the script.

Every sign-up form, search box and Add button is an event and some data. Vue joins them up for you.

Two new pieces join the page to the data.

Events: @click="addPlayer" runs a function when the button is clicked. The function lives in setup and is returned like the data:

function addPlayer() {
  players.value.push(newName.value);
  newName.value = "";
}
return { players, newName, addPlayer };

Inputs: v-model="newName" joins a box to a ref, both ways: typing changes the data, and changing the data changes the box. No .value of the box and no "input" event, as in the DOM course.

The hard part: on the page you write newName; in the script you write newName.value. A ref is a box, and in the script you open it yourself.

PredictRead the code and say what it will do before you run anything. A wrong guess is fine: it shows you what to look for.

Predict

page.html
<div id="app">
  <input v-model="name">
  <p>Hello, {{ name }}!</p>
</div>

<script src="vue.js"></script>
<script>
  const { createApp, ref } = Vue;
  createApp({
    setup() {
      const name = ref("friend");
      return { name };
    }
  }).mount("#app");
</script>

There is no event and no .value here, only v-model.

You clear the box and type Kai. What does the paragraph show while you type?

RunRun it and compare the result with your prediction.

Run

Press Run, type a name and press Add. Then press Add with the box empty: nothing happens, because of the if.

page.html
<div id="app">
  <h1>Chess team</h1>
  <input v-model="newName" placeholder="A name">
  <button @click="addPlayer">Add</button>
  <p>Adding: {{ newName }}</p>
  <ul>
    <li v-for="name in players">{{ name }}</li>
  </ul>
</div>

<script src="vue.js"></script>
<script>
  const { createApp, ref } = Vue;
  createApp({
    setup() {
      const players = ref(["Ada", "Sam"]);
      const newName = ref("");
      function addPlayer() {
        if (newName.value !== "") {
          players.value.push(newName.value);
          newName.value = "";
        }
      }
      return { players, newName, addPlayer };
    }
  }).mount("#app");
</script>
Try it yourselfYour edits are saved in this browser.

InvestigateTrace how each line of code connects to what you see.

Investigate

page.html
<div id="app">
  <h1>Chess team</h1>
  <input v-model="newName" placeholder="A name">
  <button @click="addPlayer">Add</button>
  <p>Adding: {{ newName }}</p>
  <ul>
    <li v-for="name in players">{{ name }}</li>
  </ul>
</div>

<script src="vue.js"></script>
<script>
  const { createApp, ref } = Vue;
  createApp({
    setup() {
      const players = ref(["Ada", "Sam"]);
      const newName = ref("");
      function addPlayer() {
        if (newName.value !== "") {
          players.value.push(newName.value);
          newName.value = "";
        }
      }
      return { players, newName, addPlayer };
    }
  }).mount("#app");
</script>

Box, button and function.

The page and the script.

The score will not change.

Match each part with what it does.

v-model="newName"
@click="addPlayer"
players.value.push(newName.value);
newName.value = "";
return { players, newName, addPlayer };

Why does the function write newName.value, but the page writes newName?

Clicking Add a point shows a red error, and the score stays at 0. Click the line that is wrong.

ModifyChange the code on purpose and check the result.

Modify

A function of your own.

Need a hint?

A function in setup, returned, and a button that names it.

Another hint

function reset() { players.value = ["Ada", "Sam"]; newName.value = ""; } then add reset to the return, and <button @click="reset">Reset</button>.

Show the expected result
What the finished page looks like
What it should look like. Your words can be different.

A second box.

Need a hint?

A new box needs a new ref, returned, and v-model on the box.

Another hint

const club = ref("Chess");, club in the return, <input v-model="club">, and {{ club }} in the heading.

Show the expected result
What the finished page looks like
What it should look like. Your words can be different.
Try it yourselfYour edits are saved in this browser.

Add a Reset button that puts the list back to Ada and Sam.

    This ticks when your page has all of the above.

    Add a box for the club's name, and show it in the heading.

      This ticks when your page has all of the above.

      MakeBuild something new using what you have learned. Expect a few tries; that is normal.

      A badge maker

      Make a name badge: boxes for a name and a role joined with v-model, the badge shown with {{ }} in a bordered box, and a button that runs a function (make the name shout in capitals with toUpperCase(), or swap the two).

      Try it yourselfYour edits are saved in this browser.

      Make boxes with v-model, show them on a badge, and add a button that runs a function of your own.

        This ticks when your page has all of the above.

        Info

        Where this leads

        Next: values worked out from data, with computed.

        Your page should show how many players there are, and say Team full at 5. Where should the count come from?

        For teachers

        Students predict that v-model follows each letter, add names with a function, match the parts, find score++ on a ref, add a Reset function and a second box, and make a badge maker. About 55 minutes.

        Open the teacher guide: answers, solutions, slides and a worksheet.

        Students will be able to

        Before class

        Read the unit primer first. Two new pieces. @click="addPlayer" names a function to run on a click; the function is written in setup and returned with the data. v-model="newName" joins a box to a ref both ways: typing changes the ref, and setting the ref (to empty text, say) changes the box. Compare the DOM course, where students read box.value and listened for the "input" event themselves.

        The hard part of the whole course: a ref is a box. On the page Vue opens it for you ({{ score }}, @click="score++"); in the script you open it yourself (score.value++). Writing score++ in a function gives a red error, "Assignment to constant variable", because the ref was made with const. The playground's hint names .value for this case. Expect it all lesson; it is also the planted bug.

        Running the lesson

        1. Predict: ask what DOM-course students would have needed (an input event and box.value).
        2. Run: press Add with an empty box and ask which line stops it.
        3. Investigate: before the bug, ask students to underline every .value in the script and none on the page.
        4. Modify: a new function is three edits: write it, return it, name it in @click.
        5. Make: ask which ref each box changes, and what the button's function changes.

        The hard part (on purpose)

        Students mix up when to write .value. Ask "are you on the page or in the script?" every time, and let them answer before they type.

        When students get stuck

        Stretch and support

        Stretch: Add when Enter is pressed: @keyup.enter="addPlayer" on the box. Remove a player when their name is clicked (pass the name to a function).

        Support: Give the Reset function written out; students return it and add the button.

        Where it leads

        Next lesson: computed values, worked out from data and kept up to date. Class idea: find an Add button on a real site and say which data it changes.

        Sources

        What each step is for

        Now you can
        Next lesson4. Values worked out from data: computedWork a value out from other data with computed, so it is worked out again whenever that data changes, and see why a plain calculation goes stale.