Accessing specific element in hyperHTML template repeater - hyperhtml

Is there a way to access a specific element in a wire map?
render() {
hyper(this.shadowRoot)`
<style>${css}</style>
<container>
${this.referenceImages.map(image => wire(image)`
<cell>
<inner-cell class="${this.returnClass()}">
</inner-cell>
</cell>
`)}
</container>
`;
}
How would I access the node in returnClass()?
Is there a better way to do what I want by using wire ids and weak references?

Depending on how / why you want to access it, you could store a reference and use <HTMLElement>.querySelector()
render() {
hyper(this.shadowRoot)`
<style>${css}</style>
<container>
${this.referenceImages.map(image => {
const el = wire(image)`
<cell>
<inner-cell class="${this.returnClass()}">
</inner-cell>
</cell>
`;
el.querySelector('inner-cell');
...
return el;
})}
</container>
`;
}

Related

Passing directive as props

For example I have a simple directive like this:
export const inputLogger = (el: HTMLInputElement) => {
const onInput = (e: Event) => {
console.log((e.currentTarget as HTMLInputElement).value)
}
el.addEventListener("input", onInput);
};
and my component looks like this
const CustomInput: Component<JSX.InputHTMLAttributes<HTMLInputElement>> = (props) => {
return <input {...props} />
}
function Counter() {
return (
<>
<CustomInput use:inputLogger /> {/* directive is not being used */}
<br />
<input use:inputLogger /> {/* directive works properly */}
</>
);
}
Seems like inputLogger directive doesn't work properly when being passed as a prop to other component, but works fine when passed to a native input element.
Am I doing something wrong? Is there a workaround for this or is this a limitation from Solid.js?
Playground Example
Unfortunately, in Solid, directives can only be applied to native elements, not to components. See here: https://www.solidjs.com/tutorial/bindings_directives.
You can also use a pattern like this, but you no longer can rely on spreading props.
const CustomInput: Component<JSX.InputHTMLAttributes<HTMLInputElement>> = (
props
) => {
return <input {...props} ref={props["use:inputLogger"]} />;
};
function Counter() {
return (
<>
<CustomInput use:inputLogger={inputLogger} />
</>
);
}
https://playground.solidjs.com/?hash=-51759678&version=1.4.1

what's ref in react native and when should i use ref?

I'm working on react native project, i created forms with react native components.
I used TextInput to edit a state value like this :
<TextInput
shake
keyboardAppearance='light'
autoFocus={false}
autoCapitalize='none'
autoCorrect={false}
keyboardType='default'
returnKeyType='next'
value={this.state.sector}
onChangeText={sector => this.setState({ sector })}
/>
With console.log the sector value i get correctly the current value after input change, but i have seen some examples with ref like this :
<TextInput
shake
keyboardAppearance='light'
autoFocus={false}
autoCapitalize='none'
autoCorrect={false}
keyboardType='default'
returnKeyType='next'
value={this.state.sector}
ref={input => (this.sectorInput = input)}
onChangeText={sector => this.setState({ sector })}
/>
i don't understand this operation :
ref={input => (this.sectorInput = input)}
can someone explain what's the ref and why we used with an input and when should i use a ref ?
If you want to access TextInput methods then you have to create reference of that component, then using reference you can use it's method.
For example you have form in your app and you want that when user fill the first field and after that you want to set focus on next field then you can do like this :
<TextInput
shake
keyboardAppearance='light'
autoFocus={false}
autoCapitalize='none'
autoCorrect={false}
keyboardType='default'
returnKeyType='next'
value={this.state.sector}
ref={input => { this.sectorInput = input}}
onSubmitEditing={() => {
this.nextField.focus();
}}
onChangeText={sector => this.setState({ sector })}
/>
<TextInput
shake
keyboardAppearance='light'
autoFocus={false}
autoCapitalize='none'
autoCorrect={false}
keyboardType='default'
returnKeyType='next'
value={this.state.sector}
ref={input => { this.nextField = input}}
onSubmitEditing={() => {
this.handleSubmit();
}}
onChangeText={nextField => this.setState({ nextField })}
/>
Now, when user will fill the sector field then if he press next, then nextField will automatically get focused.

Extbase Property Mapping for arrays

I want to use a Array Object inside a Fluid form using Property Mapper. The products are dynamic added if user clicks an "add_product" link:
<f:form action="property" name="newOrder" object="{newOrder}">
<f:for each="{newOrder.orderProduct}" as="orderProduct" iteration="iterator">
<f:form.hidden property="orderProduct.{iterator.index}.product" value="8" />
<h3>OrderProduct: {orderProduct.product.title}</h3>
</f:for>
<f:form.hidden name="add_product" value="1" />
<input type="submit" value="submit" />
</f:form>
What I get after submit is this exception.
Uncaught TYPO3 Exception #1297759968:
Exception while property mapping at property path "orderProduct.0":
Property "product" was not found in target object of type "MyVendor\MyShop\Domain\Model\Product".
The hidden field resolves to: <input name="tx_myshop_pi1[newOrder][orderProduct][0][product]" value="8" type="hidden"> (the static value of 8 is just to simplify the example)
I also tried key="key" instead of iterator, empty brackets orderProduct[], using name instead of property without result.
This is the (simplified) Debug output:
newOrder (MyVendor\MyShop\Domain\Model\ShopOrder)
=> orderProduct (TYPO3\CMS\Extbase\Persistence\ObjectStorage)
3222112 => MyVendor\MyShop\Domain\Model\OrderProduct
product => MyVendor\MyShop\Domain\Model\Product
uid => 8
title => 'Product1'
This is the Model Code:
ShopOrder https://pastebin.com/YN7X37ei
OrderProduct https://pastebin.com/zFyztLAQ
For the Property Mapper, I tried a lot of configurations without success. In my opinion this should work but it does not:
public function initializePropertyAction()
{
/** #var \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration $propertyMappingConfiguration */
$propertyMappingConfiguration = $this->arguments['newOrder']->getPropertyMappingConfiguration();
$propertyMappingConfiguration->allowAllProperties();
$propertyMappingConfiguration->setTypeConverterOption('TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter',
PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED,
TRUE);
$propertyMappingConfiguration->forProperty('orderProduct')->allowAllProperties();
$propertyMappingConfiguration->forProperty('orderProduct')->setTypeConverterOption(
'TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter',
PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED,
TRUE
);
//workaround from https://forge.typo3.org/issues/61628
for ($i = 0; $i < 99; $i++) {
$propertyMappingConfiguration->forProperty('orderProduct.' . $i)->allowAllProperties();
$propertyMappingConfiguration->forProperty('orderProduct.' . $i . '.*')->allowAllProperties();
$propertyMappingConfiguration->forProperty('orderProduct.' . $i)->setTypeConverterOption(
'TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter',
PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED,
TRUE
);
}
}
I solved it like this, in my case for a question with a dynamic number of answers having two fields, each:
$propertyMappingConfiguration = $this->arguments->getArgument('question')->getPropertyMappingConfiguration();
$propertyMappingConfiguration->skipProperties('category');
$propertyMappingConfiguration->allowProperties('answers');
$propertyMappingConfiguration->forProperty('answers.*')->allowProperties('answerField1', 'answerField2');
$propertyMappingConfiguration->allowCreationForSubProperty('answers.*');
$propertyMappingConfiguration->allowModificationForSubProperty('answers.*');

Autocomplete - How can I set a default value?

Does anyone know how to add a default value to the Autocomplete component?
The component have a dataSource, and I'd like to load the page with a specific item already selected(e.g. fill the text field with the selected item's text and it's value already set).
Does anyone knows how? Big thanks for any help <3
You can achieve this by setting the appropriate state in componentDidMount, for example:
componentDidMount() {
// load your items into your autocomplete
// set your default selected item
this.setState({ allItems: [itemYouWantToSet], selectedItem: item.name, selectedItemId: item.id }
}
render() {
return (
<Autocomplete
value={this.state.selectedItem}
items={this.state.allItems}
getItemValue={(item) => item.name}
onSelect={(value, item) => {
this.setState({ selectedItem: value, selectedItemId: value, allItems: [item] });
}}
/>
)
}
Then your item is correctly selected from the list of available options when it loads.
I tried all the above solutions and nothing worked. Perhaps the API has changed since then.
I finally figured out a solution. It's not so elegant, but in principle it makes sense. In my case the options are objects. I just had to set the "value" prop using the exact item from my options array. This way componentDidMount and getOptionSelected aren't needed.
Autocomplete is wrapped inside another component in our case. This is the main code:
class CTAutoComplete extends React.PureComponent {
getSelectedItem(){
const item = this.props.options.find((opt)=>{
if (opt.value == this.props.selectedValue)
return opt;
})
return item || {};
}
render() {
return (
<Autocomplete
id={this.props.id}
className={this.props.className}
style={{ width: '100%' }}
options={this.props.options}
getOptionLabel={this.props.getOptionLabel}
renderInput={params => (
<TextField {...params} label={this.props.label} variant="outlined" />
)}
onChange={this.props.onChange}
value={this.getSelectedItem()}
/>
);
}
}
IMPORTANT: When setting "value", you have to make sure to put the null case " || {} ", otherwise React complains you are changing from an uncontrolled to controlled component.
you can provide the defaultValue prop for AutoComplete.
<Autocomplete
multiple
id="tags-outlined"
options={this.state.categories}
getOptionLabel={(option) => option.category_name}
onChange={this.handleAutocomplete}
defaultValue={'yourDefaultStringValue'} //put your default value here. It should be an object of the categories array.
filterSelectedOptions
renderInput={(params) => (
<TextField
fullWidth
{...params}
variant="outlined"
label="Add Categories"
placeholder="Category"
required
/>
}
/>
This approach works for me (using hooks):
First of all define the options you need in a variable:
const genderOptions = [{ label: 'M' }, { label: 'V' }];
Second you can define a hook to store the selected value (for example store it in session storage for when the page refreshes, or use useState directly):
const age = useSessionStorage('age', '');
Next you can define your Autocomplete as follows (notice the default values in value and getOptionLabel, if you omit those you'll get those controlled to uncontrolled warnings):
<Autocomplete
id="id"
options={ageOptions}
getOptionLabel={option => option.label || ""}
value={ageOptions.find(v => v.label === age[0]) || {}} // since we have objects in our options array, this needs to be a object as well
onInputChange={(_, v) => age[1](v)}
renderInput={params => (
<TextField {...params} label="Leeftijd" variant="outlined" />
)}
/>
It is tricky specially in case of you are using along with filter option which load API on every filter. I was able to load initial value by setting up within state and onInputChange option.
Below is code that work for me or click below link for full working demo.
https://codesandbox.io/s/smoosh-brook-xgpkq?fontsize=14&hidenavigation=1&theme=dark
import React, { useState, useEffect } from "react";
import TextField from "#material-ui/core/TextField";
import Typography from "#material-ui/core/Typography";
import Autocomplete from "#material-ui/lab/Autocomplete";
export default function CreateEditStrategy({ match }) {
const [user, setUser] = useState({
_id: "32778",
name: "Magic User's Club!"
});
const [filter, setFilter] = useState("");
const [users, setUsers] = useState([]);
const [openAutoComplete, setOpenAutoComplete] = React.useState(false);
useEffect(() => {
(async () => {
//Will not filter anything for testing purpose
const response = await fetch(
`https://api.tvmaze.com/search/shows?q=${filter}`
);
const shows = await response.json();
setUsers(
shows.map((a, i) => {
return { _id: a.show.id, name: a.show.name };
})
);
})();
}, [filter]);
return (
<div>
<Typography variant="h6">Autocomplete</Typography>
<Autocomplete
open={openAutoComplete}
onOpen={() => setOpenAutoComplete(true)}
value={user}
inputValue={filter}
onClose={() => setOpenAutoComplete(false)}
onChange={(event, user) => {
if (user) setUser(user);
else setUser({ _id: "", name: "" });
}}
onInputChange={(event, newInputValue) => setFilter(newInputValue)}
getOptionSelected={(option, value) => option.name === value.name}
getOptionLabel={(option) => option.name}
options={users}
renderInput={(params) => (
<TextField
{...params}
label="Asynchronous"
variant="outlined"
InputProps={{
...params.InputProps
}}
/>
)}
/>
</div>
);
}
Call your component like this
<SelectCountryAutosuggest searchText="My Default Value" />
Make sure you apply the default value to state on class load
class SelectCountryAutosuggest extends React.Component {
state = {
value: this.props.searchText, //apply default value instead of ''
suggestions: [],
};
...
}
The api docs suggest the best approach in the current version (June 2022) is to use value and isOptionEqualToValue.
So for example, if I have a list of users and am choosing which user this thing is assigned to:
const [assignedTo, setAssignedTo] = useState(initialOption);
return (<Autocomplete
options={users.map((i) => ({
label: i.name,
value: i._id,
}))}
isOptionEqualToValue={(o, v) => o.value === v.id}
value={assignedTo}
onChange={(evt, val) => setAssignedTo(val)}
renderInput={(params) => (
<TextField {...params} label="Assigned To" />
)}
/>);
We can setup initial value through value property of Autocomplete component
<Autocomplete
fullWidth={true}
label={'Location'}
margin={'noraml'}
multiple={false}
name={'location'}
getOptionSelected={useCallback((option, value) => option.value === value.value)}
value={formValues.location === '' ? {label: ''} : {label: formValues.location}}
options={location}
ref={locationRef}
onChange={useCallback((e, v) => handleInputChange(e, v))}
/>
You can use the searchText property.
<AutoComplete searchText="example" ... />
Try this...
componentWillReceiveProps(nextProps) {
let value = nextProps.value
if (value) {
this.setState({
value: value
})
}
}
onUpdateInput worked for me - for anyone looking through all this as I was
Have you tried setting the searchText prop dynamically? You can pass the value you want to set to the Autocomplete component as the searchText prop. Something like,
<Autocomplete
searchText={this.state.input.name} // usually value={this.state.input.name}
/>
By default, it'll have the initial value set into the TextField of the Autocomplete component but when the user makes any modifications, it calls up the Autocomplete options depending on the dataSource prop.

How to use checkboxes with Phalcon forms?

I am creating a form using Phalcon that has a checkbox on it. I use this code to create the checkbox in my PagesForm.php file
$this->add(new Check('usesLayout'));
and then in my view I have
{{ form.render("usesLayout") }}
However, if the checkbox is unchecked then Phalcon complains about usesLayout is required.
The html code produced by the view is
<input type="checkbox" id="usesLayout" name="usesLayout" value="1" checked="checked" />
What is the correct way to create a Phalcon form with a checkbox so that it accepts it both checked and unchecked?
Desired outcome
After looking back at a form made when using CakePHP the html output is
<input type="hidden" name="usesLayout" id="usesLayout_" value="0" />
<input type="checkbox" name="usesLayout" id="usesLayout" value="1" checked="checked" />
This works fine, so I am looking for something similar to this.
Current Workaround
After modifying the code in the final response to this question I have this workaround currently (I use this instead of Phalcon\Forms\Element\Check)
namespace Armaware\InBrowserDev\Forms\Element;
use Phalcon\Forms\Element\Check as PhalconCheck;
class Check extends PhalconCheck
{
/**
* Renders the element widget returning html
*
* #param array|null $attributes Element attributes
*
* #return string
*/
public function render($attributes = null)
{
$attrs = array();
if (!is_null($attributes)) {
foreach ($attributes as $attrName => $attrVal) {
if (is_numeric($attrName) || in_array($attrName, array('id', 'name', 'placeholder'))) {
continue;
}
$attrs[] = $attrName .'="'. $attrVal .'"';
}
}
$attrs = ' '. implode(' ', $attrs);
$id = $this->getAttribute('id', $this->getName());
$name = $this->getName();
$checked = '';
if ($this->getValue()) {
$checked = ' checked';
}
return <<<HTML
<input type="hidden" id="{$id}_" name="{$name}" value="0" />
<input type="checkbox" id="{$id}" name="{$name}" value="1"{$attrs}{$checked} />
HTML;
}
}
public Phalcon\Forms\ElementInterface setDefault (unknown $value) inherited from Phalcon\Forms\Element
Sets a default value in case the form does not use an entity or there is no value available for the element in _POST
Source.
Looks like your declaration of form can look like this:
$controls[] = (new Check('usesLayout', ['value' => '1']))
->setLabel('Should I use layout?')
->setDefault('0') // or `false` in case it's not filtered
->addFilter('bool'); // filtering to boolean value
Not tested, but probably will do. You can always try to make this trick with handling this in beforeValidation() method of form, but have no space to test it right now and am not risking on failurable solution here.