Node dev : getNodeParameter usage?

For 2/ my node can have several "previous-node"s which set their own value for a property which will remain empty indeed in the form. I don’t know it in advance. But for other WF, the user may set this value in the form.
So I must fetch this property value in the items values (yes obtained with this.getInputData())
But I still don’t know how to do because I do not kown where it is located in items[]
For example :

[
	{
		json: {
    			"day" : "monday",	
	    		"other" : "whatever1"
		}
	},
	{
		json: {
                   "other" : "whatever2",
                   "brand" : "philips",
		}
	}
]

and

[
	{
		json: {
    			"brand" : "toyota",	
              }
    }
]

What do you mean with?

my node can have several "previous-node"s

Does the node have multiple inputs? Because if it only has one it should not matter if there is one node before your custom one or multiple. What matters is what the node directly before the current one outputs and nothing else.

Sereval arrows can reach this destination node, with different outputs, but only one of them will be activated in the WF.
Even without this context, so only one node before, I must predict that its form parameters come from this node ouput.

Still so not understand what should be predicted. Even if multiple nodes would connect to the node and all are active there is nothing to perdict.

Let’s assume 3 nodes are connected to the node. It would then execute once with the data of the first node, once with the data of the second and once with the data of the third. The data of all the previous nodes would never end up in the execute function at the same time. It is only possible to use data from multiple nodes at the same time if there are different inputs.

Yes I agree jan, and yes only data from a unique previous node is considered, since only this previous node is activated during one execution of a WF.
But this is not my problem : imagine there’s only one previous node which outputs data.
My problem is how to read previous node output, once we getInputData().
Let’s My current node has “brandName” and “age” in its form.
Please, and it would be the end, could you give me the ts code as a whole which :

  • read form fields “brandName” and “age”
  • if set by user in form, keep the value, if not, read the value from inputs items (with loop ?)

and which convention must follow the previous node to arrange these values in its ouput json ?

Here an example which does exactly that:

import { IExecuteFunctions } from 'n8n-core';
import {
	INodeExecutionData,
	INodeType,
	INodeTypeDescription,
} from 'n8n-workflow';


export class Example implements INodeType {
	description: INodeTypeDescription = {
		displayName: 'Example',
		name: 'example',
		group: ['input'],
		version: 1,
		description: 'Example',
		defaults: {
			name: 'Example',
			color: '#0000FF',
		},
		inputs: ['main'],
		outputs: ['main'],
		properties: [
			{
				displayName: 'Age',
				name: 'age',
				type: 'number',
				default: 0,
			},
			{
				displayName: 'Brand Name',
				name: 'brandName',
				type: 'string',
				default: '',
			},
		]
	};


	execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {

		const items = this.getInputData();

		if (items.length === 0) {
			items.push({json: {}});
		}

		const returnData: INodeExecutionData[] = [];
		let item: INodeExecutionData;

		for (let i = 0; i < items.length; i++) {
			const age = this.getNodeParameter('age', i) as number | undefined;
			const brandName = this.getNodeParameter('brandName', i) as string | undefined;

			item = items[i];

			const newItem: INodeExecutionData = {
				json: {
					age: age || item.json.age,
					brandName: brandName || item.json.brandName,
				},
			};

			returnData.push(newItem);
		}

		return this.prepareOutputData(returnData);
	}
}

But that was really just an exception as we do not do free custom node development!

and which convention must follow the previous node to arrange these values in its ouput json ?

Again do not understand. There is simply no sorting in a JavaScript-Object.

Thanks a lot jan, I better understand this method.
Just a thing which confuses me, if first items in input json are for example “date” and “color”, won’t you get 2 undefined values in 2 iterations, and build a quite empty json in corresponding newItem, which is pushed anyway ? Or must I understand that the method will randomly get the good item in input json ? In short, I guess that there will be items.length items of json in output. I would use a ‘continue’ and would push only once at the loop exit, no ?
Something like that :

let ageKept: number;
let brandNameKept: string;
for (let i = 0; i < items.length; i++) {
         const age = this.getNodeParameter('age', i) as number | undefined;
         const brandName = this.getNodeParameter('brandName', i) as string | undefined;
         // several age or brandName are possible in input json, we take the last seen
         if (age != undefined) { ageKept = age; continue; }
         if (brandName != undefined) { brandNameKept = brandName; continue; }
}

// TODO test on undefined *Kept variables
const newItem: INodeExecutionData = {
      json: {
            age: ageKept,
            brandName: brandKept,
      },
};

returnData.push(newItem);

Yes exactly. If the input data does not have “age” and “brandName” and it is also not set as a parameter on the node both of them would be undefined. Which can be right or wrong depending on what exactly you are doing.

If one item would contain “date” and “age” it would result in one empty item. If there is an item with “date” and another item with “age” then it would return in two. So the node currently simply outputs exactly as many items as it receives.

Not sure what you mean with randomly. Nothing happens anywhere randomly.

The code you wrote would always return only one single item (no matter how many input items there were). Also does it totally ignore the data on the item and only reads it from the parameters. Which is now totally different from your above requirements. Additionally would it not read the “brandName” of an item if it has “age” set. Not sure if that is really what you want. If not the both “continue” can simply get deleted.

Randomly = direct access. I mean : does the method will return age if age is in the input json, whatever the index in the method parameter and the location of age in the json ? I don’ think so ?
Yes the 2nd continue is useless of course, the first just avoids a useless test on brandName.

This should work no ?
A Set node, where itCode is valued, followed by a node which itCode may be type as parameter or read from previous node if left empty (here I left it empty)
The loop read at least once to be sure to get the values of typed parameters (kept values of parameters in loop will be handled later)

let itCode: string;
let url: string;
console.log("items:"+JSON.stringify(items));
for (let idx = 0; idx <= items.length; idx++) {
    itCode = this.getNodeParameter('itCode', idx) as string;
    console.log("itCode: "+itCode);
    etc ....

gives

items:[{"json":{"itCode":"ABC-DEF-0000"}}]
itCode:
itCode:

If you have two nodes one after another then the second node would overwrite whatever the first node did set.

And also there it looks again like you are mixing up parameters and items. It is true that the item data has “itCode” set but the parameter “itCode” is empty. So the result looks correct.

To say it again they are two totally different things. “itCode” parameter has nothing at all to do with “itCode” in the items. There is only a relationship if an expression on the “ItCode” parameter got set to use the “itCode” item-data.

Well understood jan thanks ! :slight_smile:
But now, how can I do what I want ? Here, the final node can have several arrows reaching it, with their own value of itCode each (which is a mandatory field), and I want to get the value typed in this final node BUT I want also get the value of the previous node if this typed value is left empty. In fact I would want a more flexible usage of parameters.
Maybe it’s not possible ?
Or … using an other parameter name in the previsous name ? eg itCodeNode

If you say several arrows it means you need multiple inputs. If you would all point them to the same one the node would simply execute multiple times, once for every “arrow”.

How to work with multiple inputs you can see in the merge node. Here the code:
https://github.com/n8n-io/n8n/blob/master/packages/nodes-base/nodes/Merge.node.ts

In the end, you have to get the data for each input separately like this:

const dataInput1 = this.getInputData(0); // items of input 0
const dataInput2 = this.getInputData(1); // items of input 1

What you want to do sounds exactly like what I programmed for you here (except there just for one input):

That uses the data of the parameter and if it is empty it falls back to the data of the item.

I was not clear enough sorry jan : ONLY ONE predecessor node can be activated in the WF
So getting multiple outputs from the previous nodes in the design does not interest me, I just want to get the argument itCode passed from ONE of the previous nodes.

Ah OK. In this case the node I did create above should do exactly that.

Sorry jan I didn’t expand your code …
Yes, items[i].json.itCode is the good key
So the loop is not necessary : I get a local node parameter value (in its form) by this.getNodeParameter(paramName, 0) and I get input value by this good key, so it’s ok for me :slight_smile:

Last thing jan, you said :

And also there it looks again like you are mixing up parameters and items.

So I’ll never understand why I see so many loops on input items which calls getNodeparameter() each … very confusing for me, and that’s why I had to create this post.
But I won’t care anymore :slight_smile:

Ah no it is important to understand that concept.

The reason is expressions.

So if the following two items get sent into a node:

[
	{
		"json": {
			"name": "Tim"
		}
	},
	{
		"json": {
			"name": "Frank"
		}
	}
]

That data gets received exactly like that by this.getInputData(). I guess so far it is clear.

Ok, now lets add a parameter called “message” to the node and set it to “Hello”. The following data would get returned:

this.getNodeParameter('message', 0) // For the first item => "Hello"
this.getNodeParameter('message', 1) // For the second item => "Hello"

They return the same as the value of the parameter is set to a fixed value which is here “Hello”.

Now lets change the parameter “message” to an expression and then set the value to

Hello {{$json["name"]}}

Now the result would be:

this.getNodeParameter('message', 0) // For the first item => "Hello Tim"
this.getNodeParameter('message', 1) // For the second item => "Hello Frank"

So the {{$json["name"]}} tells n8n to replace it with the value it finds under “name” from the incoming data. As there could be multiple items (and in the above example there are, exactly two) we have to tell it which item it should use to resolve the expression. Therefore the loop and the index. In the first one we tell it to use the data of index 0 which has for “name” the value “Tim” and for the second one we give it the index 1 for the second item which has the value “Frank”.

I hope that makes sense.

Well understood jan, but this cannot suit my case
It could if we could type code in the itCode field value (but we can’t), for example
{{$json["itCode"]}} || AAA-BBB-1111
meaning ‘take the input itCode, and if undefined take this default value’

You can, but you have to write it differently:

{{$json["itCode"] || "AAA-BBB-1111"}}

hey :slight_smile: I’m going to test that, thanks jan !