Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Mastering Vue Component Architecture and Communication Patterns

Tech May 19 2

Interactive Exercise: Rendering and Filtering Student Scores

Start with a table that displays student scores. The first column shows the total score for each student.


// Data structure: each entry has name, math, chinese, english scores
const studentScores = [
  { name: 'Bob', math: 97, chinese: 89, english: 67 },
  // ... more students
];

// Compute total and sort descending by total
studentScores.forEach(s => s.total = s.math + s.chinese + s.english);
studentScores.sort((a, b) => b.total - a.total);


<table>
  <tr>
    <th>Rank</th>
    <th>Name</th>
    <th>Math</th>
    <th>Chinese</th>
    <th>English</th>
    <th>Total</th>
  </tr>
  <tr v-for="(student, index) in studentScores" :key="index">
    <td>{{ index + 1 }}</td>
    <td>{{ student.name }}</td>
    <td>{{ student.math }}</td>
    <td>{{ student.chinese }}</td>
    <td>{{ student.english }}</td>
    <td>{{ student.total }}</td>
  </tr>
</table>

To display only students who passed all subjects (score ≥ 60):


<tr v-for="(student, index) in studentScores" v-if="student.math >= 60 && student.chinese >= 60 && student.english >= 60">
  ...
</tr>

Add filtering controls:

  • Three buttons: Chinese, Math, English – clicking a button highlights it and sets the filter subject.
  • Two input fields for minimum and maximum scores. The table updates to show only rows matching all conditions.

<style>
  .active-filter {
    background-color: lightblue;
  }
</style>

<div id="app">
  <button @click="selectedSubject = 'chinese'" :class="{ 'active-filter': selectedSubject === 'chinese' }">Chinese</button>
  <button @click="selectedSubject = 'math'" :class="{ 'active-filter': selectedSubject === 'math' }">Math</button>
  <button @click="selectedSubject = 'english'" :class="{ 'active-filter': selectedSubject === 'english' }">English</button>

  <p>
    Score range:
    <input type="number" min="0" max="100" v-model.number="minScore">
    ~
    <input type="number" min="0" max="100" v-model.number="maxScore">
  </p>

  <table border="1" style="margin: auto">
    <tr>
      <th>Rank</th>
      <th v-for="(value, key) in studentScores[0]" v-if="key === 'name' || key === selectedSubject || !selectedSubject">{{ key }}</th>
    </tr>
    <tr v-for="(student, index) in filteredScores" :key="index">
      <td>{{ index + 1 }}</td>
      <td v-for="(value, key) in student" v-if="key === 'name' || key === selectedSubject || !selectedSubject">{{ value }}</td>
    </tr>
  </table>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    studentScores: [...], // original sorted data
    selectedSubject: '',
    minScore: '',
    maxScore: '',
  },
  computed: {
    filteredScores() {
      return this.studentScores.filter(s => {
        const subject = this.selectedSubject;
        const score = s[subject];
        if (!subject) return true; // no filter
        if (this.minScore !== '' && score < this.minScore) return false;
        if (this.maxScore !== '' && score > this.maxScore) return false;
        return true;
      });
    }
  }
});
</script>

Component Fundamentals

A Vue component is a reusable bundle of HTML, CSS, and JavaScript logic. There are three types:

  • Root component: created via new Vue().
  • Local component: defined as an object and registered inside a parent component.
  • Global component: registered with Vue.component(name, definition).

Every component has a template property that defines its DOM structure.


// Root component with explicit template
new Vue({
  el: '#app',
  data: { msg: 'Root component data' },
  template: '<div>{{ msg }}</div>'
});
// If no template is provided, the el's innerHTML becomes the template.

Child Components

The root component acts as the top parent; local and global components serve as children. Relationships are relative. Each child component has isolated data scope. Local components require registration before use, while global ones are always available (preload into memory). Prefer local components to save memory.


<div id="app">
  <div class="gallery">
    <local-card></local-card>
    <global-card></global-card>
  </div>
</div>

<script>
// Local component definition
const LocalCard = {
  template: `
    <div class="card" @click="handleClick">
      <img src="path/to/image.jpg" alt="Profile">
      <h2>Beauty</h2>
    </div>
  `,
  methods: {
    handleClick() { alert('Clicked!'); }
  }
};

// Global component
Vue.component('global-card', {
  template: `
    <div class="card">
      <img src="path/to/product.jpg" alt="Product">
      <h2>Milk</h2>
    </div>
  `
});

new Vue({
  el: '#app',
  components: {
    LocalCard // ES6 shorthand: 'local-card' from 'LocalCard'
  }
});
</script>

Component Data Isolasion

Analogous to class instantiation, each component instance gets its own data scope. The data property must be a function returning an object, so every reuse of the component creates a fresh data object.


const ClickCounter = {
  template: `
    <div class="counter-box" @click="increment">
      <img src="path/to/image.jpg" alt="Image">
      <p>Clicks: {{ count }}</p>
    </div>
  `,
  data() {
    return { count: 0 };
  },
  methods: {
    increment() { this.count++; }
  }
};

Parent-to-Child Communication (Props)

The child component defines expected props as an array of strings. The parent binds its data to these custom attributes using the v-bind directive.


<div id="app">
  <div class="gallery">
    <student-card v-for="person in people" :person-data="person" :key="person.id"></student-card>
  </div>
</div>

<script>
const people = [
  { id: 1, name: 'Alice', image: 'path/to/alice.jpg' },
  { id: 2, name: 'Bob', image: 'path/to/bob.jpg' }
];

const StudentCard = {
  props: ['personData'],
  template: `
    <div class="card">
      <img :src="personData.image" alt="personData.name">
      <h2>{{ personData.name }}</h2>
    </div>
  `
};

new Vue({
  el: '#app',
  data: { people },
  components: { StudentCard }
});
</script>

Child-to-Parent Communication (Events)

A child component can emit custom events to notify the parent. The parent listens to these events via the v-on directive and defines the handler. The child uses this.$emit('eventName', payload).


<div id="app">
  <h1>{{ title }}</h1>
  <h2>{{ subtitle }}</h2>
  <title-editor @title-changed="updateTitle" @subtitle-changed="updateSubtitle"></title-editor>
</div>

<script>
const TitleEditor = {
  template: `
    <div>
      Main title:
      <input type="text" v-model="newTitle" @input="emitTitle">
      <br>
      Subtitle:
      <input type="text" v-model="newSubtitle">
    </div>
  `,
  data() {
    return {
      newTitle: '',
      newSubtitle: ''
    };
  },
  methods: {
    emitTitle() {
      this.$emit('title-changed', this.newTitle);
    }
  },
  watch: {
    newSubtitle(val) {
      this.$emit('subtitle-changed', val);
    }
  }
};

new Vue({
  el: '#app',
  data: {
    title: 'Default Title',
    subtitle: 'Default Subtitle'
  },
  methods: {
    updateTitle(value) {
      this.title = value || 'Default Title';
    },
    updateSubtitle(value) {
      this.subtitle = value || 'Default Subtitle';
    }
  },
  components: { TitleEditor }
});
</script>

Remember: a component’s template must have exactly one root element. Events belong to the child, but the logic is provided by the parent via the handler.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.