I have some data, lets say:
var dat = JSON.stringify(frm.serializeArray())
I want to submit this to the server using a roundtrip (aka, non ajax).
I know this is possible, but I can't find any literature on it. Ideas?
(I am using jQuery, if that makes it easier)
EDIT: while all of these answers so far answer the question, I should have included that I want an "content type" of "application/json"
Create an HTML form with unique "id" attribute. You can hide it using CSS "display:none". Also fill the action and method attributes.
Add a text or hidden input field to the form. make sure you give it a meaningful "name" attribute. That's the name that the server would get the data within.
Using JQuery (or plain old javascript) copy the variable "dat" into the input field
Submit the form using script
There is a working draft to support the so called HTML-JSON-FORMS, see:
http://www.w3.org/TR/2014/WD-html-json-forms-20140529/
So far use ajax or send the json into an input text field.
<form action="xxx.aspx" method="POST">
<input type='hidden' id='dat' />
<!-- Other elements -->
</form>
<script type='text/javascript'>
$('#dat').val(JSON.stringify(frm.serializeArray()));
</script>
You would need to assign the json string to an input's value inside a form tag in order for it to get POSTed to the server (either by the user submitting the form or by clicking the submit button programmatically).
Alternatively from javascript you could use window.location to send the variable as part of a GET request.
In another answer someone mentioned a W3 working draft which is outdated now and the newer version of the document says we can use enctype="application/json" attribute for the form and it will send the whole form fields as properties of an object.
It is still unclear to me how to send an array though, but refering to the above document you can send an object simply as:
<form enctype='application/json'>
<input name='name' value='Bender'>
<select name='hind'>
<option selected>Bitable</option>
<option>Kickable</option>
</select>
<input type='checkbox' name='shiny' checked>
</form>
// produces {"name": "Bender", "hind": "Bitable", "shiny": true}
I can't copy the whole doc here, so check out the document to see how to create more complex objects using array notation and sparsing arrays in input field names.
To create the form out of your object, you have to make a series of input elements, that produces the same JSON object you have in hand. You can either do it manually, or if your object is large enough, you can use a code snippet to convert your object to the desired input elements.
I ended up with something like the code below as the base.
You can change it to your need (e.g. make the form hidden or even produce more diverse input field types with styles for different property types for a real proper form)
(function () {
const json = {
bool: false,
num: 1.5,
str: 'ABC',
obj: {b:true, n: .1, s: '2', a: [1, '1']},
arr: [
true, 500.33, 'x', [1, 2],
{b:true, n: .1, s: '2', a: [1, '1']}
]
};
const getFieldHTML = (value, name) => {
if (name||name===0) switch (typeof value) {
case 'boolean': return `<input type="checkbox" name="${name}" ${value?'checked':''}>\n`;
case 'number': return `<input type="number" name="${name}" value="${value}">\n`;
case 'string': return `<input type="text" name="${name}" value="${value}">\n`;
}
return '';
};
const getFieldsHTML = (value, name) => {
const fields = [];
if (value instanceof Array)
fields.push(...value.map((itemValue, i) =>
getFieldsHTML(itemValue, name+'['+i+']')
));
else if (typeof value === "object")
fields.push(...Object.keys(value).map(prop =>
getFieldsHTML(
value[prop], //value is an object
name?(name+'['+prop+']'):prop
)
));
else
fields.push(getFieldHTML(value, name));
return fields.join('');
};
const fieldsHTML = getFieldsHTML(json);
const frm = document.createElement('form');
frm.enctype = 'application/json';
frm.method = 'POST';
frm.action = 'URL GOES HERE';
frm.innerHTML = fieldsHTML;
console.log(fieldsHTML);
console.log(frm)
})();
Check your browser's console to inspect the created form DOM and its children.
Related
I have an array of student objects that has some basic information like Name and Address. I want to be able to add a tag property to the object that is based on the user's input
I am acomplishing this with an input like this
<input placeholder="new tag" onInput={(e) => setAddedTag(e.target.value)} />
<button type="submit" onClick={() => addTag()}>Add Tag</button>
And I am adding the tag property to the specific object with this code
const addTag = () => {
setStudentIndex(students.id - 1)
students[studentIndex].tag = [AddedTag]
// needs to add more than 1
}
However this seems to only work with one tag, and if the user adds a second tag it will overwrite the first one. (or it will just crash)
I have tried using the spread operator
students[studentIndex].tag = [...AddedTag]
However this instead set the tag to be ['a', 'b', 'c'] when the user had typed in abc
How can I accomplish adding an array of string as a prop?
have you tried using push()? something like:
students[studentIndex].tag.push(AddedTag);
define the tag to be an array within the object. Something like this:
const students = [
{
name: "xyz",
address: "abc",
tag: []
}
]
Then in your code change the following line from:
students[studentIndex].tag = [AddedTag]
to students[studentIndex].tag.push(AddedTag)
That should do it.
try
const { tag = [] } = students[studentIndex]
students[studentIndex].tag = [...tag, AddedTag]
I cant seem to figure this one out I am trying to check if an input is inlcuded in an array of nodes. No matter what I try it seems to return false. I'm thinking it must be something simple that I'm missing.
Here is what I have
const error = document.getElementsByClassName('has-form-error')
let focusedInputNodeList = error[0].childNodes
let focusedInput = Array.from(focusedInputNodeList)
This is to check for the most immediate form error. I then want to check the return if it is an input, textarea, or select.
the above code returns the array to console from my html
(6) [text, label, text, div.form-error, input#first_name, text]
Then I try
console.log(focusedInput.includes('input')) //returns false
I figured maybe it was due to the #first_name so I tried using
console.log(focusedInput.includes('text')) //returns false
and
console.log(focusedInput.includes('#text')) //returns false
None of these attempts seemed to work. My goal is to eventually have something that looks like
if (focusedInput.includes('input')) {
focusedInput[4].focus() //even better if I could find a way to select focusedInput.input.focus()
}
if (focusedInput.includes('textarea')) {
focusedInput[5].focus() // even better if I could find a way to select focusedInput.textarea.focus()
}
The problem is that your array focusedInput contains elements and not strings. In the code below I get an additional array of the element types, as strings, and from that you can find what you want.
The other thing I did was to use .children to get all of the non-text elements to reduce what is in the array.
const error = document.querySelector('.has-form-error');
let focusedInputNodeList = error.children;
let focusedInput = Array.from(focusedInputNodeList);
let nodeTypes = focusedInput.map(el=>el.localName);
console.log(focusedInput);
console.log(nodeTypes.indexOf('label'));
console.log(nodeTypes.indexOf('input'));
<div class="has-form-error">
<label>Click me</label>
<div class="form-error">Error</div><input id="first_name" />
</div>
UPDATE
In a comment you said: "even better if I could find a way to select focusedInput.input.focus()"
So why not do this:
const input = document.querySelector('.has-form-error input');
if (input) {
input.focus();
}
<div class="has-form-error">
<label>Click me</label>
<div class="form-error">Error</div><input id="first_name" />
</div>
You cannot just check for includes like that, since it's not an array of strings ['text', 'input', 'etc']. It's an array of node objects, and the name you're looking for is a property of that node, which you can find like focusedInput[5].localName (or .tagName or .nodeName).
So, like:
focusedInput.forEach(item => {
if (item.localName === 'input') {
item.focus()
}
})
Building off of this SO question, I'm trying to pass two variables to a custom validator method. Using console.log I can see that the upper & lower ranges are defined in the HTML, but are not being passed correctly to the options, instead I'm getting NaN instead.
The problem seems to be that the values of the two text boxes are not defined or set yet (when they're sent in the validation rules below), but are when they arrive to the validator (this is just a guess, and I haven't been able to come up with a method to sniff their values prior to the validation attempt). So if I log them inside the validator method, they show up fine, but if I pass them as variables, they show up as NaN inside the PCBID object (Chrome browser):
Object {PCBID: Object}
PCBID: Object
lower: NaN
upper: NaN
__proto__: Object
__proto__: Object
Here's the validator, there are other rules set to prevent anything from integers being entered, so that shouldn't be the problem:
//this validator checks to make sure the user has entered the PCBIDs in
//the correct order in the range form.
$.validator.addMethod("highLowCheck", function (value, element, options)
{
console.log("Inside highLowCheck validator");
console.log(parseInt($('#pcbid_range_lower').val(),10)); //shows expected values
console.log(parseInt($('#pcbid_range_upper').val(),10)); //shows expected values
console.log(options.PCBID.upper); //NaN
console.log(options.PCBID.lower); //NaN
//evaluates to false because NaN is falsey
console.log(options.PCBID.upper > options.PCBID.lower);
console.log("Done logging");
return options.PCBID.upper > options.PCBID.lower;
}
);
Here are the variables I'm trying to pass:
pcbid_range_upper: {
required: true,
digits: true,
rangelength: [3, 6],
highLowCheck:
{ PCBID:
{
//this doesn't work
lower: parseInt($('#pcbid_range_lower').val(),10),
upper: parseInt($('#pcbid_range_upper').val(),10)
}
},
If I pass in primitive values like this:
highLowCheck:
{ PCBID:
{
lower: 1000, //works
upper: 2000
}
},
This method works, but it's not very useful because users can enter any value they like so I have to be able to pass them in as variables. I also need this to work with variables because I need to call it from more than one validation routine, otherwise I'd just use the variables in the validator directly (as I was before the need for more than one form to use the validator).
In case it's useful, here is the HTML for the two inputs:
<div data-role="fieldcontain" data-controltype="textinput" class="pcbid_selections" tabindex="2">
<label for="pcbid_range_lower">Starting PCBID *</label>
<input name="pcbid_range_lower" id="pcbid_range_lower" class="create_group" placeholder="52759" value="" type="text" data-mini="true" />
</div>
<div data-role="fieldcontain" data-controltype="textinput" class="pcbid_selections" tabindex="3">
<label for="pcbid_range_upper">Ending PCBID *</label>
<input name="pcbid_range_upper" id="pcbid_range_upper" class="create_group" placeholder="52769" value="" type="text" data-mini="true">
</div>
The Question:
How can I pass variables inside a rule to a custom validator?
EDIT:
The Solution: with thanks to #Sparky & Mathletics
The validator method was changed to only receive the strings of the names of the two variables I wanted to pass, not their contents. Then using #Mathletic's suggestion, I simply put them into jQuery variable form inside the validator:
//this validator checks to make sure the user has entered the PCBIDs in the
//correct order in the range form.
$.validator.addMethod("highLowCheck", function (value, element, options){
return parseInt($('#' + options.PCBID.upper).val(), 10) >
parseInt($('#' + options.PCBID.lower).val(), 10);
}
);
And called them from the rules like so:
highLowCheck:
{
PCBID:
{
lower: 'pcbid_range_lower',
upper: 'pcbid_range_upper'
}
},
Just an FYI to anyone who finds this, I tried passing in the pound sign ("#") with the strings from the rules (EG: '#pcbid_range_lower'), but that didn't seem to work. This method does, and you can just prepend the "#" in the validator method instead which works well.
Quote OP:
"The Question: How can I pass variables inside a rule to a custom validator?"
Declared within .validate()...
rules: {
myField: {
customMethod: [1, "two", 3, "foo"]
}
}
customMethod defined...
$.validator.addMethod("customMethod", function (value, element, options) {
console.log(options[0]); // <- 1
console.log(options[1]); // <- "two"
console.log(options[2]); // <- 3
console.log($('[name=' + options[3] + ']').val()); // <- value of field named "foo"
console.log(value); // <- the value of "myField"
console.log(element); // <- the "myField" object
// your function
}, "Custom message with options used as {0}, {1}, {2}, {3} etc.");
DEMO: http://jsfiddle.net/pupggbu7/
Documentation: http://jqueryvalidation.org/jQuery.validator.addMethod/
I've searched for this particular topic and couldn't find anything similar to it. If there is please close this and give a link.
I'm creating a json data api simulator. I want users to be able to copy and paste a json object request into a textarea where they can also modify it before sending it to the server.
Problem is json obj copy and patses often results in extra spaces and is never aligned properly, even with the pre tag. I also want a good color scheme applied to keys and values.
I've seen plugins, other questions and snippets of code, but they don't apply to textareas where the text is editable. Is there to keep it styled while in edit mode without it showing all the html tags that styled it? I want to be able to write it from scratch with javascript or jquery.
The syntax highlighting is tough but check out this fiddle for pretty printing a json object entered in a text area. Do note that the JSON has to be valid for this to work. (Use the dev console to catch errors.) Check jsLint for valid json.
The HTML:
<textarea id="myTextArea" cols=50 rows=10></textarea>
<button onclick="prettyPrint()">Pretty Print</button>
The js:
function prettyPrint() {
var ugly = document.getElementById('myTextArea').value;
var obj = JSON.parse(ugly);
var pretty = JSON.stringify(obj, undefined, 4);
document.getElementById('myTextArea').value = pretty;
}
First try simple input like: {"a":"hello","b":123}
Simple pretty printing of JSON can be done rather easily. Try this js code: (jsFiddle here)
// arbitrary js object:
var myJsObj = {a:'foo', 'b':'bar', c:[false,2,null, 'null']};
// using JSON.stringify pretty print capability:
var str = JSON.stringify(myJsObj, undefined, 4);
// display pretty printed object in text area:
document.getElementById('myTextArea').innerHTML = str;
For this HTML:
<textarea id="myTextArea" cols=50 rows=25></textarea>
And check out JSON.stringify documentation.
Late answer but modern one, use the secret intendation parameter.
I usually go for:
JSON.stringify(myData, null, 4);
Here's the code definition, it explains it well.
stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string;
/**
* Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
* #param value A JavaScript value, usually an object or array, to be converted.
* #param replacer An array of strings and numbers that acts as a approved list for selecting the object properties that will be stringified.
* #param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
*/
For the parsing step you're just going to want to JSON.parse the contents of the textarea and handle any errors from bad input.
For the formatting part of your question, Use JSON.stringify(blob, undefined, 2). Alternatively, if you need colors here is a simple JSON format/color component written in React:
const HighlightedJSON = ({ json }: Object) => {
const highlightedJSON = jsonObj =>
Object.keys(jsonObj).map(key => {
const value = jsonObj[key];
let valueType = typeof value;
const isSimpleValue =
["string", "number", "boolean"].includes(valueType) || !value;
if (isSimpleValue && valueType === "object") {
valueType = "null";
}
return (
<div key={key} className="line">
<span className="key">{key}:</span>
{isSimpleValue ? (
<span className={valueType}>{`${value}`}</span>
) : (
highlightedJSON(value)
)}
</div>
);
});
return <div className="json">{highlightedJSON(json)}</div>;
};
See it working in this CodePen:
https://codepen.io/benshope/pen/BxVpjo
Hope that helps!
If you are a jquery fan, you can also use this small plugin I wrote:
// The plugin
$.fn.json_beautify= function() {
this.each(function(){
var el = $(this),
obj = JSON.parse(el.val()),
pretty = JSON.stringify(obj, undefined, 4);
el.val(pretty);
});
};
// Then use it like this on any textarea
$('textarea').json_beautify();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<textarea id="myTextArea" cols=50 rows=5>{"name":"John","age":30}</textarea>
<textarea id="myTextArea2" cols=50 rows=5>{"name":"Bob","age":55}</textarea>
UPD
Changed code to multiselected elements.
I don't think that can be done with regular textareas. What you can do (and how most online code editors do it) is to create a transparent textarea that overlays on top of a div that contains the styled code. The user would still be able to type and interact with the input (and it fires the associated form events), and you can show syntax highlighting in the div that the user will visually see
(see Textarea that can do syntax highlighting on the fly?)
Now as for JSON formatting, I would add custom events to the textarea so that when a user types or paste something, run it through a Javascript JSON prettifier (see How can I pretty-print JSON using JavaScript?) and then re-populate the div and textarea accordingly
Here's a recursive function to return an object if it has been stringified multiple times:
const jsonPrettify = (json) => {
if (typeof json === 'object' && json !== null) {
const pretty = JSON.stringify(json, undefined, 4);
return pretty;
}
try {
const obj = JSON.parse(json);
return jsonPrettify(obj);
} catch (e) {
return json;
}
};
in an html page i have got, i have a select box like this with values.
<select onChange="return filler(this.value);">
<option value="{'name':'rajiv',age:'40'}">a</option>
<option value="{'name':'mithun',age:'22'}">f</option>
</select>
i want to pass a javascript array or object as the option value.
right now it is treating the option value as a string.
i want it to be an array so that i can access it by
this.value.name,this.value.age in the filler function.
is this possible?
You will not be able to store objects/arrays in the value attribute, however an option would be to use data-* attributes which supports json automatically with jQuery 1.4.3+
<select>
<option data-value='{"name":"rajiv","age":"40"}'>a</option>
<option data-value='{"name":"mithun","age":"22"}'>f</option>
</select>
And using .change()
$("select").change(function(){
alert($(this).find(":selected").data("value").age);
});
Example on jsfiddle
No, not just like that. Values have to be strings. I'd strongly recommend to use something like jQuerys .data() method to hold Arrays or Objects in an expando property.
If it must be in the value, you just need to JSON decode (.parse) it:
var myValue = JSON.parse(this.value);
myValue.age; // 40
myValue.name // rajiv
But again, I don't think this is a good solution. Have a look at http://api.jquery.com/jQuery.data/
Also, jQuery will automatically convert Arrays and Objects if you put the JSON strings in any data- HTML5 attribute. For instance:
<option value="A" data-info="{'name':'rajiv',age:'40'}">something</option>
If you access that node with jQuery now, we automatically got that object in it's data expando
$('option').data('info').name; // === rajiv
You can use parseJSON to convert the string to an object when using it, but the value needs to be a string.
var option = $('select').val();
var selected = $.parseJSON(option);
alert( selected.name + ': ' + selected.age );
React solution
const myObjects = [
{
text: 'a',
value: 1
},
{
text: 'b',
value: 2
},
];
const handleSelectChange = (myStringifyObject) => {
const myObject = JSON.parse(myStringifyObject);
// logic with myObject
}
<input type='select' onChange={(evt) => handleSelectChange(evt.target.value)}>
{myObjects.map((myObject) => (
<option
value={JSON.stringify(myObject)}
key={myObject.value}
>
{myObject.text}
</option>
))}
</input>