Component not rendering within .map forEach - javascript

I'm having trouble getting a component to render within .map
render: function() {
var self = this;
var ListItems = this.props.data.map(function(data){
self.props.fields.forEach(function(field, i) {
if (field.fieldKey in data) {
console.info(field.fieldKey + ': ' + data[field.fieldKey]);
return (<ListItem {...data} key={'field-' + i}/>)
} else {
console.error(field.fieldKey + " doesn't exist. Please make sure you match your 'fieldKey' to an existing column in your data table");
}
});
});
return <tr onDoubleClick={this.handleEditRow} onClick={this.handleSelectRow}>
{ListItems}
<td className="text-center">
<span className="btn"><input type="checkbox" ref="deleteCheckbox" checked={this.props.checked} onChange={this.handleDeleteChange}/></span>
<a className="btn" onClick={this.handleDeleteRow} title="Delete this Item"><i className="md md-close"></i></a>
</td>
</tr>
},
So my ListItem doesn't show at all. If I move it under the first loop, it shows fine. Can anyone tell me what I'm doing wrong?
UPDATES
JSON DATA
http://pastebin.com/MLbR77tG
So, I'm creating a list view. Basically, each item under data has a fields setting attached via the fieldKey
So the plan is to spit out the data, but use the configuration options under fields to format the list view. i.e. fieldKey is a dropdown, it's dataSource is message yada yada.
Name(data: test 1) (fields: is dropdown) | calling_gt(data: 123456) (fields: is text) | op_code (data: 5678) (fields: is dropdown)

Check out Array.prototype.forEach() - MDN
forEach() executes the callback function once for each array element; unlike map() or reduce() it always returns the value undefined and is not chainable. The typical use case is to execute side effects at the end of a chain.
For example, check out this jsfiddle. When you return a value within forEach() method, the value will not be received by the outer function, in this case, map loop. Instead, the outer loop will always get a undefined from the forEach() loop.

Your return statement is inside the call to forEach meaning it can't affect the outer map statement.
This means that the map function is returning undefined each time, which is why the component doesn't render.
Instead, you can write a predicate function that you can use to filter out rows that aren't used.
hasValidFieldKeys: function(fields, data) {
var invalidFields = fields.filter(function(field) {
var hasKey = field.fieldKey in data;
if(!hasKey) {
// log error
}
return !hasKey;
});
return invalidFields.length > 0;
}
Then use the predicate function inside component's render method.
var ListItems = this.props.data
.filter(function(data) {
// remove the rows that aren't valid
return self.hasValidFieldKeys(self.props.fields, data);
})
.map(function(data, index) {
return data.map(function(data, index2) {
var compositeKey = 'field-' + index + ':' + index2;
return <ListItem {...data} key={compositeKey}/>;
});
});
If it's important that your app stops if there is data missing certain field keys, then throw errors, rather than using console.error.

Related

Cypress - have to use forEach loop with array returned by function in describe block

I have a function which returns array
getOffersCategories() {
cy.get(offersHeaders).then($els => {
cy.wrap(Cypress._.map(Cypress.$.makeArray($els), 'innerText')).as("categories")
})
return this;
}
Now I need to call this function and use array with forEach block having multiple test cases (something like this)
offersCategories.forEach((item, index) => {
it("Validate Max Carousels Limit, Left Right Swipe, See All link of " + item, function ()
{
...
}
})
For me it works fine if I array value is declared in same spec file Like var offersCategories = ["a", "b",c"] or fetched from fixtures, but not finding any way to use the same functionality when array details fetched from function.
Thanks in advance :)

Petite-Vue filtered array reactivity?

I played arround with Petite-Vue and really like it. Works fine with reactivity / ui updates and helpers like "$el" and "$refs" for easy form field handling / data binding.
Now I tried to have a reactive list with updates live if backend data (gundb) changes.
UI updates work fine if I list all array entries with "v-for".
If I try to filter the array of players by one team (object property "team") the UI is initialy correct rendered, but if a players name changed the update is pushed to gundb and by listener also to players array based on PetiteVue.reative(). So all is fine, but no UI update is triggered. Item is a Proxy as needed for (petite-)vue reactivity.
So it looks like a players.filter(...) call break reactivity. Also tried a method and getter inside of a component and also directly by write players.filter(...) to the html template.
Do I missing the right way how to trigger a re-render of changed filtered reactive array of objects?
I have a function to sync gundb "backend" changes to Petite-Vue.reactive object
gunArray2vue = (gunNode, vueArray) => {
gunNode.map().on((value, soul) => {
item = vueArray.find(item => item && item._ && item._['#'] === soul)
console.log('gunArray2vue', `soul=${soul}`, value, item)
if(!!item && value === null) {
// item in VUE but seems deleted... remove from vue array...
console.log("ToDeleteItem", item)
vueArray.splice(vueArray.indexOf(item), 1)
} else if(!value) {
console.log("Ignore creation of empty object")
return // don't add empty object...
} else if(item) {
//vueArray[vueArray.indexOf(item)] = value // update
console.log("UpdateItem")
item = value // update
} else {
console.log("AddArrayItem")
vueArray.push(value) // add new
}
})
}
Changed Value checked with console:
checked change in js console
But UI isn't refreshed.
Here the code in componente
tested as getter / function
//filteredPlayers: function() {
get filteredPlayers() {
if(this.form.nr || this.form.name) {
return this.players.filter(player =>
player.name.includes(this.form.name) || player.nr == this.form.nr
)
} else {
return this.players
}
}
The filtering is reactive to my input field! But changed name is ignored.
And here is a simpler getter function
get myPlayers() {
page.players
}
But ui isn't updated if an item changes without refresh.
So filtering works reactive, but changed items are not detected and re-rendered.
Also tried directly to use the unfiltered PetiteVue.reactive object like that.
<div v-for="player in page.players">{{player.nr}} {{player.name}}</div>
Rendering works fine, but also not updated on change.
Update
Noticed during my tests today, that updates work if I directly change my original object page.players, but not / no more if I use a reference to the original reactive object?
So I can't use the reactive object by reference without loose ui reactivity for item changes?
Update2
Add and remove an reactive array entry works fine!
Just update an item won't work. Tested different ways to set the item object / key -> val of object. Change isn't reactive.
Function with my tests
gun2arrayListener = (name, table) => {
console.log(`Register Listener Gun table "${name}"`)
DB.get(name).map().on((value, soul) => {
console.log("DEBUG", soul, value)
item = table.find(item => item?._['#'] === soul)
index = table.indexOf(item)
console.log('gun2array', `soul=${soul}`, value, item)
if(Object.is(item, value)) {
console.log("Skip unchanged item")
} else if(item && value === null) {
console.log("ToDeleteItem", item)
table.splice(index, 1)
} else if(!value) {
console.log("Ignore creation of empty object?!")
} else if(item) {
console.log("UPDATE", "ARRAY", item, value)
// !!! CHANGES NOT REACTIVE HERE !!!
// !!! method is triggered, changes looks good, but reload page needed to update UI !!!
//item = value
//TABLES[name][index] = value
//Object.assign(item, value)
//table.splice(index, 1, value)
//table.splice(index, 1); table.push(value)
for (const [key, val] of Object.entries(value)) {
console.log(`${key}: ${val}`)
//item[key] = val
store.players[index][key] = val
console.log(item)
}
} else {
console.log("AddNewGunNodeItem", value)
table.push(value) // add new
}
})
}
Strange... if I test reactivity from outside it works ?!
store.players[0].name = "BUM!" // works as needed in function... ?!
I noticed gun fires the listener twice, but that should result in correct updated array object, just changed to the new value twice...
I can't prevent that, because can't compare old object (vue proxy object) with the new object (plain object without vue proxy) to skip unchanged object here. Object.is results in false because of the Proxy arround...

How to use a constant in map return function ReactJS

I am wondering how can I use a constant in the map function, basically meaning: I have saved correctly the option I want from my falling menu regarding the constant (I checked it with console.log), for instance I have a name chosen and then I want to use it in the map function but unfortunately I get all the elements undefined when I use the constant; when I replace the constant with a directly written "name", I get all the elements correctly with their names.
Filterhosts=() =>{
var newState = this.state.array.slice(); // in the state array is empty
const selectedOption = this.state.selectedOption;
const writtenOption = this.state.writtenOption;
console.log(selectedOption) //ok
const namearray= this.state.filteredhosts.map(host=> {
return (
host.software.map((sub, subindex) => {
if(selectedOption=="name" || selectedOption=="vendor") {
newState.push(sub.selectedOption) //when I write sub.selectedOption , I receive empty array with all elements as undefined otherwise I become the names of all elements
}
else {
if(sub.vulnerable==true){
newState.push(sub.vulnerability.cve)}
}
})
)
})
const filteredarray = newState.filter( function(item){
return item === writtenOption // here I become properly the searched name//vendor
}
// how to show the whole info for the searched name/vendor(cpe, cve, cvss etc.)
)
console.log(newState); //ok
console.log(filteredarray); //ok
}
Oh I see.
sub.name
is the same as
sub["name"]
which is also the same as
sub[selectedOption]
IF selectedOption is "name". So just use newState.push(sub[selectedOption]) and I think that should work for you.

JSX skip if JSON field does not exist

I am currently working with a JSON file from an API that does not add a key pair if the field is null. This is causing me grief when trying to iterate through the entire JSON file.
My code currently is
var ListOfItems = React.createClass({
render: function () {
var itemList = jsonFile.data.map(function(item)
{
return <Item key={item.__key}
itemQuestion={item.question}
itemQuestionAnswer={item.answer.answer}
userName={item.user.name}
staffName={item.staff.name}
staffImage={item.staff.image_url} />
});
return (
<div>
<ul>{itemList}</ul>
</div>
);
}
});
Which gives an error for when item.answer.answer has no value.
Any help will be greatly appreciated.
You can do that by adding a condition to check, whether the item's answer type is not undefined. If it's not, proceed with returning a value, else don't return anything. This way, you only append another item if the condition has passed (I used shorthand for the condition).
var ListOfItems = React.createClass({
render: function() {
var itemList = jsonFile.data.map(function(item)
{
typoeof item.answer != 'undefined' &&
return <Item key={item.__key} itemQuestion={item.question}
itemQuestionAnswer={item.answer.answer} userName={item.user.name}
staffName={item.staff.name} staffImage={item.staff.image_url} />
});
return (
<div>
<ul>{itemList}</ul>
</div>
);
}
});
If you always get item.answer but it's answer is either undefined or null, you can check for item.answer.answer in the code I've provided instead.
Depending on how big your list of items is, you could use the builtin filter Array method to first remove all the items you don't want, and then proceed to map through them. Keep in mind this will potentially go through your entire list twice.
A note about returning undefined from inside map. This will not prevent an item from getting returned. You will instead have an undefined item in your resulting array. The array will not be shorter.
Here's an example with filter():
var ListOfItems = React.createClass({
renderItems: function() {
return jsonFile.data
.filter(function(item) {
// This will return any item that has a truthy answer
return item.answer && item.answer.answer;
})
.map(function(item) {
return (
<Item
key={item.__key}
itemQuestion={item.question}
itemQuestionAnswer={item.answer.answer}
userName={item.user.name}
staffName={item.staff.name}
staffImage={item.staff.image_url} />
);
});
},
render: function() {
return (
<div>
<ul>
{this.renderItems()}
</ul>
</div>
);
}
});

Unexpected modify of non primitive values in loop

I'm working with an angular service of the type:
services.factory('SaveHistory', function($rootScope, $localForage){
return {
videoAccessed: function(idPillola) {
$localForage.getItem('trainings_user_'+$rootScope.user.id)
.then(function(succ, err) {
for (var item in succ) {
[].forEach.call(succ[item], function(el, index) {
el.pillole.forEach(function(el, index){
if (el.idPercorso == idPillola) {
console.log(idPillola);
el.tracking.completion_status = 1;
}
});
});
}
var newTrainings = succ;
...
});
}
When the function is fired with the correct idPillola , console.log logs the correct idPillola value one single time, so it seems that the cycle works correctly. But : if the attribute in the object (object or rather 'el' in the nested forEach cycle) that i want to change is a primitive , there are no problems, if the attribute is not primitive but an another object attribute, like tracking.completion_status in this case, all elements are updated ! (Like the if control had been ignored).
It is related to Angular or Javascript itself?

Categories

Resources