Something that has started appearing in my code quite a bit is:
<Parent>
{if (condition) {
<Child />;
} else {
<div />;
}}
<Sibling />
</Parent>;
Basically I only want the Child to render if the condition is true, otherwise render nothing.
It feels wrong putting the div in the else condition, since this renders a div that really shouldn't be there. How can I conditionally render a component without having to render an unnecessary element if the condition is false?
You have to use React.null instead of the empty div.
ReasonML forces you to have the same type in the if and else blocks, and as you return a React.element in the if block, you also need to return one in the else block.
You can however use ternary, if you want your code to be a bit shorter:
<Parent>
{condition ? <Child /> : React.null}
<Sibling />
</Parent>;
Related
Recently React Admin input components started to add extra space below in order to display helperText (if provided). But it seems that it is impossible to avoid this behaviour by added helperText={false} as it is recommended, e.g.:
<TextInput source="myField" helperText={false} />
It always displays some extra space no matter if I pass "false" as value to the heplerText prop or not.
I'm on React Admin version 3.2.3 now.
In RA's TextInput component we can see this code:
helperText={ // <-- goes to MUI TextField
<InputHelperText
touched={touched}
error={error}
helperText={helperText} // <-- goes to RA InputHelperText
/>
}
If the value of InputHelperText component's helperText prop is equal to false then InputHelperText renders nothing (returns null in its render function).
But the value of heplerText prop that goes to underlying MaterialUI TextField is never null nor undefined, even if I pass "false": it is always an InputHelperText component, which just may or may not render something.
MaterialUI TextField component, in turn, analyses its heplerText prop:
const helperTextId = helperText && id ? `${id}-helper-text` : undefined; // <-- helperText from TextInput
...
{helperText && (
<FormHelperText id={helperTextId} {...FormHelperTextProps}>
{helperText}
</FormHelperText>
)}
Since helperText is never null nor undefined, it will always render FormHelperText, perhaps with an empty helperText.
If I change the RA TextInput component's code like that:
helperText={helperText && touched && error ?
<InputHelperText
touched={touched}
error={error}
helperText={helperText}
/> : null
everything works fine: no extra space is added since the value of helperText (passed to the underlying MUI TextField) is indeed null when the value of TextInput component's helperText prop is equal to false.
Am I missing something or it is indeed a bug?
This is a feature:
https://github.com/marmelab/react-admin/pull/4364
A questionable feature.
As of react-admin v4.3(+), passing a props helperText={false} to <TextInput/> does remove the extra space.
Example:
<TextInput source="meta.og.description" helperText={false} />
Background:
I wish to execute work based off whether a node is content editable or not. The node is captured onkeyup through event.target. Consider this complex contenteditable node structure:
<div contenteditable="true">
<div contenteditable="true">
<ol>
<li>
<a href="google.com">
<span style="color: red;">Hi!</span>
</a>
</li>
</ol>
"A text node"
<p>Some more text</p>
</div>
</div>
I wish a keyup inside the span node, as well as the text node, to detect them as a contenteditable node and perform work.
So, I wrote this function (long but straightforward):
// callForParent: flag to prevent infinite recursion
window.isContentEditable = function(node, callForParent){
var tgN = node && node.tagName,
attr, parentCount, parent, MAX_PARENTS_CHECKED = INTEGER_VALUE (3,4,5,etc.);
// insanity checks first
if(!node || tgN === "TEXTAREA" || tgN === "INPUT" || !node.getAttribute)
return false;
else{
attr = node.attr("contenteditable");
// empty string to support <element contenteditable> markup
if(attr === "" || attr === "true" || attr === "plaintext-only")
return true;
// avoid infinite recursion
if(callForParent) return false;
parentCount = 1;
parent = node;
do{
parent = parent.parentNode;
parentCount++;
if(!parent) return false;
// parent check call
if(isContentEditable(parent, true)) return true;
}while(parentCount <= MAX_PARENTS_CHECKED);
return false;
}
};
Problem:
Notice the counter limit MAX_PARENTS_CHECKED above. That makes sure how many levels up the DOM tree I go in search of a parent contenteditable node.
Does that counter affect the logical correctness of my program? I mean, like in the HTML structure I showed above, we need the limit to be 4 to cover all cases. There might be need of more.
But I fear that increasing the limit greater than some point might make non-contenteditable nodes to be detected to be detected as contenteditable. Is this fear of mine true/possible? Are there examples to show that deep descendants of contenteditable node can be non-CE node?
I don't want to execute things in a deep-down non-CE node if say, it CANNOT be edited by the user and then my app gives an error.
Sorry if I sound nitpicky but I need confirmation
UPDATE: As confirmed by user3297291 in the comments, there exist instances where keyup on a non-CE child of a CE node are detected as having event.target == to the parent and NOT the child, contrary to what one might expect. These are situations where my app might fire, and expect a caret Range, Selection, etc. to perform some work, and there it will give an error.
Does there exist some fix/proper way of doing this?
I am fairly new to Javascript and have a basic question. I have an HTML form with first_name and last_name input fields. I have the following Javascript code in the header but after the code runs, the focus goes to the next field (last_name). Why is that and how do I correct it?
Thank you.
<script>
function validateForm()
{
valid = true;
//validate first name
if (document.contactform.first_name.value == "")
{
//alert user first name is blank
alert("You must enter a first name");
document.getElementById("first_name").focus();
return false;
}
return valid;
}
</script>
and the form field code is:
input type="text" name="first_name" id="first_name" maxlength="50" size="30" onBlur="validateForm()"
A fix for this is to add a slight delay.. like so:
setTimeout(function() {
document.getElementById('first_name').focus()
}, 10);
Here is your example with this fix in jsfiddle: http://jsfiddle.net/FgHrg/1/
It seems to be a common Firefox problem.. I don't know exactly why but it has something to do with Firefox loading the javascript before the DOM is fully loaded.. in otherwords getElementById('first_name') returns null. But adding the slight delay fixes this problem.
I am new to Titanium, so excuse my lack of understanding.
Even though I am using sdk version 3.2 (have sdk-version: 3.2.0.v20130801162445 in my tiapp.xml) when I try and have a view that uses the xml above I get this error:
[ERROR][V8Exception( 615)] Exception occurred at alloy/controllers/feed.js:22: Uncaught TypeError: Object # has no method 'createTemplates'
I cut down all my code so that the feed.js file is just:
function loadMoreBtnClicked(_event) {
alert('not implemented yet');
}
function createListView(_data) {
// this is pretty straight forward, assigning the values to the specific
// properties in the template we defined above
var items = [];
for (var i in _data) {
// add items to an array
items.push({
template : "template1", // set the template
textLabel : {
text : _data[i].name // assign the values from the data
},
pic : {
image : _data[i].pic_square // assign the values from the data
}
});
}
// add the array, items, to the section defined in the feed.xml file
$.section.setItems(items);
}
alert('feed loaded');
The XML is in feed.xml and looks like this:
<Alloy>
<Window class="container" formFactor="handheld">
<ListView id="list" defaultItemTemplate="template1">
<Templates>
<ItemTemplate name="buttonItem" height="Ti.UI.SIZE">
<!-- will use this in the next blog post -->
<Button id="loadMoreBtn" onClick="loadMoreBtnClicked">Load More</Button>
</ItemTemplate>
<!-- main template for displaying the list items -->
<ItemTemplate id="template1" name="template1" class="template1">
<ImageView id="pic" bindId="pic" class="imageThumb"/>
<View id="textContainer">
<Label id="textLabel" bindId="textLabel" class="title"/>
</View>
</ItemTemplate>
</Templates>
<!-- we only have one section and the items are contstucted using template1 -->
<ListSection id="section" >
<ListItem template="template1" />
</ListSection>
</ListView>
</Window>
</Alloy>
I still get the error (just using the XML with no actual controller code other than the alert running). If I pull the ListView XML out of the feed.xml file the alert fires, when I put the ListView XML back in I get the Error above.
I am trying to use code from this example:
https://gist.github.com/aaronksaunders/5896390
but cant really tell what I am missing?
Thanks!
-James
found out what the issue was, my problem had to do with not having the updated version of alloy that is needed to support the ListView Templates in XML. I needed to run this at the command line in Windows: "npm install -g alloy#1.2.0-alpha" (without quotes). After that I was able to use ListView templates in XML as shown above.
I'm attempting to use a custom directive to make a conditional required statement. The first condition I've added is 'firstInArray' to make the element required if it's the first in array of choices (necessary for a UI where you need to pick at least one item, but you could pick indefinitely many):
.directive('variableRequired', [
()->
return {
require: 'ngModel',
link: (scope, el, attrs, ctrl)->
vars = attrs.variableRequired.split(',')
condition = vars[0]
if condition is 'firstInArray'
item = vars[1]
arr = vars[2]
if scope[item] == scope[arr][0]
$(el).removeAttr('variable-required')
$(el).attr('required', 'required')
}
])
When I add scope.$apply() within my directive, the app freezes up (seems like infinite recursion).
Is there a better way to approach this than a custom directive? If not, what's wrong with my directive?
You may be able to use the undocumented ng-required directive, instead of your own custom directive:
<li ng-repeat="itemObj in items">
<input type="text" ng-model="itemObj.text" ng-required="$first">
</li>