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]
Related
I have an array of objects called 'Tags' of type :
type Tag = {
id: number,
label: string
}
Below is some sample data inside it.
const [tags, setTags] = useState<Tag[]>([{id: 1, label: "random"}, {id: 2, label: "important"}, {id: 3, label: "ignore"}])
I have an input field which takes input and sets it to "input" state on change.
I want to display a paragraph element only if the searched input field doesn't exist inside the tags.
I was unable to find a way to directly search it as the tags array is made of objects and i want to search that object's label property.
So i ended up doing something like this...
{tags.map(tag => {
if(!(tag.label.toLowerCase().includes(input.toLowerCase()))){
return <p>{input}</p>
}
})}
but this would render the paragraph each time the input doesn't match the values. So in this case it renders the paragraph 3 times if i add a new label. I want it to render just once. How do i fix this?
You could use filter to filter tags and then if result has a length > 0 show <p>{input}</p> one time:
return (
{tags.filter(tag => !(tag.label.toLowerCase().includes(input.toLowerCase())).length > 0 &&
<p>{input}</p>
})});
To render the input in a p tag when none of the labels includes it:
const Bla = () => { // assuming Bla is your component
const shouldRenderInput = tags.every(
(tag) => !tag.label.toLowerCase().includes(input.toLowerCase())
);
return <>{shouldRenderInput && <p>{input}</p>}</>;
};
see Array.prototype.every
I am working with react app in typescript. From API, I have this input:
a) list of variable names ["name", "surname"]
b) few strings in form of simple html with variables "<p>Hello, how are you {name}?</p>"
c) number of inputs with variables such as {input1: "name"}
everything as a string/JSON
what i need to do is: render simple html (only few tags) received from API but "create" binding between those dynamic inputs and variables in strings
in static world, result would look like:
[name, setName] = useState("")
<p>Hello, how are you {name}?</p>
<input type="text" onChange={e => setName(e.target.value)}/>
However, all of this is dynamic. String "<p>Hello, how are you {name}?</p>" doesnt get binded to the input on its own.
I tried:
setting variable [vars, setVars] = useState({}), property for each dynamic variable, with
a) dangerouslySetInnerHTML - renders only html (cannot bind the variable inside to the input)
b) react-html-parser - same as above
c) babel.transform - couldnt make it work as this is done dynamically and in browser, it cannot find the right preset, i couldnt make the mimified babel.js work with typescript How to render a string with JSX in React
do you see some easy way? For example how could i use React.createElement to render html with "live" variable inside, represented as {variableName}? Or maybe something out of the box? giving each elemnt a class and finding the class in DOM and editing the text with input change would be probably very non-optimal?
I hope this could be a better example:
{response:
{
variables: ["name", "name2", "mood"],
texts: [
"<em> Hello! My name is {name}</em>",
"<p> Hi {name} ! I am <strong>{name2}</strong> and I feel {mood} today</p>"
],
inputs: [
{
label: "How do i feel?"
input: {mood}
}
]
}
}
EDIT:
This should give you a good idea:
https://stackblitz.com/edit/react-ts-cwm9ay?file=DynamicComponent.tsx
EDIT #2:
I'm pretty good with React and interpolation, it's still in progress (specifically the docs, but the readme is complete) but I'm going to shamelessly plug my ReactAST library
EDIT #3 - If you're interested in doing crazy dynamic interpolation, then you might also want to check out a neat dynamic interpolation (and it's reverse) library
Let's assume this:
{
vars: ['name','surname'],
strings: ["<p>Hello, how are you {name}?</p>","<p> It's night to meet you {name} {surname}"],
inputs: {
input1: 'name',
input2: 'surname'
}
}
Create a component (or set of components) that can do this.
I haven't even ran any of this, but here's the idea. I'll put it in a stackblitz in a bit and polish it up to make sure it works:
const MyDynamicComponent = ({vars,strings,inputs}) => {
const [state,setState] = useState(vars.reduce((acc,var) => ({...acc,[var]:undefined}));
return (
<>
<MyDynamicText vars={vars} strings={strings} state={state}/>
<MyDynamicInputs onChange={(name,val) => setState({[name]:val}) state={state} inputs={inputs}/>
</>
)
}
const MyDynamicText = ({vars,strings,state}) => {
return strings.map((s,i) => s.replaceAll(`{${vars[i]}}`,state[vars[i]])
}
const MyDynamicInputs = ({onChange,inputs,state}) => {
return Object.entries(inputs).map(([inputName,varName]) => <input key={inputName} onChange={e => onChange(varName,e.target.value)} value={state[varName]}/>
}
I am developing angular blockchain application using hyperledger composer tool.When i query the historian i got a response like in the below.
{
transactionType:"org.hyperledger.composer.system.AddParticipant"
}
I display the transaction type using follwing code snippet.
<div class="col-md-6">
{{participant.transactionType}}
</div>
The displayed part like this.
org.hyperledger.composer.system.AddParticipant
but I only want to display the 'AddParticipant' part in the response without 'org.hyperledger.composer.system.' part. How can I fix it?
For that just do little string manipulation. Make use of JS .split() method which splits string by argument character/string.
let arr = this.participant.transactionType.split(".");
then arr[arr.length-1] is your required string part which you can bind to view. Like use below {{txTyepe}} in template binding
this.txType = arr[arr.length-1];
you can use "substr" to pick a word from string but you need position of your word in your string first so :
const str = 'org.hyperledger.composer.system.AddParticipant'
let getPosition = str.indexOf('AddParticipant'); // get the position of AddParticipant
let getWord = str.substr(getPosition,13);
the length of AddParticipant is 13 also you can change the code above for better and cleaner and multi use code
const splitWord = (index)=>{
const str = 'org.hyperledger.composer.system.AddParticipant'
let strArray = str.split('.')
let getPosition = str.indexOf('AddParticipant'); // get the position of AddParticipant
let getWord = str.substr(getPosition,strArray[index].lenght); //you just need to change the number
return getWord;
}
console.log(splitWord(4));
You can also get the last "word" with regular expression :
<div class="col-md-6">
{{participant.transactionType.match(/\w+$/i)}}
</div>
When you see your historian data it'll look something like this
'$namespace': 'org.hyperledger.composer.system',
'$type': 'HistorianRecord',
'$identifier': '6e43b959c39bdd0c15fe45587a8dc866f1fa854d9fea8498536e84b45e281b31',
'$validator': ResourceValidator { options: {} },
transactionId: '6e43b959c39bdd0c15fe45587a8dc866f1fa854d9fea8498536e84b45e281b31',
transactionType: 'org.hyperledger.composer.system.IssueIdentity',
transactionInvoked:
Relationship {
'$modelManager': [Object],
'$classDeclaration': [Object],
'$namespace': 'org.hyperledger.composer.system',
'$type': 'IssueIdentity',
'$identifier': '6e43b959c39bdd0c15fe45587a8dc866f1fa854d9fea8498536e84b45e281b31',
'$class': 'Relationship' },
So, instead of taking transactionType you can use the transactionInvoked object. And then you can get whatever information you want from that object.
Finally your code should be like this
<div class="col-md-6">
{{participant.transactionInvoked.$type}}
</div>
In my case it will give me transaction type as just 'IssueIdentity'.
I am building a web application using JavaScript and firebase.
everytime I click on the "remove" paragraph tag, I can only remove the most recent added item, but cannot remove the rest.
Example: if I have 9 items, and I just added a 10th item, I can only remove the 10th item, but not the other 9 items. I basically can only remove items in a backwards order, as opposed to being able to remove items in any order of my choice.
Here is my code:
function displayFav(){
const dbRef = firebase.database().ref();
dbRef.on("value", (firebaseData) => {
// empty out element before adding new updated firebase data so there are no repeated data
document.getElementById("displayUsername").innerHTML = "";
let accounts = [];
const accountData = firebaseData.val();
for (let itemKey in accountData) {
accountData[itemKey].key = itemKey;
accounts.push(accountData[itemKey])
const key = accountData[itemKey]["key"];
const password = accountData[itemKey]["password"];
let user = accountData[itemKey]["username"];
// code where I try to render results from page
document.getElementById('displayUsername').innerHTML += `
<li> Username: ${user} Password: ${password}</li>
<p id=${key}>Remove</p>
`;
// code where I try to remove the item
document.getElementById(key).addEventListener("click", function(){
removeItem(key)
})
}
});
}
This is my function to remove the firebase data:
function removeItem(itemToRemove){
const dbRef = firebase.database().ref(`${itemToRemove}`);
dbRef.remove();
};
What can I change or add to my code that will allow me to remove items in any order I want, instead of letting me delete only the most recent items?
Not trying to be rude, this is just a tip: Please indent/format your code well so that people can understand it without having to format it themselves. Usually when people see large code that's not well formatted, it throws them off (sometimes including me), and therefore they wouldn't want to continue to read or help you with the question.
Suppose you have a table called posts, and the structure looks like this:
that is:
{
<postId>: {
date: 1518925839059,
message: 'some message'
},
<postId>: {
date: 151892583967,
message: 'some message'
},
...
...
}
Suppose you want to delete the extra property of the first post, as circled in the picture, you must know the full path of the data:
firebase.database().ref('posts/-L5b1EZ1L_FycluzTIn8/extra').remove();
If you want to delete everything in post 1:
firebase.database().ref('posts/-L5b1EZ1L_FycluzTIn8').remove();
Or, if you want to delete every single post:
firebase.database().ref('posts').remove();
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.