Skip to content

Vue.js Code Breakdown for Rock RMS Messages Page

Click here to view the Messages Page written in Vue.js

Goal/Intent

The purpose of this code is to create an interactive and dynamic "Messages" page within a Rock RMS site.
Users can view the latest messages, browse a series archive, and filter messages by topic, speaker, or keyword.
The interface leverages Vue.js for interactivity and integrates with Rock RMS's backend and Lava templating.

Zooming out Overall Functionality

The page provides:

  • Dynamic Filtering: Messages can be filtered by topic, speaker, or keyword.
  • Series Navigation: Users can view details of a selected series.
  • Latest Message Display: Highlights the most recent message by default.

Gotchas

  1. Missing Functions:

    • Functions like insertParam and removeParam are not defined in the snippet but are likely crucial for updating the URL dynamically.
    • Search in Rock RMS's custom scripts or page settings for these functions.
  2. Rock RMS Integration:

    • Check for Lava templates or page-specific scripts under the Block Properties in Rock RMS.
    • Ensure the Vue instance doesn't conflict with other Rock RMS scripts.
  3. Vue Delimiters:

    • Rock RMS uses Lava syntax (), which conflicts with Vue's default delimiters. Custom delimiters ({*,*}) are a workaround, but ensure all Vue bindings use this format consistently.
  4. Dynamic Data Source:

    • The initialLoadMessages function seems to fetch data dynamically. If the source is an API or a Lava block, ensure it aligns with Rock RMS's structure.

Mental Model: Visualization of Codebase

Vue Codebase

Where to start? The HTML Template

The Vue.js template is encapsulated in a div with the ID app and uses Vue.js directives to dynamically render elements.

Vue Directives: An Overview and Syntax

Directives

Special attributes prefixed with v- that reactively update the DOM.
Examples: v-html, v-bind, v-if.
Syntax:

html
<p v-if="seen">Now you see me</p>

v-if conditionally renders elements based on the truthiness of seen.

Arguments

Extend directive functionality with a colon (e.g., v-bind:href). Examples:

html

<a v-bind:href="url">Link</a> <!-- or shorthand: <a :href="url"> -->
<a v-on:click="doSomething">Click</a> <!-- or shorthand: <a @click="doSomething">#

Dynamic Arguments

Wrap arguments in [] for dynamic evaluation. Examples:

html
 
<a v-bind:[attributeName]="url">Link</a> <!-- Evaluates attributeName dynamically -->
<a @[eventName]="doSomething">Event</a> <!-- Binds dynamic event names -->

Constraints:

Dynamic arguments must evaluate to a string or null.
Complex expressions (e.g., :['foo' + bar]) may trigger warnings.

Modifiers

Postfixes (e.g., .prevent) customize directive behavior.
Example:

html
 
<form @submit.prevent="onSubmit">...</form> <!-- Prevents default form submission -->

Core Principles

Flexibility: Simplify binding with shorthand and dynamic expressions.
Detail: Pay attention to syntax constraints (e.g., lowercase keys in DOM templates).
Customization: Leverage modifiers for tailored functionality.

Mental Model: Visualization of Syntax

Vue Directives

html
<a v-bind:[dynamicAttr].modifier="expression"></a>

Takeaway: Directives bring interactivity and reactivity to Vue's declarative structure, balancing simplicity and power.

Key Vue.js Sections in the Codebase:

  1. Latest Message Section (v-if="showLatestMessage")
    Displays the most recent message by default.

  2. Series Archive Section (v-if="!showSelectedSeries")
    Shows a list of message series when no specific series is selected.

  3. Filtered Messages by Topic Section (v-if="showMessagesFilteredByTopic")
    Displays messages filtered by the selected topic.

  4. Filtered Messages by Keyword Section (v-if="showMessagesFilteredByKeyword")
    Displays messages filtered based on keywords.

  5. Selected Series Section (v-if="showSelectedSeries")
    Shows details and messages for the currently selected series.

Components and Libraries:

  • Multiselect: Uses the vue-multiselect library for dropdowns to select topics, speakers, and keywords.
    Here are the key aspects of how it's implemented:
    The multiselect component is used for the "Other Messages" dropdown:
xml
<multiselect v-model="selectedTopic" 
             :options="topicOptions.slice(7,topicOptions.length)" 
             :multiple="false" 
             :searchable="false" 
             :show-labels="false" 
             :close-on-select="true" 
             placeholder="Other Messages" 
             label="topic" 
             track-by="topic" 
             @input="setSelectedTopic()">
</multiselect>

JavaScript Code

The JavaScript defines a Vue.js instance using new Vue.
This instance manages the state, user interactions, and rendering logic for the "Messages" page.

Key Features:

  1. Custom Delimiter: Uses {*,*} as the delimiter for mustache interpolation.

In Vue.js, mustache syntax (`{{ ... }}`) is typically used to interpolate variables into the template. However, if you need to use a custom delimiter for interpolation, Vue allows you to change the default delimiter to any string you choose. In this case, you are considering `{*,*}` as the custom delimiter.<br/>

By default, Vue.js uses `{{` and `}}` to denote dynamic content, but if you have a situation where `{*` and `*}` don't conflict with your content (such as in situations where you are working with template engines that use similar syntax), you can change Vue's delimiter to prevent conflicts.<br/>
  1. Component Registration: Includes components like Multiselect.

  2. Element Control: Links to the DOM using el: '#app'.


Vue Component Properties and Methods

  • data:
    Holds the reactive state of the component.

  • props:
    Allows the parent component to pass data to the child component.

  • methods:
    Contains methods that define actions or behavior, often triggered by events.

  • computed:
    Defines computed properties that are derived from reactive state.

  • watch:
    Observes changes to specific data or computed properties and triggers actions in response.

  • name:
    A name for the component (used for debugging and recursive components).

    All the Vue components w their Methods and syntax

    javascript
    export default {
    name: 'MyComponent',
    props: {
      initialMessage: String
    },
    data() {
      return {
        count: 0,
        message: this.initialMessage
      };
    },
    computed: {
      doubledCount() {
        return this.count * 2;
      },
      reversedMessage() {
        return this.message.split('').reverse().join('');
      }
    },
    methods: {
      increment() {
        this.count += 1;
      },
      greet() {
        alert('Hello!');
      }
    },
    watch: {
      count(newCount) {
        console.log(`Count changed to: ${newCount}`);
      },
      message(newMessage) {
        console.log(`Message changed to: ${newMessage}`);
      }
    },
    created() {
      console.log('Component created, initial message:', this.message);
    },
    mounted() {
      console.log('Component mounted');
    }

Data Properties in Codebase for State management:

The data function initializes the state of the application with these key properties:

  • Arrays for Data:

    • seriesArray: All series objects available for display.
    • messageArray: All message objects.
    • messagesFilteredBySeries: Messages filtered by the selected series.
    • messagesFilteredByTopic: Messages filtered by the selected topic.
    • messagesFilteredByKeyword: Messages filtered by the selected keywords.
  • State Selections:

    • selectedSeries: The currently selected series.
    • selectedTopic: The currently selected topic.
    • selectedKeywords: Keywords for filtering messages.
  • Options for Dropdowns:

    • speakerOptions: List of speakers.
    • topicOptions: List of topics.
  • Visibility Flags:

    • showLatestMessage: Toggles display of the latest message.
    • showSelectedSeries: Toggles display of a selected series.
    • showMessagesFilteredByTopic and showMessagesFilteredByKeyword: Toggles filtered views.

Vue's Lifecycle Hooks

created

The created created hook is triggered right after the Vue component instance is created but before it is mounted to the DOM (i.e., before the user sees it). It’s an ideal place for setting up and modifying the component's internal state based on initial conditions, ensuring the component is ready when it becomes viewable.

  • Sets the browser state to initial.
  • Adds a listener for popstate to handle browser navigation.
  • Calls initialLoadMessages to fetch and set initial data.

Summary

Basically, the create hook works for when, created but before mounted or "viewable to the user", so that I can make tweaks to the UI according to "state" or what the user has already performed and expects to see.

mounted

Called after the component is mounted to the DOM.

updated

Called after the component’s reactive data changes and the DOM is updated.

destroyed / beforeDestroy / unmounted

Called before the component is destroyed or unmounted from the DOM.


Key Vue LifeCycle Hooks Implimented in Our Codebase:

  1. initialLoadMessages Function

The initialLoadMessages function fetches and sets the initial data (e.g., series, messages, and selected topic).

Lifecycle Hook: created
The created hook is ideal for initializing the component’s state, such as calling initialLoadMessages to fetch data right after the component instance is created.

javascript
created() {
  this.initialLoadMessages(); // Load initial data
},
methods: {
  initialLoadMessages() {
    this.series = fetchSeries(); // Fetch series data
    this.messages = fetchMessages(); // Fetch messages data
    this.setSelectedTopic('Traditional'); // Set the initial topic
  }
}
  1. setSelectedTopic Function Updates the view when a topic is selected:
    • Filters the series and messages arrays.
    • Sets the latest message.
    • Updates the URL parameters using the insertParam function.

Lifecycle Hook: Updated
The updated hook is triggered after the component’s reactive data changes and the DOM is updated.

javascript
updated() {
  console.log('Data has been updated after selecting a topic.');
},
methods: {
  setSelectedTopic(topic) {
    this.selectedTopic = topic;
    this.filteredSeries = this.series.filter(series => series.topic === topic);
    this.filteredMessages = this.messages.filter(message => message.topic === topic);
    this.latestMessage = this.filteredMessages[0]; // Set the latest message
    this.insertParam('topic', topic); // Update URL params dynamically
  }
}

insertParam and removeParam Functions These functions manage URL query parameters dynamically.

  1. insertParam and removeParam Functions Likely manages URL query parameters dynamically (missing from the provided code).
    Example: Update the URL when a filter is applied or removed.

Lifecycle Hook: mounted The mounted hook is called after the component is mounted to the DOM, making it the ideal place to sync URL parameters.

javascript

mounted() {
  this.insertParam('topic', this.selectedTopic); // Sync URL params after mounting
},
methods: {
  insertParam(key, value) {
    const url = new URL(window.location);
    url.searchParams.set(key, value);
    window.history.pushState({}, '', url); // Update the browser URL
  },
  removeParam(key) {
    const url = new URL(window.location);
    url.searchParams.delete(key);
    window.history.pushState({}, '', url); // Update the browser URL
  }
}
  1. Handling Browser Navigation Functions Handle browser navigation (back/forward buttons) by listening for popstate.
javascript
created() {
  window.addEventListener('popstate', this.handlePopState);
},
beforeDestroy() {
  window.removeEventListener('popstate', this.handlePopState);
},
methods: {
  handlePopState(event) {
    const urlParams = new URLSearchParams(window.location.search);
    const topic = urlParams.get('topic');
    if (topic) {
      this.setSelectedTopic(topic); // Sync topic from URL
    }
  }
}
  1. Component Cleanup Functions

Use beforeDestroy (or unmounted for Vue 3) to clean up resources like listeners.

javascript

beforeDestroy() {
  window.removeEventListener('popstate', this.handlePopState); // Cleanup
},

Full Reference to Lifecyle hooks

javascript
export default {
  data() {
    return {
      series: [],
      messages: [],
      selectedTopic: 'Traditional',
      filteredSeries: [],
      filteredMessages: [],
      latestMessage: null
    };
  },
  created() {
    this.initialLoadMessages(); // Load initial data
  },
  mounted() {
    this.insertParam('topic', this.selectedTopic); // Sync URL params
  },
  updated() {
    console.log('Data has been updated after selecting a topic.');
  },
  beforeDestroy() {
    window.removeEventListener('popstate', this.handlePopState); // Cleanup
  },
  methods: {
    initialLoadMessages() {
      this.series = fetchSeries(); // Fetch series data
      this.messages = fetchMessages(); // Fetch messages data
      this.setSelectedTopic(this.selectedTopic); // Set the initial topic
    },
    setSelectedTopic(topic) {
      this.selectedTopic = topic;
      this.filteredSeries = this.series.filter(series => series.topic === topic);
      this.filteredMessages = this.messages.filter(message => message.topic === topic);
      this.latestMessage = this.filteredMessages[0]; // Set the latest message
      this.insertParam('topic', topic); // Update URL params dynamically
    },
    insertParam(key, value) {
      const url = new URL(window.location);
      url.searchParams.set(key, value);
      window.history.pushState({}, '', url); // Update the browser URL
    },
    removeParam(key) {
      const url = new URL(window.location);
      url.searchParams.delete(key);
      window.history.pushState({}, '', url); // Update the browser URL
    },
    handlePopState(event) {
      const urlParams = new URLSearchParams(window.location.search);
      const topic = urlParams.get('topic');
      if (topic) {
        this.setSelectedTopic(topic); // Sync topic from URL
      }
    }
  }
};

Where to learn more about Vue.js


Actionable Next Steps

  1. Locate Missing Functions: Search for insertParam and removeParam in Rock RMS's scripts or theme files.

Explore and learn. Released under the MIT License.