DDD Value Object front-end practice

Beginners on

In a front end page, when we need to provide users with a limited number of changes to modify a state, a lot of people do it and it looks pretty clean.

You can also use checkboxes or other presentational components. We’re not talking about the interaction level here, just programming.

<select
  v-model="formModel.monitorStatus"
  placeholder="Application Type"
>
  <option :key="1" :value="1">new</option>
  <option :key="2" :value="2">open</option>
  <option :key="3" :value="3">disable</option>
</select>
Copy the code

Now I have a place to show my status, what do I do?

<div>
  <span v-if="formModel.monitorStatus === 1">new</span>
  <span v-else-if="formModel.monitorStatus === 2">open</span>
  <span v-else-if="formModel.monitorStatus === 3">disable</span>
  <span v-else>There is no</span>
</div>
Copy the code

What if you want to give the user a default value? Set a value, it looks pretty simple!

formModel.monitorStatus = 1;
Copy the code

What if there are multiple pages that need this functionality? Copy, of course! One word is cool!

<! -- Page1 -->.<! -- Page2 -->.<! -- Page3 -->.Copy the code

What if the default values for each page might be different? Set different defaults, stupid Diao!

// Page1
formModel.monitorStatus = 1;

// Page2
formModel.monitorStatus = 2;

// Page3
formModel.monitorStatus = 3;
Copy the code

What happens when the meaning of the value changes? What? Depend, XXXXX!

A little bit into

First, a state has only a few optional values, and each value has a corresponding description, so we can define it as the following enumerated type.

export const MONITOR_STATUS_ENUM = [[1."New"], [2."Open"], [3."Stop"]].Copy the code

We then use the enumeration type in the view and modify it when the menu item needs to change.

<select
  v-model="formModel.monitorStatus"
  placeholder="Application Type"
>
  <option
    v-for="item in MONITOR_STATUS_ENUM"
    :key="item[0]"
    :value="item[0]"
  >{{ item[1] }}</option>
</select>
Copy the code

Now I have a place to show my status, what do I do? Just add a dictionary and look it up.

export const MONITOR_STATUS_ENUM = [[0."New"], [1."Open"], [2."Closed"]].export const MONITOR_DESC_MAP = new Map(MONITOR_STATUS_ENUM);
Copy the code

You might think of it that way, but in our case, I don’t know if there’s anything wrong with it, but I’m open to discussion.

const MONITOR_DESC_MAP = Object.fromEntries(MONITOR_STATUS_ENUM);
Copy the code

Along with the registration of a filter for easy use.

<div>{{ formModel.monitorStatus | toStatusDesc }}</div>

<! You can't do that, you know.filter toStatusDesc(status) { const result = MONITOR_DESC_MAP.get(status); return result ? Result: "none "; }Copy the code

What if you want to set a default value for the user?

// If I do not open the definition file of the enumerated type, I will not know what the meaning of 1 is
formModel.monitorStatus = 1;
Copy the code

Include a semantic dictionary, which greatly improves readability.

export const MONITOR_STATUS_ENUM = [[1."New"], [2."Open"], [3."Stop"]].export const MONITOR_DESC_MAP = new Map(MONITOR_STATUS_ENUM);
export const MONITOR_STATUS_DICT = {
  "create": 1."start": 2."stop": 3
};

// This is readability
formModel.monitorStatus = MONITOR_STATUS_DICT.create;
Copy the code

What if multiple pages have different default states? That, say no more.

// Page1
formModel.monitorStatus = MONITOR_STATUS_MAP.create;

// Page2
formModel.monitorStatus = MONITOR_STATUS_MAP.start;

// Page3
formModel.monitorStatus = MONITOR_STATUS_MAP.stop;
Copy the code

What happens when the meaning of the value changes? Instead of enumerating type and meaning mappings.

export const MONITOR_STATUS_ENUM = [[0."New"], [1."Open"], [2."Closed"]].export const MONITOR_STATUS_DICT = Object.fromEntries(MONITOR_STATUS_ENUM);
export const MONITOR_STATUS_MAP = {
  "create": 0."start": 1."stop": 2
};
Copy the code

It feels really ok, but every time you set the default value, or change the state through JS without view, you need to rely on Map mapping, which is a bit tedious, can you improve it? Wipe, sure enough give grain to want meat to eat…

And this method for the first time to write trouble trouble, the back use is still so troublesome, sure not someday I will cross the Map directly assign a value, heh heh ~_~. Damn, dig a hole for yourself and say…

Well, one more tip for you -> value object

/ / the old things, needless to say const MONITOR_STATUS_ENUM = [[1, "new"], [2, "open"], [3, "disable"]]. const MONITOR_DESC_MAP = new Map(MONITOR_STATUS_ENUM); const MONITOR_STATUS_DICT = { "create": 1, "start": 2, "stop": 3 }; // Export default Class MonitorStatus {// Expose static enum = MONITOR_STATUS_ENUM; #value = null; Set value(status) {if (! This. IsMeaningful (status)) {throw new Error(' Not found status: ${status}, optional status: ${object. values(MONITOR_STATUS_DICT)} '); } this.#value = status; } get value() { return this.#value; } constructor(status) { this.value = status; } // Check status validity isMeaningful(status) {return!! MONITOR_DESC_MAP.get(status); } // Get the status description toDesc() {return monitor_desc_map.get (this.status); } // updateByTag(tag) {const status = MONITOR_STATUS_DICT[tag]; If (status == null) {throw new Error(' No status: ${tag}, ${object. keys(MONITOR_STATUS_DICT)} '); } this.#value = status; }}Copy the code

When you use it now, you just import the class, and it feels more cohesive than before.

import MonitorStatus from "xxx";

// get the enumeration value
const MONITOR_STATUS_ENUM = MonitorStatus.enum;

// Create a value object
formModel.monitorStatus = new MonitorStatus(db.status);
Copy the code

The value of the view binding corresponds to the value property when the option is provided.

<select
  v-model="formModel.monitorStatus.value"
  placeholder="Application Type"
>
  <option
    v-for="item in MONITOR_STATUS_ENUM"
    :key="item[0]"
    :value="item[0]"
  >{{ item[1] }}</option>
</select>
Copy the code

Somewhere you need to show the state, just change the method.

<div>{{ formModel.monitorStatus.toDesc() }}</div>
Copy the code

Set the default value and also change the method.

formModel.monitorStatus.updateByTag("start");
Copy the code

Scripts are required to modify values, assign values, or tune methods as appropriate

// value assignment: An assignment of a state value obtained from a channel without worrying about the value crossing the boundary
formModel.monitorStatus.value = 2;

// Call method: explicitly update to a certain state, more semantic
formModel.monitorStatus.updateByTag("stop");
Copy the code

Later, when the meaning of the value changes, you can also modify the enumeration type and meaning mapping.

const MONITOR_STATUS_ENUM = [[0."New"], [1."Open"], [2."Closed"]].const MONITOR_DESC_MAP = new Map(MONITOR_STATUS_ENUM);
const MONITOR_STATUS_DICT = {
  "create": 0."start": 1."stop": 2
};

export default class MonitorStatus {... }Copy the code

I depend, seem a bit complicated, seem a bit diao again, want to use? With you ~ ~ ~

What if I want to submit this data to the back end? Pass an object and don’t break it. It’s not gonna break you, it’s gonna cut you, but it’s gonna have to be fixed.

First of all, if you use this value object, there’s no guarantee that there will be more than one class in the future, so to distinguish between them, let’s define a parent class, and it’s best to define an interface if you can use it, so that you can enforce the naming of key methods like isMeaningful, toDesc, and updateByTag.

class BaseValueObject {

  // If the subclass is not overridden, it is ignored when json.stringify serializes the value object
  get value() {
    return undefined; }}// Subclass inheritance is fine
class MonitorStatus extends BaseValueObject {}
class XXX extends BaseValueObject {}
Copy the code

Now, most of the time, we’re transferring data in JSON format. Here we’ll use json.stringify and its customization capabilities. For convenience, you can wrap this conversion logic in a request preprocessor interceptor.

const fromModel = { a: 1.b: new MonitorStatus(1)};JSON.stringify(fromModel, (key, val) => {
  // The value object takes its value property
  if (val instanceof BaseValueObject) return val.value;
  return val;
});
Copy the code

This value object is not designed exactly according to standard principles, such as immutability, and more importantly, to borrow its ideas.