I made a function that removes spaces, sets to lower case and split on , of the msg.payload
Now every time i need to do this i copy this function into my function node.
But is it possible to put this in a file together with other functions and then include the file into the function node? So i do not need to copy paste it every time in a new function node?
no it is not possible to import or require external files into your Function node, despite what @krambriw suggests.
There is no built-in way to store your Function node code in external files. There is a contrib node that lets you do that, but it is very old and has long since fallen behind the functionality of the core Function node - so I really wouldn't recommend it.
It is a pain point we're aware of for a number of reasons. For example, if you are using the projects feature, the Function code appears in a single line in the JSON which makes it very hard to see the changes when comparing versions.
So it is something to address in the future. You do lose the portability of having your flows in a single file, but there are trade-offs to be made.
Hi .. you could always create a Subflow from that piece of logic so instead of copy/pasting it you can drag and drop the subflow where you need it .. dunno if its any faster or cleaner for a few lines of code
Actually, there is a work around but it requires you to put the code into an external module file rather than into a flow.
I do that with some utility functions. You then require the module in your settings.js file and it is available as a global. so you can global.get it in your function nodes.
You can use a Function Node as a repository for often used functions. This is an example of how I have done it;
const utility = (function () {
'use strict';
// recursive function to clone an object. If a non object parameter
// is passed in, that parameter is returned and no recursion occurs.
function cloneObject(object) {
if (object === null || typeof object !== 'object') {
return object;
}
var newObject = object.constructor(); // give temp the original obj's constructor
for (var key in object) {
newObject[key] = cloneObject(object[key]);
}
return newObject;
}
// add properties of Object 2 to Object 1
function concatObject(object_1, object_2) {
var properties;
for ( properties in object_2 ) {
if ( object_2.hasOwnProperty(properties) && !object_1[properties] ) {
object_1[properties] = object_2[properties];
}
}
}
// Returns a number as a string wth a specified number of preceding zeros
function padZero(number, padSize = 0) {
return number.toString().padStart(padSize, 0);
}
function ordinal(number, inclusive = true) {
let x = number;
if (inclusive) {
return number + ["th", "st", "nd", "rd"][(x =~~ (x < 0 ? -x : x) % 100) > 10 && x < 14 || (x %= 10) > 3 ? 0 : x];
} else {
return ["th", "st", "nd", "rd"][(x =~~ (x < 0 ? -x : x) % 100) > 10 && x < 14 || (x %= 10) > 3 ? 0 : x];
}
}
function isArray(myArray) {
return myArray.constructor === Array;
}
/**
* Represents a search through an array of objects.
* @function findArrayProperty
* @param {Array} array - The array you want to search through
* @param {string} [property] - The property name to search
* @param {string} key - The key to search for
*
* Return: The object where the key was found, or 'false' if not found
*/
function findArrayProperty(array, property, key){
// Set foundProperty to default
var foundObject = false;
for (var i = 0; i < array.length; i++) {
if (array[i][property] === key) {
foundObject = array[i];
}
}
return foundObject;
}
return {
cloneObject: cloneObject,
padZero: padZero,
concatObject: concatObject,
ordinal: ordinal,
isArray: isArray,
findArrayProperty: findArrayProperty
};
}());
global.set('utility', utility, 'file');
(The functions are possibly not the best but you get the idea). I use an Inject node to initialise the global.set. When you want to use one of the functions you just include let utility = global.get('utility', 'file'); and then reference the required function as (for example) utility.padZero(60, 3) etc.
This is not exactly what you were after - an external file - but does only have to be added once for each Node-red instance. I usually put the two nodes in the first Flow. Not ideal but it works.
Hope I understood the question correctly and that this work around works (Not my original idea I hasten to add)
That's what I had in mind yes. However, I would use a relative reference in settings.js as that lets you move the whole folder and everything would still work.
Strange but it works:
The global variable 'bigobj': (
{
some[]more[]{dx:"here"} , // array of objects with array of objects. The 'Config' node requires JSON.
.....
"get_dx": "function( d,p) { return this.some[d].more[p].dx; }"
}
i.e. a JSON version of a function.... (all in one line )
USE:
Inside a function node:
var bigO = global.get("bigobj");
function callJSONMethod(obj, fname, a, b, c, d){
// see: https://stackoverflow.com/questions/49125059/how-to-pass-parameters-to-an-eval-based-function-injavascript
var wrap = s => "{ return " + obj[fname] + " };" //return the block having function expression
var func = new Function(wrap(obj[fname]));
return func.call( null ).call( obj, a, b, c, d); //invoke the function using arguments
}
msg.payload =callJSONMethod(bigO, "get_dx", 2, 2);
return msg:
returns "here", unbelieve!
i.e I must add the function callJSONMethod() to any function node using bigobj.....
maybe acceptable.