React Final Form blurring each field - react-final-form

I'm using Final Form arrays to dynamically render custom fields that consist of two text fields, both of which are validated.
I'm not sure how to go about blurring the fields individually. This is what I currently have:
<FieldArray name="customField">
{({ fields }) => {
return fields.map((name, index) => {
return (
<Field name={name}>
{(props) => {
<CustomInput
onBlur={props.input.onBlur}
}
</Field>
And then in CustomInput:
<>
<TextField name='first' onBlur={onBlur} />
<TextField name='second' onBlur={onBlur} />
</>
The problem with this approach is that whenever the first TextField is blurred, both of them get validated (instead of just the first one).
Is something analogous to this
<TextField name='first' onBlur={e => onBlur(e, 'first')} />
<TextField name='first' onBlur={e => onBlur(e, 'second')} />
possible?

Related

Xamarin IOS Collection View Layout is misaligned at first render

I'm new with xamarin and I have a really weird bug which is making me crazy. I have a Collection View inside a Tab Item from xamarin tool kit.
The item template for the collection view consists on elements inside a Grid. Inside the template I have two grids which each one is visible depending on a boolean (IsStaticCheckList) which tells you if you have custom fields or normal fields.
I put my render function on the tap item command from the tab item (in that way I'm able to force the render each time the checklist item is tapped). The problem is that on IOS version the layout is not rendering well when I open the form for the first time but if we repeat the process it does it.
As an additional info:
I'm using the MVVM pattern so I use commands for all actions inside my view model.
The difference between custom and normal fields. In the UI is that you are able add as many normal fields in the list as you want and edit them. But if we are using custom fields you are not able to do those actions as you have a checklist template selected.
This only happens on IOS version, Android version works as expected.
This issue only happens with normal fields (IsStatiChecklist = true) on ios version. I've been debbuging the code but nothing seems to be wrong. At the end the layout is working as expected at second time. But I'm not able to make it work the first time I open the form.
I attached some pictures for reference
Here it is how it looks at first time (the 3 dot menu does not appear and the other fields neither because the content is using more space that it should) So all the elements are moved in some way to the right.
Render issue
Here it is how it looks after I leave the tab and go again to my checklist tab
Render Second time
Here is a screenshot of the other use case which is working correctly in all cases custom fields screenshot
Here I attached my Item Template XAML Code
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:CheckListAnswerModel">
<Frame>
<Grid
Margin="-10,-20,0,0"
ColumnDefinitions="Auto,*,25,50,Auto"
ColumnSpacing="3">
<Grid
Grid.Column="0"
Grid.ColumnSpan="2"
Padding="20"
IsVisible="{Binding IsStaticCheckList, Source={RelativeSource AncestorType={x:Type viewModels:AddTaskViewModel}}}">
<Label
Grid.Row="0"
IsEnabled="{Binding Complete, Converter={x:StaticResource InvertedBoolConverter}}"
HorizontalOptions="Start"
Text="{Binding Question}"
VerticalOptions="Center"
IsVisible="{Binding ShowLabel}">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding EnableEditionCommand, Source={RelativeSource AncestorType={x:Type viewModels:AddTaskViewModel}}}" CommandParameter="{Binding .}" />
</Label.GestureRecognizers>
<Label.Triggers>
<DataTrigger
Binding="{Binding Complete}"
TargetType="Label"
Value="true">
<Setter Property="TextDecorations" Value="Strikethrough" />
</DataTrigger>
</Label.Triggers>
</Label>
<Entry
Grid.Row="0"
IsEnabled="{Binding Complete, Converter={x:StaticResource InvertedBoolConverter}}"
IsVisible="{Binding ShowEntry}"
Placeholder="Click to add text"
ReturnCommand="{Binding CompleteAddCommand, Source={RelativeSource AncestorType={x:Type viewModels:AddTaskViewModel}}}"
ReturnCommandParameter="{Binding .}"
Text="{Binding Question}"
TextColor="{x:StaticResource PrimaryColor}">
<Entry.Effects>
<effects:BorderlessEntryEffect />
</Entry.Effects>
</Entry>
</Grid>
<ia:Checkbox
Grid.Column="2"
CheckColor="{x:StaticResource WhiteColor}"
FillColor="{x:StaticResource DefaultButtonColor}"
IsChecked="{Binding Complete}"
IsVisible="{Binding Complete}"
OutlineColor="{x:StaticResource DefaultButtonColor}"
Shape="Circle" />
<Image
Grid.Column="3"
IsVisible="{Binding HasTask}"
Scale="0.5"
Source="{x:Static res:Images.CreatedTaskIcon}" />
<ImageButton
Grid.Column="4"
BackgroundColor="Transparent"
Command="{Binding OpenMenuItemCommand, Source={RelativeSource AncestorType={x:Type viewModels:AddTaskViewModel}}}"
CommandParameter="{Binding .}"
Scale="2"
Source="{x:Static res:Images.VerticalMoreIcon}"
VerticalOptions="Center" />
</Grid>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
The Items Source for the collection view is called CheckList which has this definition:
private ObservableCollection<CheckListAnswerModel> _checklist;
public ObservableCollection<CheckListAnswerModel> Checklist
{
get => _checklist;
set
{
_checklist = value;
RaisePropertyChanged(() => Checklist);
}
}
Here is the funtion that tells the view model to add the corresponding items to the Checklist:
private async Task RenderElements()
{
if (_checkListTemplateId > 0)
{
var checklistTemplate = _checkListTemplates.FirstOrDefault(x => x.Id == _checkListTemplateId);
if (checklistTemplate != null)
{
CheckListTemplateText = checklistTemplate.Name;
customFieldAnswer = String.Empty;
_checkListTemplateId = checklistTemplate.Id;
IsStaticCheckList = false;
_checklistAnswerEntities = new List<CheckListAnswerEntity>();
if (Id > 0 && loadAnswers) // taskId > 0
{
_checklistAnswerEntities = await _userTaskManager.GetChecklistAnswerByTaskId(Id);
}
if (_checklistAnswerEntities != null && _checklistAnswerEntities.Count() > 0)
{
await RenderCustomFields(checklistTemplate.ChecklistCustomFields, _checklistAnswerEntities);
}
else
{
await RenderCustomFields(checklistTemplate.ChecklistCustomFields);
}
}
else
{
CheckListTemplateText = AppResources.Label_SelectTemplate;
}
}
else
{
if (HasNormalItems)
{
IsStaticCheckList = true;
Checklist = new ObservableCollection<CheckListAnswerModel>();
foreach (var item in EditTaskParam.TaskListModel.CheckListItems)
{
CheckListAnswerModel model = new CheckListAnswerModel(new CheckListAnswerEntity { TaskId = item.TaskId, ChecklistCustomFieldId = item.Id, Question = item.ItemName });
model.Complete = item.Complete;
model.ShowEntry = false;
model.ShowLabel = true;
model.IsTextEntry = false;
model.IsCustomDropdown = false;
model.IsYesNoEntry = false;
Checklist.Add(model);
}
SetCheckListLabel();
}
else
{
SetCheckListLabel();
}
}
}

react-hook-form and react-datetime: How to set the time to moment() from a button

I am using react-datetime inside a react-hook-form
I want the user to easily set the time to current time using a button Immediate. Instead of selecting the current time manually.
I am trying the below
const [currentDateTime, setcurrentDateTime] = useState(null);
<Controller
name="resetDateTime"
control={control}
required
render={({ field }) => (
<Datetime
onChange={setcurrentDateTime}
inputProps={{
placeholder: "MM-DD-YYYY HH:mm",
}}
value={currentDateTime}
viewMode="time"
/>
)}
/>
<Button color="primary" className="ml-1" onClick={() => setcurrentDateTime(moment())}>
{"Immediate"}
</Button>
The problem is onSubmit the react-hook-form I get resetDateTime = undefined
How to implement this properly. So I can use the Immediate button and also submit form and get the resetDateTime value
You're mixing RHF with your local state currentDateTime and are not linking the. field to RHF as you're missing to spread the field object to your <Datetime /> component.
The correct way would be to use RHF's setValue to update your resetDateTime field and get rid of the useState hook.
const { control, handleSubmit, setValue } = useForm();
<Controller
name="resetDateTime"
control={control}
required
render={({ field }) => (
<Datetime
{...field}
inputProps={{
placeholder: "MM-DD-YYYY HH:mm",
}}
viewMode="time"
/>
)}
/>
<Button color="primary" className="ml-1" onClick={() => setValue("resetDateTime", moment())}>
{"Immediate"}
</Button>

react hook form: as vs render: unable to understrand the syntax and how are they same

I have seen in react hook forms using as and also render
Eg:
<Controller
render={({ field }) => <input {...field} />}
name="firstName"
control={control}
defaultValue=""
/>
or
<Controller
as={<input .. />}
name="firstName"
control={control}
defaultValue=""
/>
whats the difference
<Controller/> is good to use with there is an external controlled component such (e.g. Material-UI) to wrap the whole component and control is easier.
render is useful when you want to customise the external component while as is just renders the original component. An example of using render could be:
import { TextField } from '#mui/material';
import { Controller } from 'react-hook-form';
const FormTextField = ({
label,
name,
...props
}) => {
return (
<Controller
name={name}
render={({ field, fieldState: { error } }) => (
<TextField
{...field}
label={label}
error={!!error}
helperText={error ? error?.message : ''}
/>
)}
{...props}
/>
);
};
As you can see, render gives you ability to access different values (such as error) in the Material UI component which is not easy to do with as.
Read more about what properties you have access in render at https://react-hook-form.com/api/usecontroller/controller
This example is also helpful: https://codesandbox.io/s/react-hook-form-v7-controller-5h1q5

Unable to access mutator functions in Wizard form page while using react-final-form

I am trying to create a Wizard form using react-final-form by referring to this code https://codesandbox.io/s/km2n35kq3v. For my use case I need some mutator functions to be used inside my form fields. This example illustrates how to do that - https://codesandbox.io/s/kx8qv67nk5?from-embed.
I am not sure how to access mutator functions in my form steps when I am using a wizard form instead of a single page form.
I tried to combine both the examples by modifying the <Form> component rendered by Wizard.js to pass in the mutators. However I cannot access these mutators in the Wizard form pages.
In Wizard.js
return (
<Form
mutators={{
// potentially other mutators could be merged here
...arrayMutators,
}}
render={({
handleSubmit,
submitting,
values,
pristine,
invalid,
form: {
mutators: {push, pop, remove},
},
}) => {
return (
<form onSubmit={handleSubmit}>
Another file index.js
<Wizard
initialValues={{ employed: true, stooge: "larry" }}
onSubmit={onSubmit}
>
<Wizard.Page>
<FieldArray name="customers">
{({ fields }) =>
fields.map((name, index) => (
<div key={name}>
<label>Cust. #{index + 1}</label>
<Field
name={`${name}.firstName`}
component="input"
placeholder="First Name"
/>
<span
onClick={() => fields.remove(index)}
style={{ cursor: "pointer" }}
>
❌
</span>
</div>
))
}
</FieldArray>
</Wizard.Page>
</Wizard>
It errors out - remove is undefined in index.js
Look at this working example: https://codesandbox.io/s/znzlqvzvnx
changes I have made:
Wizard.js
static Page = ({ children, mutators }) => {
if(typeof children === 'function'){
return children(mutators);
}
return children;
};
...
<form onSubmit={handleSubmit}>
{
// activePage
<activePage.type {...activePage.props} mutators={mutators} />
}
...
index.js (only first <Wizard.page>)
<Wizard.Page>
{
({ upper }) => (
<React.Fragment>
<div>
<label>First Name</label>
<Field
name="firstName"
component="input"
...
</div>
</React.Fragment>
)
}
</Wizard.Page>

issues with loading images in custom control

I am new to the WP7 platform and I am facing something strange, I have a custom component that has an image. (Part of the custom component below)
<Rectangle x:Name="ImageForeground" Height="240" Width="240" HorizontalAlignment="Center" VerticalAlignment="Center" Fill="{TemplateBinding Foreground}"
DataContext="{TemplateBinding Image}"
OpacityMask="{Binding Converter={StaticResource brushConverter}}" />
Here is the brushConverter
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
BitmapImage image = (BitmapImage)value;
ImageBrush imageBrush = new ImageBrush();
if (image != null)
{
imageBrush.ImageSource = image;
}
return imageBrush;
}
Now i add the custom component to a page with
<rounded:RoundedImageView Image="/Images/image1.png"/>
Here comes the weird part. The component only displays certain png images!! I have 5 all in all, with ALL their Build Action properties set to Content. But the control only loads some and not all. Even stranger is if i put the images in a regular Image component like so
<Image Height="200" HorizontalAlignment="Left" Margin="54,17,0,0" Name="image3" Stretch="Fill" VerticalAlignment="Top" Width="200" Source="/Images/service_reload.png" />
It works!!! WTF!!
Any ideas?!
Solved:
Solved: I attached the brushConverter binding to the Fill property of the Rectangle instead of the OpacityMask property and it worked like a charm
<Rectangle x:Name="ImageForeground" Height="240" Width="240" HorizontalAlignment="Center" VerticalAlignment="Center" DataContext="{TemplateBinding Image}" Fill="{Binding Converter={StaticResource brushConverter}}" />

Resources