How to get value of "bind:value" element? - javascript

Is there some easy way to get value of bind:value element in svelte?
E.g. this is the html that I have:
<input type="text" name="prop" bind:value="{model.subObject.subSubObject.subSubSubProp}">
<span class="error" data-error-for="prop"></span>
And js that I currently have:
let fields : NodeList = document.querySelectorAll('[data-error-for]');
fields.forEach((v,k) => {
let nameOf = v.dataset['errorFor'];
let n = document.getElementsByName(nameOf);
//how to get here the value of `n.value` to be `model.subObject.subSubObject.subSubSubProp`
});
I do not need the real bound value, but actually the object path (e.g. above it would be model.subObject.subSubObject.subSubSubProp).

Related

Adding an array of properties to an object

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]

how can i make my function reusable whilst using the (e) parameter?

const days = document.querySelector('#days');
const hours = document.querySelector('#hours');
const minutes = document.querySelector('#minutes');
document.querySelector('#years').addEventListener('input', function (e) {
let years = e.target.value;
days.lastElementChild.innerHTML = years * 365;
hours.lastElementChild.innerHTML = years * 8760;
minutes.lastElementChild.innerHTML = years * 525600;
});
I really need help with this one. When using the e parameter i can target the value from when the event is triggered using this code. I get that i can call the parameter anything and use target to access all the juicy information about the event in the console. What i do nott understand is , why cant i pass arguments with the e to make my function reusable. I want to pass my variables via arguments and store placeholders as parameters and work against them. Instead if I store (e) as a parameter, unless i am missing something , I am forced to reference my variables inside my function because I cannot seem to pass other arguments with (e) . Is there a way i can e.target.value and still pass arguments to my function? This one has really got me stuck , thanks
One method is to record the id attribute values for output parent elements as a data attribute on the input element used to enter a year. Another method could involve setting up a table of parent elements using the id of the input as key.
A third option is to add the event listener (which is passed an event object as argument) inside a closure: i.e. inside an outer, reusable function which is called with all necessary argument values to add an event listener to a specific elements and handle events that are raised.
Here's an example of the first approach:
"use strict";
function showDHM(event) {
let years = event.target.value;
event.target.dataset.for.trim().split(/\s*,\s*/)
.map( id => document.getElementById( id))
.forEach( (element, index) => {
const multiplicand = [ 365, 8760, 525600];
element.lastElementChild.innerHTML = years * multiplicand[index];
});
}
const year1 = document.querySelector('#year1')
year1.dataset.for = "days1, hours1, mins1";
year1.addEventListener('input', showDHM);
<label> Year: <input type="number" id="year1"></lable>
<p>
<span id="days1"><span>days</span></span>,
<span id="hours1"><span>hours</span></span>,
<span id="mins1"><span>mins</span></span>
Note the code is for demonstration only: adjustment for leap years not included!
The next snippet demonstrates the closure option. The conversion process used to modify the posted code was largely mechanistic: replace hard-coded values with argument names and include the modified code in an outer function.
"use strict";
function handleYears( yearsSel, daysSel, hoursSel, minutesSel) {
const days = document.querySelector(daysSel);
const hours = document.querySelector(hoursSel);
const minutes = document.querySelector( minutesSel);
document.querySelector(yearsSel).addEventListener('input', function (e) {
let years = e.target.value;
days.lastElementChild.innerHTML = years * 365;
hours.lastElementChild.innerHTML = years * 8760;
minutes.lastElementChild.innerHTML = years * 525600;
});
}
handleYears("#year1", "#days1", "#hours1", "#mins1");
<label> Year: <input type="number" id="year1"></lable>
<p>
<span id="days1"><span>days</span></span>,
<span id="hours1"><span>hours</span></span>,
<span id="mins1"><span>mins</span></span>

Getting the value from <input> element in typescript

I'm currently trying to get the values a user would insert into an input form. In vanilla javascript, I can just target the element by id or class, etc, and then I can just use the .value method to actually get in use that method. For some reason, typescript cannot do that, which I do not understand because typescript is a superset of javascript. Is there a specific way to get a value from an input element in pure typescript or do I have to use angular or something?
Typescript code:
interface IUserFoodOptions {
food: string;
calories: number;
foodDate: any;
}
class Food implements IUserFoodOptions {
food: string;
calories: number;
foodDate: any;
// store all the calories in an array and calculate the total amount of calories
caloriesArray: number[] = [];
// constructor
constructor(food: string, calories: number, foodDate: any) {
this.food = food;
this.calories = calories;
this.foodDate = foodDate;
}
}
// event listener for when the add food button is clicked
let addFood = document.getElementById("add-food-button").addEventListener("click", () => {
// get the values from inputs and store them in an array
let foodName = document.getElementById("food-name-val");
let foodCalories = document.getElementById("calories-val");
let dateVal = document.getElementById("date-val");
// store these values in a list and display them below
// user will have the ability to edit and delete them
// am I create event listeners within event listeners
});
If you are using an editor like VSCode to write Typescript, I've found the ability to inspect code very valuable in learning more about what's occurring in the typing system. In VSCode you can right click on the method(or type) and choose Go to definition.
Inspecting the method in your question, getElementById, you can see it returns an HTMLElement. This type doesn't have a value property on it. This makes sense as getElementById can return any HTMLElement on the page as long as it has an id attribute. Not every HTMLElement though has a value property(for instance a div/span/p, etc).
Since you know what type you are expecting, but the type system can't, to get this to work, you have to tell Typescript what type of element you expect to be selecting. You would do that through casting the type of the selected element as follows:
const inputElement = <HTMLInputElement> document.getElementById("food-name-val");
or
const inputElement = document.getElementById("food-name-val") as HTMLInputElement;
Now, since Typescript recognizes the selected element as an HTMLInputElement, it won't error when you access the value property on it.
In your case that would look like:
let foodName = (document.getElementById("food-name-val") as HTMLInputElement).value;
Yeah, TypeScript has this "little issue", but it is for safety.
You can get the input value doing something like this:
var inputValue = (<HTMLInputElement>document.getElementById(elementId)).value;
You can see more about this casting <> thing here: TypeScript: casting HTMLElement
Hope it works!
You can get the value of an input in TypeScript.
For a number,
var num = parseFloat((<HTMLInputElement>document.getElementById("myValue")).value);
or
let num : number = parseFloat((<HTMLInputElement>document.getElementById("myValue")).value);
Strings;
var str = (<HTMLInputElement>document.getElementById("myUnit")).value;
or
let str : string = (<HTMLInputElement>document.getElementById("myUnit")).value;
It's important to cast HTMLElement to HTMLInputElement, otherwise 'value' property doesn't exist for HTMLElement in TypeScript and TypeScript compiler will show an error.
// event listener for when the add food button is clicked
let addFood = document.getElementById("add-food-button").addEventListener("click", () => {
// get the values from inputs and store them in an array
let foodName = (<HTMLInputElement>document.getElementById("food-name-val")).value;
let foodCalories = parseFloat((<HTMLInputElement>document.getElementById("calories-val")).value);
let dateVal = (<HTMLInputElement>document.getElementById("date-val")).value;
// And so on ...
});
I find the following code more readable:
const user: string = document.querySelector<HTMLInputElement>('input[name="user"]').value;

Es6 how to check if an item exists in an array built from a nodeList using includes()

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()
}
})

Can I post JSON without using AJAX?

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.

Categories

Resources