How to create a custom Inject Node in NodeRED? - javascript

I want to start my node with a button click, so that I do not have to put the inject node in front. How would that be possible to register a button click in the javascript file?
I have tried to put node.on("input", async function(msg){/*some code*/}) inside the javascript file, where I register my node. I was able to add this button through this:
//HTML file script
<script type="text/javascript">
RED.nodes.registerType('light', {
category: "input",
color: "#f3c12b",
defaults: {
name: {value: ""},
plus: {value: ""},
topic: {value: this.name},
payload: {value: ""}
},
inputs: 0,
outputs: 1,
label: function(){
return "Licht '"+this.name+"'" || "Licht";
},
button: {
enabled: function(){
return true;
},
onclick: function(){
//I´ve put the code here, but then I have to reconfigure my functions
}
}
});
</script>
//Javascript file --> register function
//Not getting any response
node.on("input", async function(msg) {
msg = {};
msg.topic = this.topic;
msg.payload = "This is a new message!";
node.send(msg);
});
I was expecting, that when I click this the node is sending a message, but the node is not responding anything.

The best bet here is to look at the inject node source code.
inject.html
inject.js
In the case of the inject node, the onclick function of the button parameter in the HTML file actually does a POST call to /inject/{id} on the server.
onclick: function() {
...
var node = this;
$.ajax({
url: "inject/"+this.id,
type:"POST",
success: function(resp) { ... }
});
}
The inject JS file, which runs on the server, hosts an http endpoint at /inject/:id that when it's called gets the node by id and called node.receive() which acts as the trigger for it's input.
module.exports = function(RED) {
...
RED.httpAdmin.post("/inject/:id", RED.auth.needsPermission("inject.write"), function(req,res) {
var node = RED.nodes.getNode(req.params.id);
...
node.receive();
...
});
}

Related

Call Restlet from User Event Script Error INVALID_LOGIN_ATTEMPT

I am trying to access Restlet from User Event script but i am receiving error: body":"error code: INVALID_LOGIN_ATTEMPT\nerror message: Invalid login attempt. Do i need to pass credentials too? what should i pass client id, client secret? is there another way?
I also tried url.resolveScript but no luck.
/**
* #NApiVersion 2.x
* #NScriptType UserEventScript
* #NModuleScope Public
*/
define(["N/https", "N/log", "N/record", "N/url" ],
function(https, log, record, url) {
function beforeLoad(context) {
// log.debug('beforeLoad Triggered');
// context.newRecord;
// context.type;
// context.form;
return;
}
function afterSubmit(context) {
log.debug('Before code');
var record = context.newRecord;
var requestBody = {
"recordId": record.id,
"recordType": record.type,
};
var output = url.resolveScript({
scriptId: '1157',
deploymentId: '1',
});
var output1 = 'https://12345-sb5.restlets.api.netsuite.com';
log.debug('After code', output );
var response = https.post({
url: output1 + output,
body: requestBody,
});
if (response.code === 200) {
// success!
} else {
// handle error
}
log.debug('After code', response );
return;
}
function beforeSubmit(context) {
// log.debug('beforeSubmit Triggered');
// context.newRecord;
// context.oldRecord;
// context.type;
return;
}
return {
beforeLoad : beforeLoad,
afterSubmit : afterSubmit,
beforeSubmit : beforeSubmit
}
})
Why do you need your UE Script to submit and trigger your RESTlet every record submit?
Below could work for ClientScript, just not sure if saveRecord (only when the Submit/Save is clicked) entry point will trigger this.
var requestBody = JSON.stringify({
recordId: record.id,
recordType: record.type
});
var output = url.resolveScript({
scriptId: '1157',
deploymentId: '1'
});
var response = https.post({
url: output,
body: requestBody
});
Previously you could manipulate the cookies and pass it. However, it's no longer the case and Server Scripts cannot call Server Scripts unless you pass Authentication which is a pain if you have no library. Oauth1 + SHA256 is complicated to figure out without libraries.
You could probably approach this on another angle depending on your use case. At this moment, it isn't clear why you want AfterSubmit to call RESTlet. If you do this, if you have routine Scheduled Scripts that touches that record and saves the record, it will keep triggering your AfterSubmit unless you place an if statement.
try https.requestRestlet
var response = https.requestRestlet({
body: JSON.stringify({
"recordId": record.id,
"recordType": record.type,
}),
deploymentId: '1',
method: 'POST',
scriptId: 1157,
});

ng-grid not binding with latest data after async XMLHttpRequest's response

I have a AngularJS web application, I'm trying to upload a file to a server and while the upload is complete, I have to update ng-grid with the last uploaded file's entry. The following is my grid html,
<div class="gridholder" data-ng-grid="viewmodel.gridOptions">
</div>
The following is my controller logic.
vm.gridOptions = {
data: 'gridData',
enableColumnResize: true,
enablePaging: true,
columnDefs: [
{ field: 'FileName', displayName: 'File Name', width: 250 }
{ field: 'UploadedDate', displayName: 'Uploaded Date'}
],
multiSelect: false,
enableSorting: true,
showFooter: true,
};
The requirement is that I show the progress of file upload and the entire application to be responsive when upload is in progress, I have achieved this but my ng-grid not is updating in a particular scenario.
If I remain in the same page until the file is uploaded and the response comes, the grid is refreshing but when I move to another page of my application and come back to the file upload page, and the response comes after, my grid is not getting refreshed.
This is my file upload js code,
var data = new FormData();
data.append('file', file);
var xhrRequest = Factory.uploadFileRequest('UploadFile');
xhrRequest.upload.addEventListener("progress", progressHandler, false);
xhrRequest.onreadystatechange = function (e) {
};
xhrRequest.onload = function (e) {
if (JSON.parse(e.currentTarget.responseText).Success == true) {
$timeout(function () {
$scope.LoadGrid();
//showing success message here
}, 2000);
}
else
{
//showing error message here
}
};
xhrRequest.onerror = function (e) {
//showing error message here
};
xhrRequest.send(data);
$scope.LoadGrid = function () {
Factory.callGet("Files").then(function (d) {
$scope.gridData = d.data;
}
$scope.totalItems = $scope.gridData.length;
}, function error(err) {
//Error Message
});
}
gridData is my data-ng-grid value. I'm calling my LoadGrid method inside a $timeout already but still the grid is not refreshing with latest data. Any help would be much appreciated.
Possible Problem
You implemented upload logic inside the controller. When you switch to another view, angularjs destroys your controller and therefore no one listens on file upload response.
Possible solution:
1) Use a service (or Factory) kind of singleton to manage upload process there.
For example MyService.upload(data).then(function (response) {/**/});
2) By default MyService.upload(data) returns a promise on a regular basis but also stores the result inside the Service, for example, upload_results:
app.service('MyService',['$q',function ($q) {
var self = this;
var upload_results = [];
self.upload = function (_data) {
return // <YOUR_PROMISE>
.then(function (response) {
upload_results.push({
id: new Date().getTime(),
data: response.data
})
}
, function (error) {
console.error(error);
return $q.reject(error);
});
};
self.getResults() = function(){
return upload_results;
}
self.resetResults() = function(){
upload_results = [];
}
}
When you initialize the controller on start or go back to the previous controller, you ask the service if it has something for you:
var results = MyService.getResults();
if(results.length > 0){
$scope.gridData = results[0].data; // or use timestamp to manage it
MyService.resetResults();
}
Hope it will give you some insight,

Handling Direct Update with navigation to native page in IBM Mobile first

I am developing a project, in which i need to call a native page in wlCommonInit()
function wlCommonInit(){
WL.NativePage.show(nativePageClassName, backFromNativePage, params);
}
I want my project to receive the direct update with persession mode. So to connect with the Mobile First Server, I have called WL.Client.connect()
function wlCommonInit(){
busyind = new WL.BusyIndicator;
busyind.show();
WL.Client.connect({onSuccess: connectSuccess, onFailure: connectFail});
WL.NativePage.show(nativePageClassName, backFromNativePage, params);
}
More over I want to handle the direct update so I have added the required code.
wl_directUpdateChallengeHandler.handleDirectUpdate = function(directUpdateData,
directUpdateContext) {
// custom WL.SimpleDialog for Direct Update
var customDialogTitle = 'Custom Title Text';
var customDialogMessage = 'Custom Message Text';
var customButtonText1 = 'Update Application';
var customButtonText2 = 'Not Now';
WL.SimpleDialog.show(customDialogTitle, customDialogMessage, [{
text: customButtonText1,
handler: function() {
directUpdateContext.start(directUpdateCustomListener);
}
}, {
text: customButtonText2,
handler: function() {
wl_directUpdateChallengeHandler.submitFailure();
}
}]);
};
var directUpdateCustomListener = {
onStart: function(totalSize) {},
onProgress: function(status, totalSize, completeSize) {},
onFinish: function(status) {
WL.SimpleDialog.show('New Update Available', 'Press reload button to update to new version', [{
text: WL.ClientMessages.reload,
handler: WL.Client.reloadApp
}]);
}
};
Here the problem is, the application is navigating to the native page
before it can go to the direct update handler function when the direct
update is available.
Is there any way to resolve it?
I think what you should do instead if use the API [WL.Client.checkForDirectUpdate.
This way you will have the ability to first check for direct update - handle it if there is an update and then execute the function for opening the native page.
The code that is running is async, so you can't control it if you're not following the above suggestion.

Why each oo function/module after ajax, then each function/module attribute change to the last execute?

I tried to sync database return html string append to each .buttonContainer , use action.js execute other file loadButton module use parameter point specific element.
the result should be use different .buttonContainer and with their attr id connect db get result append to each .buttonContainer
My question is if I set ajax option async default true, then both return html string will append to the last .buttonContainer.
I can't understand I already set each module/function have their attr el from each execute parameter.
Why and how to solve it?
I tried to change async to false then it work but slowly the page, so I'm trying to find other solution.
<div class="buttonContainer" data-user-id="0"></div>
<div class="buttonContainer" data-user-id="1"></div>
action.js
define(['DomReady!', 'jquery', 'loadButton'],function (DomReady, $, loadButton) {
return {
init: function() {
// loop each element as parameter and execute
$('.buttonContainer').each(function(index, el) {
var config = {};
config.el = $(this);
loadButton.init(config);
});
},
}
});
loadButton.js
define(['DomReady!', 'jquery'],function (DomReady, $) {
return {
init: function(config) {
this.config = {};
this.config.el = config.el; // set each execute have their own private attribute
this.onLoadAction();
},
onLoadAction: function() {
this.onLoadController();
},
onLoadController: function() {
var userId = this.config.el.attr('data-user-id');
var mthis = this;
this.onLoadRequestDB('load/'+userId).done(function(response) {
console.log(mthis.config.el);
var response = JSON.parse(response);
mthis.config.el.append(response.button);
});
},
onLoadRequestDB: function(url) {
return $.ajax({
url: url,
type: 'GET',
processData: false,
contentType: false,
// async: false
});
},
}
});
Edit:
https://stackoverflow.com/a/22121078/1775888 I found some solution here, so I edit loadButton.js like this
..
init: function(config) {
this.config = {};
this.config.el = config.el; // set each execute have their own private attribute
this.onLoadAction(this.config);
},
onLoadAction: function(config) {
this.onLoadController(config);
},
onLoadController: function(config) {
..
pass parameter, then it work.
But I still want to know why I set the loadButton.js init this, in each loop but still can be cover after ajax. makes all response append to element from last execute loadButton.js config parameter

jsdom not triggering internal <script>

I am trying to sample a page that has a script on it that changes the CSS of certain elements such that an attribute toggles between "active" and "inactive" based on the width of the window.
I have written nodeJS code that gathers and analyzes the page, but I cannot seem to trigger, or detect the triggering of the script. I suspect it has to do with defaultDocumentFeatures, but I could be wrong.
The script opens the page in JSDOM with a default width, then changes it to a specified block of other widths. This should result in changes in the output, but it does not. I am getting the same results for all situations. I suspect that the script on the page simply isn't running, but need help to make it do so
Here is my code (expurgated for public viewing.)
var express = require('express');
var router = express.Router();
var jsdom=require('jsdom');
router.get('/getSamplePage', function(req, res) {
getaEpicPage(req, res, function(contents){
console.log("got an sample page"+contents+"000000000");
//contents gets replaced by the actual results that will be processed upstream
res.send({'msg':'', 'contents':contents});
});
});
var getaSamplePage=function (req, res, callback) {
jsdom.defaultDocumentFeatures={
FetchExternalResources : ['script', 'css'],
ProcessExternalResources : ['script', 'css'],
MutationEvents : '2.0',
QuerySelector : false
};
var elementLocations=[
'sample_01',
'sample_02',
'sample_03'];
var contents=[{label:'DIV ID', value:'Is Populated', width: "Screen Width", position:"element size"}];
var windowWidths=[
479,
481,
781,
783,
1023,
1025,
]
for (var i in windowWidths){
jsdom.env({
url:'http://sourcefilelocation/',
scripts: ['http://code.jquery.com/jquery.js'],
created: function(errors, tstWindow) {
tstWindow.tstWindowWidth=windowWidths.pop();
tstWindow.addEventListener('resize', function() {
//verify that resize is triggered
console.log('Resize event completed');
});
tstWindow.constructor.prototype.resize = function (width){
//defining a "resize" event, since that may be what triggers things
console.log("resize has been attempted");
tstWindow.originalWidth=tstWindow.innerWidth;
tstWindow.outerWidth=tstWindow.innerWidth=width;
}
tstWindow.readyState="complete";
},
done: function(errors, tstWindow) {
setTimeout(function () {
//setting a timeout to ensure that any elements have finished I have put this as high as ten seconds.
console.log("ready state "+tstWindow.readyState);
tstWindow.resize(tstWindow.tstWindowWidth)
$=tstWindow.$;
for (var sampleLocation in sampleLocations) {
var sampleID=sampleLocations[sampleLocation];
$('div > [sampleAttribute='+sampleID+']').each(function(){
//If the script I am trying to watch work triggers, it should change the "content" attribute
var elementActive=$(this).css('content');
var position=$(this).attr('sample-position');
console.log("element css>>>>>> "+tstWindow.innerWidth+" "+sampleID+" "+position+" "+elementActive);
if (elementActive=='"active"'){
contents.push({label:sampleID, value: elementActive, width: tstWindow.originalWidth+"/"+tstWindow.innerWidth, position:position});
}
});
};
}, 50);
}
});
};
setTimeout(function () { callback(contents);}, 100);
};
module.exports = router;
Per suggestion I added this to my jsDom config object, just after the url:
FetchExternalResources : ['script', 'css'],
ProcessExternalResources : ['script', 'css'],
MutationEvents : '2.0',
QuerySelector : false,
But it has made no apparent difference.
As per the jsdom Readme when you're using jsdom.env the default feature set does not include processing scripts.
You have to pass the FetchExternalResources and ProcessExternalResources to jsdom.env specifically.
jsdom.env({
html: input,
url: url,
features: {
FetchExternalResources: ["script", "img", "css", "frame", "iframe", "link"],
ProcessExternalResources: ["script"]
},
created: function (err, window) {
console.log('Created');
},
loaded: function (err, window) {
console.log('Loaded');
}
});
jsdom.env doesn't use the jsdom.defaultDocumentFeatures object.

Categories

Resources