← Apps with Vue

Components and props

Make a tag of your own once with app.component, use it many times, and pass each copy its own data with props and :name="value".

A feed of posts, a shop of products and a ladder of players are each one card used many times, with different data.

A component is a tag of your own. You write it once, then use it as often as you like:

app.component("player-card", {
  props: ["name", "wins"],
  template: `<div><h2>{{ name }}</h2><p>Wins: {{ wins }}</p></div>`
});

props lists the data each copy is given; the template (between back-ticks) is its HTML. Using it looks like any tag:

<player-card v-for="p in players" :name="p.name" :wins="p.wins"></player-card>

The colon in :name="p.name" means "the value of": without it, the component is given the words p.name. Component names have a dash, and their tags need a closing tag.

The app is now made in three steps: createApp(...), then app.component(...), then app.mount("#app").

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">
  <player-card name="Ada"></player-card>
  <player-card name="Sam"></player-card>
  <player-card name="Lin"></player-card>
</div>

<script src="vue.js"></script>
<script>
  const { createApp } = Vue;
  const app = createApp({});
  app.component("player-card", {
    props: ["name"],
    template: `<p style="border: 2px solid teal; padding: 8px;">Player: {{ name }}</p>`
  });
  app.mount("#app");
</script>

There is no tag called player-card in HTML. Vue has been told about one.

player-card is not an HTML tag. What does the page show?

RunRun it and compare the result with your prediction.

Run

Press Run. Change Sam's wins to 9 in the list and run again.

page.html
<div id="app">
  <h1>Chess ladder</h1>
  <player-card v-for="p in players" :name="p.name" :wins="p.wins"></player-card>
</div>

<script src="vue.js"></script>
<script>
  const { createApp, ref } = Vue;
  const app = createApp({
    setup() {
      const players = ref([
        { name: "Ada", wins: 5 },
        { name: "Sam", wins: 4 },
        { name: "Lin", wins: 2 }
      ]);
      return { players };
    }
  });
  app.component("player-card", {
    props: ["name", "wins"],
    template: `
      <div style="border: 2px solid teal; padding: 8px; margin: 8px 0;">
        <h2>{{ name }}</h2>
        <p>Wins: {{ wins }}</p>
      </div>`
  });
  app.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 ladder</h1>
  <player-card v-for="p in players" :name="p.name" :wins="p.wins"></player-card>
</div>

<script src="vue.js"></script>
<script>
  const { createApp, ref } = Vue;
  const app = createApp({
    setup() {
      const players = ref([
        { name: "Ada", wins: 5 },
        { name: "Sam", wins: 4 },
        { name: "Lin", wins: 2 }
      ]);
      return { players };
    }
  });
  app.component("player-card", {
    props: ["name", "wins"],
    template: `
      <div style="border: 2px solid teal; padding: 8px; margin: 8px 0;">
        <h2>{{ name }}</h2>
        <p>Wins: {{ wins }}</p>
      </div>`
  });
  app.mount("#app");
</script>

Component, props and template.

Follow it in.

Words instead of numbers.

Match each part with what it does.

app.component("player-card", { … })
props: ["name", "wins"]
template: `…`
:wins="p.wins"
v-for="p in players"

Inside the component, where do name and wins come from?

Each card says p.wins wins instead of a number. There is no error. Click the line that is wrong.

ModifyChange the code on purpose and check the result.

Modify

Every card, one change.

Need a hint?

The template is the HTML for every card. A v-if in it can use the wins prop.

Another hint

<p v-if="wins >= 5">🏅 Top player</p> inside the template's div.

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

Data, not HTML.

Need a hint?

Each card comes from one item in the players list.

Another hint

Add { name: "Kai", wins: 3 } to the list.

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.

Show 🏅 Top player on the cards of players with 5 or more wins.

    This ticks when your page has all of the above.

    Add Kai, with 3 wins, to the ladder.

      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.

      Pet cards

      Make a pet-card component with two props (name and kind) and a template of your own design, then show every pet with v-for.

      Try it yourselfYour edits are saved in this browser.

      Make a pet-card component with two props and show every pet with v-for.

        This ticks when your page has all of the above.

        Info

        Where this leads

        Next: the final challenge, a to-do app.

        A to-do app has a box, an Add button, a list of tasks to tick off and a count of what is left. Which part would be a computed value?

        For teachers

        Students predict what a made-up tag shows, make cards from a list with a component and props, find a prop passed without its colon, add a medal to every card and a player to the data, and make pet cards. 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. app.component("player-card", { props: [...], template: ... }) registers a new tag; every <player-card> on the page becomes a copy of the template. Props are its inputs: the tag that uses it hands them in. :wins="p.wins" (with a colon, short for v-bind:) passes the value of p.wins; wins="p.wins" passes the words, which is the planted bug.

        Two rules come from writing the page straight into HTML, without a build step: component names need a dash, and their tags need a separate closing tag (<player-card></player-card>, not <player-card />). The template is a JavaScript string between back-ticks, so it can span lines.

        Props flow one way: a card should not change its own props (Vue warns if it tries). Changing data happens in the parent; sending messages up from a component (emit) is left out of this course.

        Running the lesson

        1. Predict: ask what the browser normally does with a tag it does not know (it ignores it).
        2. Run: change one player's data and ask why only one card changed.
        3. Investigate: draw the data flowing from the list, through v-for, into each card's props.
        4. Modify: ask whether the medal goes in the data or the template, and why.
        5. Make: ask students to name their two props before writing the template.

        The hard part (on purpose)

        Students try to read the players list inside the component. Ask "what has this card been given?" and point to props.

        When students get stuck

        Stretch and support

        Stretch: Give player-card a computed of its own (setup(props) { … }) that shows a rating from the wins. Make the cards sit in a row with Flexbox.

        Support: Give the pet-card component written out; students add the v-for tag only.

        Where it leads

        Next: the final challenge, a to-do app built in milestones, with an optional lesson that builds the same app in three frameworks. Class idea: open a shopping site and count the cards made from one component.

        Sources

        What each step is for

        Now you can
        Next lesson6. Final challenge: a to-do appBuild a to-do app in three milestones: add tasks from a box, tick them off with a count of what is left, then make each task a component and clear the done ones.