Closed Code-DJ closed 1 year ago
Hi, you can't update the state like that (State is not yet ready at that moment). Try this instead:
class MyTestEntryState
{
public string? Text { get; set; }
public string? PlaceholderText { get; set; }
}
class MyTestEntry : Component<MyTestEntryState>
{
string? _text;
string? _placeholderText;
public MyTestEntry Text(string? text)
{
_text = text;
return this;
}
public MyTestEntry PlaceholderText(string? placeholderText)
{
_placeholderText = placeholderText;
return this;
}
protected override OnMountedOrPropsChanged()
{
State.Text = _text;
State.PlaceholderText = _placeholderText;
base.OnMountedOrPropsChanged();
}
public override Entry Render() => new Entry()
.Text(State.Text ?? "")
.Placeholder(State.PlaceholderText ?? "");
}
BUT: from what I see you don't need at all a stateful component, like this:
class MyTestEntry : Component
{
string? _text;
string? _placeholderText;
public MyTestEntry Text(string? text)
{
_text = text;
return this;
}
public MyTestEntry PlaceholderText(string? placeholderText)
{
_placeholderText = placeholderText;
return this;
}
protected override OnMountedOrPropsChanged()
{
State.Text = _text;
State.PlaceholderText = _placeholderText;
base.OnMountedOrPropsChanged();
}
public override Entry Render() => new Entry()
.Text(_text ?? "")
.Placeholder(_placeholderText ?? "");
}
or even better, not using a Component, everything could be down to:
public static class Theme
{
//custom Entry with font, size color etc, reusable
public static Entry Entry(string text, string? placeholder = null)
=> new Entry()
.Text(_text)
.Placeholder(_placeholderText ?? "");
}
Why are you using a stateful component?
Will give it a shot. I'm planning to create a Floating Label component.
like this?
class MainPageState
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
class MainPage : Component<MainPageState>
{
public override VisualNode Render()
{
return new ContentPage
{
new VStack(spacing: 10)
{
new MudEntry()
.Label("First Name")
.OnTextChanged(text => SetState(s => s.FirstName = text)),
new MudEntry()
.Label("Last Name")
.OnTextChanged(text => SetState(s => s.LastName = text)),
}
.HFill()
.VCenter()
.Margin(50)
};
}
}
class MudEntryState
{
public bool Focused { get; set; }
public bool IsEmpty { get; set; } = true;
}
class MudEntry : Component<MudEntryState>
{
private MauiControls.Entry _entryRef;
private Action<string> _textChangedAction;
private string _label;
public MudEntry OnTextChanged(Action<string> textChangedAction)
{
_textChangedAction = textChangedAction;
return this;
}
public MudEntry Label(string label)
{
_label = label;
return this;
}
public override VisualNode Render()
{
return new Grid("Auto", "*")
{
new Entry(entryRef => _entryRef = entryRef)
.OnAfterTextChanged(OnTextChanged)
.VCenter()
.OnFocused(()=>SetState(s => s.Focused = true))
.OnUnfocused(()=>SetState(s => s.Focused = false)),
new Label(_label)
.OnTapped(()=>_entryRef?.Focus())
.Margin(5,0)
.HStart()
.VCenter()
.TranslationY(State.Focused || !State.IsEmpty ? -20 : 0)
.ScaleX(State.Focused || !State.IsEmpty ? 0.8 : 1.0)
.AnchorX(0)
.TextColor(!State.Focused || State.IsEmpty ? Colors.Gray : Colors.Red)
.WithAnimation(duration: 200),
}
.VCenter();
}
private void OnTextChanged(string text)
{
SetState(s => s.IsEmpty = string.IsNullOrWhiteSpace(text));
_textChangedAction?.Invoke(text);
}
}
Hi awesome! thank you. I guess my mistake was I was setting everything in State and applying Text, Padding, HeightRequest etc. all from State. Also, I was expecting only the component to break but it was affecting the page using the component as well.
Thanks again!
just to further improve my answer this is a perfect case for the Inline Component feature:
class MainPageState
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
class MainPage : Component<MainPageState>
{
public override VisualNode Render()
{
return new ContentPage
{
new VStack(spacing: 10)
{
RenderMudEntry("First Name", text => SetState(s => s.FirstName = text)),
RenderMudEntry("Last Name", text => SetState(s => s.LastName = text)),
}
.HFill()
.VCenter()
.Margin(50)
};
}
static VisualNode RenderMudEntry(string label, Action<string> textChangedAction)
=> Component.Render(context =>
{
MauiControls.Entry _entryRef = null;
var state = context.UseState<(bool IsFocused, bool IsFilled)>();
return new Grid("Auto", "*")
{
new Entry(entryRef => _entryRef = entryRef)
.OnAfterTextChanged(text =>
{
state.Set(s => (s.IsFocused, IsFilled: !string.IsNullOrWhiteSpace(text)));
textChangedAction?.Invoke(text);
})
.VCenter()
.OnFocused(()=>state.Set(s => (IsFocused: true, s.IsFilled)))
.OnUnfocused(()=>state.Set(s => (IsFocused: false, s.IsFilled))),
new Label(label)
.OnTapped(() =>_entryRef?.Focus())
.Margin(5,0)
.HStart()
.VCenter()
.TranslationY(state.Value.IsFocused || state.Value.IsFilled ? -20 : 0)
.ScaleX(state.Value.IsFocused || state.Value.IsFilled ? 0.8 : 1.0)
.AnchorX(0)
.TextColor(!state.Value.IsFocused || !state.Value.IsFilled ? Colors.Gray : Colors.Red)
.WithAnimation(duration: 200),
}
.VCenter();
});
}
This issue is on iOS - not sure about Android.
1.0.137
I have the following simplified custom component, when I use it on a page the State doesn't update:
Render method on any Component Page: