[英]Create Series of Prompts from Array of Questions in Node.js
我正在節點中創建命令行工具。 用戶輸入特定命令后,我將解析他們輸入的選項。
> mycommand init --name name --email user@domain.com
然后,我驗證選項的值。 驗證之后,我生成了一系列問題,這些問題無法通過用戶輸入的選項以及未通過驗證的輸入選項來回答。 生成數組后,我將遍歷該數組並針對每個問題給出提示:
for(var i = 0; i < questions.length; i++) {
var prop = questions[i],
title = messages.prompts.init[prop].question,
def = messages.prompts.init[prop].def,
input = read.createInterface(process.stdin, process.stdout);
if(messages.prompts.init[prop].hasOwnProperty('format')){
title = title + ' <' + messages.prompts.init[prop].format + '> ';
}
input.question(title + ' (' + def + ')', function (a) {
//dosomething(a);
process.exit();
});
}
問題是,代碼在不等待用戶輸入的情況下循環遍歷整個數組,並以顯示來自數組最后一項的消息的最終提示結尾。 當我嘗試鍵入響應時,每個擊鍵都將乘以數組中的項目數。 例如,嘗試輸入“ myname”將導致:
> Enter Your Name (none): mmmmyyyynnnnaaaammmmeeee
我已經嘗試了多個節點模塊,包括讀取和提示,並且遇到了相同的問題,所以我不能完全理解命令行工具如何讀取用戶輸入。
任何有關如何解決此問題的見解將不勝感激。
謝謝。
因此,我認為問題在於它正在創建回調,並且循環繼續進行,因此您得到的結果很奇怪。 使用Tiny CLI示例的變體,您可以從陣列中創建一個帶有問題的提示,並觀察line事件以獲取輸入,然后重復。 這是一個簡單的例子。
var read = require('readline'),
input = read.createInterface(process.stdin, process.stdout),
questions = ['test1: ', 'test2: '],
counter = 0;
input.setPrompt(questions[0]);
input.prompt();
input.on('line', function (a) {
console.log('answer: ', a);
counter++;
if (counter < questions.length) {
input.setPrompt(questions[counter]);
input.prompt();
} else {
process.exit(0);
}
});
我寫了一些實用的方法來解決這個問題。 像這樣在其中添加了幾對寶石,它會自動將最后輸入的值寫入一個隱藏文件,以便用戶可以按Enter鍵
```
var async = require('async');
var fileUtils = require('../util/file-util');
var LAST_OPTIONS_FILE='./.last-options-file';
/**
* The prompt object type that is used by askSeries
* @typedef {Object} Prompt
* @property {string} key - is used as the variable name
* @property {string} [default] - is a default value so you can just press enter
* @property {string} [format] - validates input against a regex
* @property {string} [question] - use to make a more human readable prompt
* @property {boolean} [confirm] - will force user to confirm the value if it was found on the command line
* @property {boolean} [forceEnter] - if true, user must enter this value (last value is ignored)
*/
/**
* Asks user for input for each of the prompts
*
* <PRE>
* Example:
* askSeries([{key:"customerId"}, {key:"subscriptionId", "default":"blah"}], function(inputs) {
* console.log(inputs);
* });
* OUTPUT:
* customerId: 5
* subscriptionId [blah]: 9
* [ customerId: '5', subscriptionId: '9' ]
*
* askSeries([{key:"customerId", question:"Enter Customer Id", format: /\d+/}, {key:"subscriptionId", "default":"blah"}], function(inputs) {
* console.log(inputs);
* });
* OUTPUT:
* Enter Customer Id: abc
* It should match: /\d+/
* Enter Customer Id: 123
* subscriptionId [blah]:
* [ customerId: '123', subscriptionId: 'blah' ]
* </PRE>
*
* @param {Prompt[]} prompts - an array of Prompts which dictate what user should be asked
* @param {object} [argv] - if any of prompts .keys match argv it sets the default appropriately,
* argv will get all prompt .key values set on it as well
* @param {function} callback - signature is function(err,params)
*/
exports.askSeries = function(prompts,argv,callback) {
var input = {};
var lastVal = {};
if(typeof argv === 'function') { callback = argv; argv=null; }
lastVal = fileUtils.readJSONFileSync(LAST_OPTIONS_FILE);
if( !lastVal ) { lastVal = {}; }
console.log("LASTVAL", lastVal);
async.eachSeries(prompts, function(prompt, next) {
if( !prompt.key ) { callback(new Error("prompt doesn't have required 'key' param")); }
if( !prompt.confirm && argv && argv[prompt.key] ) {
input[prompt.key] = argv[prompt.key];
return next();
}
else {
var defaultVal = prompt.default;
if( argv && argv[prompt.key] ) { defaultVal = argv[prompt.key]; }
else if( !prompt.forceEnter && lastVal[prompt.key] ) { defaultVal = lastVal[prompt.key]; }
exports.ask( prompt.question || prompt.key, prompt.format || /.+|/, defaultVal, function(value) {
if( !value ) {
if( prompt.default ) {
value = prompt.default;
}
if( argv && argv[prompt.key] ) {
value = argv[prompt.key];
}
}
input[prompt.key] = value;
if( argv ) { argv[key] = value;}
next();
});
}
}, function(err) {
try {
var fileData = JSON.stringify(input);
fileUtils.writeToFile(LAST_OPTIONS_FILE, fileData );
}catch(err) { console.log("Unable to save entered values"); }
callback(err,input);
});
};
/**
* Prompts user for input
*
* @param {string} question prompt that is displayed to the user
* @param {string} [format] regex used to validate
* @param {string} [defaultVal] uses this value if enter is pressed
* @param callback is invoked with value input as callback(value);
*/
exports.ask = function ask(question, format, defaultVal, callback) {
var stdin = process.stdin, stdout = process.stdout;
if( typeof(format) === 'function' ) {
callback = format;
format = null;
}
if( typeof(defaultVal) === 'function') {
callback = defaultVal;
defaultVal = null;
}
stdin.resume();
var prompt = question;
if( defaultVal ) {
prompt += " [" + defaultVal + "]";
}
prompt += ": ";
stdout.write(prompt);
stdin.once('data', function(data) {
data = data.toString().trim();
if( !data && defaultVal ) {
data = defaultVal;
}
if (!format || format.test(data)) {
callback(data);
} else {
stdout.write("It should match: "+ format +"\n");
ask(question, format, callback);
}
});
};
/**
* Prints the usage from the <link Prompt> array.
* @param {Prompt[]} prompts - the array of prompts that will be asked if not entered on cmd line.
* @param {string} binaryName - if specified it puts it in the usage as <binaryName> [options]
* @param {boolean} [dontPrint] - if true, the usage will not be written to console
*/
exports.getUsage = function(prompts, binaryName, dontPrint) {
if(!binaryName) { binaryName = "program_name";}
var s = "\nUsage: \n./" + binaryName + " [options]\n\n options:\n";
console.log(prompts);
prompts.forEach( function(p) {
s += " --" + p.key;
if(p.question) { s += ": " + p.question;}
if(p.format) { s += ", format: " + p.format; }
s += "\n";
});
if( !dontPrint ) {
console.log( s );
}
return s;
};fs
```
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.