Skip to content

Options: Changing Options

What are we learning here?

                                                    ┌──────────────┐
                                                    │   Change     │
                                                    │   Options    │
                                                    └───────┬──────┘
┌──────────────┐ ┌──────────────────────────────────────────┼──────┐
│ Loading      │ │ Component                                │      │
│ Component    │ │                                          │      │
│ ┌──────────┐ │ │              ┌──────────────────┐   ╔════▼════╗ │
│ │ options  ├─┼─┼─Transform───►│ Received Options ├──►║ Final   ║ │
│ └──────────┘ │ │ (optional)   └────────▲─────────┘   ║ Options ║ │
│              │ │                     Merge           ╚════╤════╝ │
└──────────────┘ │              ┌────────┴─────────┐   ┌────▼────┐ │
                 │              │ Static Defaults  │   │ ! Watch │ │
                 │              └──────────────────┘   └─────────┘ │
                 └─────────────────────────────────────────────────┘

  • Options can be changed via the public component property options
  • Options changes can be watched with watchEffect or watch

Now it's important to know that the options of a component are "reactive vue objects". This means that changes to these options can be observed and responded to using the watchEffect and watch methods of the vue package.

The only thing we need to change in our example is to include the two lines with the assignment of the values in a watchEffect function:

src/demo/apps/component-basics/content.js

import { Widget } from "@codecoupler/cc-ui";
import template from "./content.ejs.html";
import style from "./content.module.css";
import LoadingComponents from "../../components/loading-components";
import { watchEffect } from "vue";
export default class extends Widget {
  myHeader = "Component Basics";
  static defaults = {
    $map: "@codecoupler.walkthrough",
    "@codecoupler": {
      walkthrough: {
        myOption1: "Default 1",
        myOption2: "Default 2"
      }
    }
  };
  async start() {
    let tpl = template({
      myHeader: this.myHeader
    });
    this.element.replaceWith(tpl);
    this.element.classList.add(style.widget);
    let stage = this.registerStage(
      this.element.querySelector("[data-role=stage]")
    );
    await stage.load(LoadingComponents);
    watchEffect(() => {
      this.element.querySelector("[data-role=option1").innerHTML =
        this.#options.myOption1;
      this.element.querySelector("[data-role=option2").innerHTML =
        this.#options.myOption2;
    });
  }
}

Now we add code to change the value of this.options.myOption1 periodically in our application, where we have instatiated the widget. We use the public property options of the widget to change a single option.

The public property options can also be used to change multiple options at once. Only options that will be set there will be changed. All other options are not affected:

src/demo/apps/component-basics/index.js

import { Application } from "@codecoupler/cc-ui";
import Content from "./content.js";
export default class extends Application {
  static defaults = {
    "@codecoupler": {
      panel: {
        panelSize: "640 510",
        headerTitle: "Test Component Features"
      }
    }
  };
  async start() {
    let widget = await this.stage.load(Content, {
      myOption1: "Value 1"
    });
    let interval = setInterval(() => {
      if (!widget.isDestroyed) {
        widget.options = {
          myOption1: `Random Value ${Math.floor(Math.random() * 100)}`
        };
      } else {
        clearInterval(interval);
      }
    }, 2000);
  }
}

Now the value of myOption1 will change every 2 seconds.

Do not try to change deep options directly

Even if this in some cases would work (it do not work for the example above):

componentInstance.options.option1 = "NewValue1";

You should never change options this way. To update the options this pattern is always preferable:

componentInstance.options = {
  option1: "NewValue1",
  option2: "NewValue2",
}

The first approach can lead to strange behaviour and could not work at all:

  • If the component have a watch exactly for this option1 it will not work.
  • If the component have defined a $map in its defaults it will not work. You could of course use the correct namespace like componentInstance.options["@someNamespace"].subNamespace.option1 = "NewValue1" but then the problem from the first point still remains.