← Teacher guide

Components and props

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.

Apps with Vue · Building apps · about 55 minutes · Open the student lesson

Classroom resources

Load these into Waypoints Prepare: choose Load URL and paste the copied address, or download the file and import it. In Prepare, the worksheet's Teacher edition shows the answers; the student copy hides them.

Students will be able to

Timing

Predict and Run 10 min · Investigate 15 min · Modify 10 min · Make 20 min

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

What you will seeA question to askThe fix
Cards show words like p.name.Is there a colon before the prop?:name="p.name" passes the value.
Nothing shows, and the console warns about failing to resolve a component.Is app.component before app.mount, with the same name as the tag?Register it before mount; the tag and the name must match, dash included.
A prop is blank on every card.Is it in the props list?props: ["name", "kind"]

Answers to the checks

  1. Predict: player-card is not an HTML tag. What does the page show?
    Three boxes, each with its own name. Vue turns each player-card tag into the component's template, with that tag's name prop.
  2. Which part does what?: Match each part with what it does.
    app.component("player-card", { … }) → A. makes a new tag, player-card; props: ["name", "wins"] → D. the data each copy is given; template: `…` → E. the HTML each copy shows; :wins="p.wins" → C. gives this copy the value of p.wins; v-for="p in players" → B. one player-card for each player
    The parent app holds the data and hands each card its part through props.
  3. Where does the data come from?: Inside the component, where do name and wins come from?
    From the tag that uses it, through props
    The card does not know about the list. Each copy is given its own name and wins by the tag that makes it.
  4. Spot the bug: Each card says p.wins wins instead of a number. There is no error. Click the line that is wrong.
    line 2
    wins="p.wins" passes the words p.wins. :wins="p.wins", with the colon, passes the value.
  5. Where this leads: 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?
    The count of what is left
    The count is worked out from the list. Next: the final challenge, a to-do app that uses everything in this course.

Solutions

One possible solution for each task. Other correct programs are fine.

Modify › A medal

<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>
        <p v-if="wins >= 5">🏅 Top player</p>
      </div>`
  });
  app.mount("#app");
</script>

Modify › A new player

<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 },
        { name: "Kai", wins: 3 }
      ]);
      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>

pet cards

<div id="app">
  <h1>Pets at our school</h1>
  <pet-card v-for="p in pets" :name="p.name" :kind="p.kind"></pet-card>
</div>

<script src="vue.js"></script>
<script>
  const { createApp, ref } = Vue;
  const app = createApp({
    setup() {
      const pets = ref([
        { name: "Biscuit", kind: "guinea pig" },
        { name: "Nemo", kind: "goldfish" },
        { name: "Pepper", kind: "rabbit" }
      ]);
      return { pets };
    }
  });
  app.component("pet-card", {
    props: ["name", "kind"],
    template: `
      <div style="border: 2px dashed orange; padding: 8px; margin: 8px 0;">
        <h2>{{ name }}</h2>
        <p>A {{ kind }}</p>
      </div>`
  });
  app.mount("#app");
</script>

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.

Exit ticket

What is the difference between wins="p.wins" and :wins="p.wins"?

Without the colon the card is given the words p.wins; with the colon it is given the value of p.wins.

What each step is for

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