ProtoViewLogic: relateViewLogics()

与えられた ProtoViewLogic を子とする親子関係を結びます。

構文

protoViewLogic.relateViewLogics(protoViewLogicMap) => object

引数

  • protoViewLogicMap: object | Promise<object>

    子として追加する ProtoViewLogic とその名前を対応づけるオブジェクト、または履行時にそれを与える Promise です。

    各プロパティは ProtoViewLogic インスタンスか ProtoViewLogic インスタンスの配列です。 それぞれのプロパティ名は対象の ViewLogic インスタンスに追加するプロパティの名前として使われます。

返値: object | Promise<object>

対象の ProtoViewLogic から親子関係が解消された ProtoViewLogic をプロパティに持つオブジェクト、または履行時にそれを与える Promise です。

オブジェクトのプロパティ名は、親子関係が解消された ProtoViewLogic が以前に持っていた名前(name 参照)です。 引数 protoViewLogicMap のプロパティと同名の ProtoViewLogic が既に親子関係を結んでいる場合、既存の ProtoViewLogic の親子関係が解消(disrelateViewLogics() 参照)されます。

note

返値のオブジェクトは Object を継承していません(null-prototype object です)。

例外

  • TypeError
    • 引数 protoViewLogicMapProtoViewLogic のインスタンスだった場合。
    • 引数 protoViewLogicMap のプロパティに ProtoViewLogic でも ProtoViewLogic[] でもない値が設定されていた場合。
  • ReferenceError
    • 引数 protoViewLogicMap のプロパティに対象の ProtoViewLogic 自身またはその祖先が含まれる場合。循環参照を作ってしまうためエラーとなります。
    • 引数 protoViewLogicMap のプロパティとして、同一の ProtoViewLogic が異なる複数のプロパティ名で指定されている場合。
  • SyntaxError
    • 引数 protoViewLogicMap のプロパティ名がいずれかの条件に当てはまる場合:
      • 識別子として使用できない文字を含む
      • 空文字列
      • 既に relateElements() で関連付けられた要素に使用されている

//  SP
const { ProtoViewLogic } = await Alier.import("/alier_sys/ProtoViewLogic.js");
//  Web
//  import { ProtoViewLogic } from "/path/to/alier/src/ProtoViewLogic.js";

console.group("Example of ProtoViewLogic.relateViewLogics()");

const Watcher = class extends ProtoViewLogic {
    async messageHandler(msg) {
        console.groupCollapsed(`${msg.id}+${msg.code}`);

        console.log("%cmsg.id                      :", "font-weight: bold; font-family: monospace;", msg.id);
        console.log("%cmsg.code                    :", "font-weight: bold; font-family: monospace;", msg.code);
        console.log("%cmsg.origin                  :", "font-weight: bold; font-family: monospace;", msg.origin);
        console.log("%cmsg.target                  :", "font-weight: bold; font-family: monospace;", msg.target);
        console.log("%cmsg.param                   :", "font-weight: bold; font-family: monospace;", msg.param);
        console.log("%cmsg.param?.state            :", "font-weight: bold; font-family: monospace;", msg.param?.state);
        console.log("%cmsg.param?.newName          :", "font-weight: bold; font-family: monospace;", msg.param?.newName);
        console.log("%cmsg.param?.newParent        :", "font-weight: bold; font-family: monospace;", msg.param?.newParent);
        console.log("%cmsg.param?.oldName          :", "font-weight: bold; font-family: monospace;", msg.param?.oldName);
        console.log("%cmsg.param?.oldParent        :", "font-weight: bold; font-family: monospace;", msg.param?.oldParent);
        console.log("%cmsg.param?.addedProperties  :", "font-weight: bold; font-family: monospace;", msg.param?.addedProperties);
        console.log("%cmsg.param?.removedProperties:", "font-weight: bold; font-family: monospace;", msg.param?.removedProperties);

        console.groupEnd();
    }
};

const vl  = new Watcher;
const foo = new Watcher;
const bar = [
    new Watcher,
    new Watcher
];

console.groupCollapsed("step-1");
//  (1)  foo, bar を vl の子にする;
//       vl に同名の子はいないので空のオブジェクトが返る
console.log("eval: vl.relateViewLogics({ foo, bar })");
console.log("==> ", vl.relateViewLogics({ foo, bar }));
// ==>  {}

console.groupEnd();


console.groupCollapsed("step-2");
//  (1)  foo, bar を vl の子にする;
//       vl に同名の子はいないので空のオブジェクトが返る
foo.relateViewLogics({ baz: vl3 });
// ==> "connection state = "\disconnected\"; old name = \"bar\"; new name =    null;"
// ==> "connection state =    "\connected\"; old name = \"bar\"; new name = \"baz\";"
console.groupEnd();

bar.relateViewLogics({ foo: vl2 });
// ==> なにも行われない(vl2.parent === vl1 であり vl2.name === "foo" のため)

console.groupEnd();

解説

引数 protoViewLogicMap として渡されたオブジェクトのプロパティを対象の ProtoViewLogic へコピーし、各プロパティが持つ ProtoViewLogic と親子関係を結びます。

子の ProtoViewLogicpost() で送られたメッセージが消費されなかった場合は親の ProtoViewLogic にメッセージが送られます。また、親の ProtoViewLogicbroadcast() で送られたメッセージは子の ProtoViewLogic に送られます。

relateViewLogics() の呼び出し先の ProtoViewLogic は、引数の各プロパティが持つ ProtoViewLogic の親となります。

引数として与えられた ProtoViewLogicparent プロパティに呼び出し先の ProtoViewLogic への参照が設定されます。また、子となる ProtoViewLogicname プロパティには、引数のオブジェクトに設定されたプロパティ名が設定されます。

const vl  = new ProtoViewLogic();
const foo = new ProtoViewLogic();
const bar = new ProtoViewLogic();

vl.relateViewLogics({ foo, bar });

console.log(foo.parent === vl);
// => true
console.log(bar.parent === vl):
// => true
console.log(foo.name);
// => "foo"
console.log(bar.name);
// => "bar"

既に同名のプロパティが存在し、そのプロパティが relateViewLogics() によって関連付けられた ProtoViewLogic だった場合、既存の ProtoViewLogic の親子関係は解消されます。子だった ProtoViewLogicparent プロパティおよび name プロパティの値は null に変更されます。

const vl  = new ProtoViewLogic();
const foo = new ProtoViewLogic();
const bar = new ProtoViewLogic();

vl.relateViewLogics({ foo });
console.log(foo.parent === vl);
// => true
console.log(foo.name);
// => "foo"

// foo を vl_bar に付け替え
vl.relateViewLogics({ foo: bar });
console.log(foo.parent === vl);
// => false
console.log(foo.name);
// => null

メッセージ

メッセージの概要についてはメッセージパッシングを、他のメッセージについては ViewLogic の状態変更を通知するメッセージを参照してください

vl$connectionChanged

relateViewLogics() によって子の ProtoViewLogicparent プロパティの値が変更されると、メッセージ { id: "vl$connectionChanged" } が変更を受ける ProtoViewLogic に対して post() されます。

メッセージ内容は以下の通りです:

  • id: "vl$connectionChanged"

  • code: null

  • param: { state, oldParent, oldName, newParent, newName }

    • state: "connected" | "disconnected" --- 対象の ProtoViewLogic が他の ProtoViewLogic の子になったら "connected" が、子でなくなったら "disconnected" が設定されます。

      note

      すでに親を持っている ProtoViewLogicrelateViewLogics() に渡したとき、内部ではまず親子関係を解消してから新しく親子関係を結んでいます。なのでメッセージは "disconnected""connected" が順番に送出されます。

    • oldParent: ProtoViewLogic | null --- 直前まで設定されていた parent プロパティの値です。

    • oldName: string | null --- 直前まで設定されていた name プロパティの値です。

    • newParent: ProtoViewLogic --- 新たに設定された parent プロパティの値です。relateViewLogics() では常に呼び出し先の ProtoViewLogic になります。

    • newName: string --- 新たに設定された name プロパティの値です。

  • origin: ProtoViewLogic

  • target: ProtoViewLogic

const parent = new ProtoViewLogic;
const child = new class extends ProtoViewLogic {
    async messageHandler(msg) {
        if (msg.id === "vl$connectionChanged") {
            console.log(`new name: ${msg.param.newName}`);
        }
    }
};
parent.relateViewLogics({ foo: child });
// => 以下がログ出力されます:
//    new name: foo

メッセージ { id: "vl$connectionChanged" }disrelateViewLogics() でも送出されます。

vl$propertiesModified

この関数の呼び出し後、対象の ProtoViewLogic から自身に対してプロパティ id として vl$propertiesModified を値に持つメッセージが送られます。このメッセージが持つプロパティは以下の通りです:

  • id: "vl$propertiesModified"
  • code: null
  • param: { addedProperties, removedProperties }
    • addedProperties: ({ [property_name: string]: ProtoViewLogic | ProtoViewLogic[] })? --- 新たにプロパティに追加された ProtoViewLogic です。disrelateViewLogics() の呼び出しではこの値は常に null になります。
    • removedProperties: ({ [property_name: string]: ProtoViewLogic | ProtoViewLogic[] })? --- プロパティから取り除かれた ProtoViewLogic です。
  • origin: ProtoViewLogic
  • target: ProtoViewLogic
const parent = new class extends ProtoViewLogic {
    async messageHandler(msg) {
        if (msg.id === "vl$propertiesModified") {
            if (msg.param.addedProperties != null) {
                const addedKeys = Object.keys(msg.param.addedProperties)
                console.log(`added properties: ${JSON.stringify(addedKeys)}`);
            }
        }
    }
};
const child = new ProtoViewLogic;
parent.relateViewLogics({ bar: child });
// => 以下がログ出力されます:
//    added properties: ["bar"]

メッセージ { id: "vl$propertiedModified" }disrelateViewLogics でも送出されます。