Skip to main content

NoodlでのJavascript

このガイドで学べること

このガイドでは、NoodlでのJavascriptの使用方法について紹介します。 ほとんどの機能はNoodlのノードを使用して達成できますが、Javascriptを使いこなせる場合、直接Javascriptコードを追加する方が便利な場合があります。 Noodlは、FunctionノードとScriptノードを使用してノードとコードを簡単に組み合わせることができます。

概要

まず、シンプルな単一関数のJavascriptコードに優れたFunctionノードについて説明し、その後、Scriptノードを使用したより広範なJavascriptの例を示します。 これら二つのノードは、Javascriptを使用したビジネスロジックガイドで組み合わせて使用されています。

Functionノードの使用

NoodlでJavascriptを書く最も簡単な方法は、Functionノードを使用することです。 コードエディタを開いてコードを書くだけです!早速試してみましょう。

"Hello World"テンプレートを使用して新しいプロジェクトを作成します。テキストノードの前に二つのText Inputノードを追加します。 それらに"First String"と"Second String"のラベルを付けます。次に、Functionノードを追加します。この関数ノードに"Merge Strings"と名付けます。

以下のJavascriptを編集して、以下のコードを貼り付けます:

if (Inputs.String1 !== undefined && Inputs.String2 !== undefined) {
let length = Math.max(Inputs.String1.length, Inputs.String2.length);
let outputString = "";
for (let i = 0; i < length; i++) {
if (i < Inputs.String1.length) {
outputString = outputString + Inputs.String1.charAt(i);
}
if (i < Inputs.String2.length) {
outputString = outputString + Inputs.String2.charAt(i);
}
}
Outputs.MergedString = outputString;
}

Functionノードの入力と出力

この小さな関数は、String1String2と呼ばれる二つの文字列をマージします。 それらはInputs.String1Inputs.String2として宣言され、Functionノードの入力になります。 出力には、Outputs.MergedStringが定義されています。

Functionノードの実行時期

Functionノードを実行する方法は2つあります。

  1. Runシグナルが接続されていない場合、その入力が変更されたときにFunctionが実行されます。これが現在の例でのケースです。
  2. Runシグナルが接続されている場合、Functionはトリガーされたときにのみ実行されます。

ユーザーがボタンをクリックしたら文字列をマージするように変更しましょう。 したがって、Text Inputsの後にButtonを追加します。それにMergeというラベルを付けます。 そして、Clickを関数のRunに接続します。

出力にシグナルを送信する

Functionノードはシグナルも送信できます。これは、多くのシナリオで非常に便利です。たとえば、Functionが実行された後に何かをトリガーしたい場合などです。 例えば、マージした文字列を保存したい場合、Set Variableノードをトリガーすることができます。

コードにシグナルを追加してみましょう。マージが完了したら:

if (Inputs.String1 !== undefined && Inputs.String2 !== undefined) {
let length = Math.max(Inputs.String1.length, Inputs.String2.length);
let outputString = "";
for (let i = 0; i < length; i++) {
if (i < Inputs.String1.length) {
outputString = outputString + Inputs.String1.charAt(i);
}
if (i < Inputs.String2.length) {
outputString = outputString + Inputs.String2.charAt(i);
}
}
Outputs.MergedString = outputString;
Outputs.Done();
}

Scriptノードを使用したJavaScript

このガイドのこの部分では、Scriptノードの機能について説明します。 Scriptノードは、ノードと接続よりもコードで表現する方が簡単なロジックや計算を実装するのに最適な方法です。 また、DateMathなど、ブラウザーで利用可能なJavaScript APIにアクセスするのにも最適です。

Scriptノードは、Functionノードよりも少し強力です。Scriptノードには、複数のメソッドと関数があり、ノードが作成および破棄されたときに呼び出されるコールバックが明確に定義されています。 入力を処理するための

よりシンプルなJavaScriptの場合、通常、Functionノードの方がシンプルな選択肢です。

Scriptノードは、他のノードと同様に、入力と出力を他のノードに接続できます。 すべての入力と出力は、開発者がJavaScriptコードで使用できます。 Scriptノードでは、ブラウザ環境で通常使用可能なJavaScript関数を呼び出すことができます。

スクリプトソースファイル

JavaScriptコードをNoodlの組み込みエディタで直接編集することも、外部ファイルを使用して外部エディタで編集することもできます。 インラインエディタでコードスニペットを書くのは簡単ですが、大きなファイルや複数のファイル、モジュールを扱う場合は、外部ファイルオプションがより良い選択肢になるかもしれません。

外部ファイルは、Noodlがそれを見つけるためにプロジェクトフォルダ内に配置する必要があります。ファイルをプロジェクトフォルダにコピーするには、ファイルをNoodlウィンドウにドラッグします。

入力と出力

入力と出力を指定する方法はいくつかあります。一つの方法は、プロパティパネルを使用して明示的に追加することです。また、ソースコードでプログラム的に指定することもできます。入力はScript.Inputsオブジェクトで定義されます。各入力は、そのタイプも指定します。利用可能なタイプは以下の通りです:

  • number
  • string
  • boolean
  • color
  • object
  • array。これはNoodlの配列で、JavaScriptの配列ではありません。
  • reference。視覚ノードの_This_出力を通じてアクセス可能なNoodlノードへの参照。
  • cloudfile

出力も入力と同じように機能しますが、signalというタイプを使用できるもう一つのタイプがあります。signalタイプは、特定の値を出力するのではなく、出力でパルスをトリガーするために使用されます。以下は、二つの入力と一つの出力を持つScriptノードの例です。

入力シグナル

入力シグナルは、Script.Signalsオブジェクト内の関数にマップされます。シグナル関数は、同じ名前のシグナルがトリガーされたときに呼び出されます。ここでは、Generateシグナルの実装を示します。このコードをコピーしてScriptソースコードに追加できます。

Script.Signals.Generate = function () {
let randomNumber =
Math.random() * (Script.Inputs.RangeHigh - Script.Inputs.RangeLow) +
Script.Inputs.RangeLow;
Script.Outputs.RandomNumber = randomNumber;
};

入力、出力、シグナルをいくつかのノードに接続しましょう。

入力の読み取りと出力の設定

Script.Inputsオブジェクトを通じて直接入力を読み取ることができます。通常は、Script.Inputs.AnInputのようにします。

出力を設定する方法は2つあります。Script.Outputsオブジェクトの適切なプロパティを設定することで値を設定します:

Script.Outputs.RandomNumber = randomNumber;

Script.setOutputs関数を使用して、一度に多くの出力を設定します:

Script.setOutputs({
One: 1,
Two: 2,
});

これは、一度に送信したい複数の値

を含むオブジェクトがある場合に便利です。

最後に、出力でシグナルを送信する場合は、関数として出力を使用し、シグナルを送信したいときに呼び出す必要があります。

Script.Outputs.MySignalOutput();

さて、JavaScriptの例にもう少しコードを追加しましょう。Generateシグナルの代わりにStartStopシグナルを実装し、JavaScriptノードが継続的に新しい数値を生成するようにします。Lambda入力に影響されるランダムな時間後にトリガーされるタイマーをStartで開始します。Lambdaが高いほど、時間が短くなり、生成される数値のレートが高くなります。

タイマーがトリガーされると、ノードに提供された範囲に基づいてランダムな数値が生成されます。最後に、新しい数値が生成されたことを通知するシグナルが送信され、新しいタイムアウトでタイマーが再スタートします。 Stopシグナルがトリガーされると、タイマーは停止します。

ここに、ポアソン分布の時間間隔でランダムな数値を生成するコードがあります:

Script.Inputs = {
Lambda: "number",
RangeLow: "number",
RangeHigh: "number",
};

Script.Outputs = {
Trigger: "signal",
RandomNumber: "number",
};

var timer;

function generateRandNum(rangeLow, rangeHigh) {
return Math.random() * (rangeHigh - rangeLow) + rangeLow;
}

function calculateIntervalMs(lambda) {
let interval = -Math.log(1.0 - Math.random()) / lambda;
// 秒からミリ秒に変換
return interval * 1000;
}

Script.Signals.Start = function () {
console.log("Start");
let timeOutFunction = () => {
// ランダムな数値を生成
let randNum = generateRandNum(
Script.Inputs.RangeLow,
Script.Inputs.RangeHigh
);
// 出力"RandomNumber"に設定
Script.setOutputs({ RandomNumber: randNum });
// シグナル"Trigger"をトリガー
Script.Outputs.Trigger();
// 新しいインターバルでタイマーを再スタート
timer = setTimeout(
timeOutFunction,
calculateIntervalMs(Script.Inputs.Lambda)
);
};

// 最初のタイマーを開始
let interval = calculateIntervalMs(Script.Inputs.Lambda);

timer = setTimeout(timeOutFunction, interval);
};

Script.Signals.Stop = function () {
clearTimeout(timer);
timer = undefined;
};

入力が変更されたときにコードを実行する

特定のケースでは、ランダム数値生成器のLambda入力が変更されたときに、タイマーの間隔を即座に更新して、次のタイマーがタイムアウトするのを待たずに変更を有効にする必要があります。このようなケースを処理するには、Lambdaという名前の関数をScript.Settersオブジェクトに追加します。タイマーが停止しているときに値を変更しても開始されないように、追加の状態変数startedを追加します。

var started = false;
Script.Setters.Lambda = function (value) {
if (started === true) {
clearTimeout(timer);
startTimer();
}
};

最終的なコード

いくつかの小さなリファクタリングの後、最終的なコードは以下のようになります:

Script.Inputs = {
Lambda: "number",
RangeLow: "number",
RangeHigh: "number",
};

Script.Outputs = {
Trigger: "signal",
RandomNumber: "number",
};

var timer;
var started = false;

function generateRandNum(rangeLow, rangeHigh) {
return Math.random() * (rangeHigh - rangeLow) + rangeLow;
}

function calculateIntervalMs(lambda) {
let interval = -Math.log(1.0 - Math.random()) / lambda;
// 秒からミリ秒に変換
return interval * 1000;
}

function startTimer() {
let timeOutFunction = () => {
// ランダムな数値を生成
let randNum = generateRandNum(
Script.Inputs.RangeLow,
Script.Inputs.RangeHigh
);
// 出力"RandomNumber"に設定
Script.setOutputs({ RandomNumber: randNum });
// シグナル"Trigger"をトリガー
Script.Outputs.Trigger();
// 新しいインターバルでタイマーを再スタート
timer = setTimeout(
timeOutFunction,
calculateIntervalMs(Script.Inputs.Lambda)
);
};

// 最初のタイマーを開始
let interval = calculateIntervalMs(Script.Inputs.Lambda);

timer = setTimeout(timeOutFunction, interval);
}

Script.Signals = {
Start() {
started = true;
startTimer();
},
Stop() {
clearTimeout(timer);
started = false;
},
};

Script.Setters.Lambda = function (value) {
if (started === true) {
clearTimeout(timer);
startTimer();
}
};

Scriptノードの使用

入力と出力に接続することで、Scriptノードは他のノードと同様にNoodlで使用できます。例として、ランダムジェネレーターScriptノードを単純なUIと組み合わせて、入力を制御しています。ランダムジェネレーターの出力は、画面上の円を動かし、状

態変更をトリガーするために使用されています。また、Scriptノードをコピー&ペーストして2回使用しています。これはうまく機能しますが、インラインソースを使用している場合は、JavaScriptコードが複製されるため、一方のScriptノードのコードを変更しても他方には影響しないことに注意してください。再利用可能なScriptノードをNoodlコンポーネントでカプセル化するのはよくあるアイデアです。

デバッグ

どんなコーディングにも、遅かれ早かれコードに誤りが発生します。Noodlは、構文エラーと実行時エラーの両方をキャッチし、エラーを引き起こしたScriptノードをハイライトします。また、警告ポップアップでエラーを見つけることもできます。

上の画像に示されているように、コード内の構文エラーは、ノードの入力と出力が無効になる原因となる場合があります。構文エラーを修正すると、接続が復元されます。

ウェブデバッガーをビューアーウィンドウから起動して、デバッグアイコンをクリックすることでデバッグを開始できます。

Scriptノードが作成されたときや破棄されたときにコードを実行する

Scriptノードは、それが属するNoodlコンポーネントが作成されたときに作成されます。コンポーネントは、アプリが最初に起動されたとき、ナビゲーションが行われたとき、Repeaterノードがアイテムを作成したときに作成されます。Script.OnInit関数は、Scriptノードが作成されたときにNoodlによって自動的に呼び出されます。

コンポーネントは、ナビゲーションを行うときや、Repeaterノードが使用するリストからアイテムを削除するときに破棄されることがあります。これは、Script.OnDestroy関数を実行します。

以下は、body要素にイベントリスナーを設定し、ノードが破棄されたときにそれを削除してメモリリークを避ける例です。

function setPosition(e) {
Script.Outputs.PointerX = e.clientX;
Script.Outputs.PointerY = e.clientY;
}

Script.OnInit = function () {
document.body.addEventListener("mousemove", setPosition);
document.body.addEventListener("mousedown", setPosition);
};

Script.OnDestroy = function () {
document.body.removeEventListener("mousedown", setPosition);
document.body.removeEventListener("mousemove", setPosition);
};