Blazor event after #bind - event-handling

In a Blazor page I have a collection of checkbox input elements bound to a boolean property with code
foreach(var item in items)
{
<input type="checkbox" class="checkbox" #bind="#item.IsReady" #onclick="((e)=>ItemIsReady(item))" />
}
I'm looking for a way to trigger the function ItemIsReady after the binding has completed. Currently the function triggers before the binding has completed.
Some form of #bind directive is necessary because the page loads the items, and ready items need to be appear checked after loading.
I can't use a function in the property setter.
Anyone know how to do this?

"I'm looking for a way to trigger the function ItemIsReady after the binding has completed. Currently the function triggers before the binding has completed."
I'm assuming you want to react to changes make to the checkboxes. Maybe I'm wrong.
If you want to catch update events then you either:
Use bind and catch the events on the property setter or
Wire up the input manually.
The demo page below shows how you can do that:
#page "/Test"
#foreach (var item in model)
{
<div><input type="checkbox" class="checkbox" checked="#item.IsReady" #onchange="((e)=>ItemIsReady(e, item))" /> <span>#item.IsReady</span></div>
}
#code {
List<ModelClass> model { get; set; } = new List<ModelClass> {
new ModelClass(),
new ModelClass() { IsReady=true },
new ModelClass()
};
class ModelClass
{
public bool IsReady { get; set; }
}
void ItemIsReady(ChangeEventArgs e, ModelClass item)
{
item.IsReady = (bool)e.Value;
}
}

Related

Is there a control similar to HTML Datalist for .Net MAUI?

I have a need for a picker-type control in my MAUI app, but the selection list contains over 1000 entries. I don't want to make my users scroll through 1000 entries to find the one they want to choose. Secondarily, that is a lot of data to get from my API every time the page is accessed, but I can figure that out.
Is there something in .Net MAUI that is equivalent to the HTML Datalist, where there's an input box and as the user types, the list condenses down to what they type - like a search box. All I can find on the Microsoft docs is the Picker. I'd like to not have to pay for a third-party control if possible.
https://www.w3schools.com/tags/tag_datalist.asp
Here is what the Search/List looks like -- it could work if
I can prepopulate the field with existing data for that column from the db.
It would show the filtered list as the user types in characters. Currently it opens up blank lines and doesn't show the data.
It does NOT show all 1000+ entries in the List if the Search is blank unless the user is actually on that field. E.g. if you type in a search, then backspace and delete it and move to another field, all List entries remain displayed.
As suggested by Jason ans Steve, you can use a SearchBar with a ListView or CollectionView. Here's the sample code below for your reference:
Model:
public class Notes
{
public string Name { get; set; }
public string Num { get; set; }
}
Xaml:
<VerticalStackLayout>
<SearchBar TextChanged="SearchBar_TextChanged"></SearchBar>
<ListView x:Name="list">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}" Detail="{Binding Num}">
</TextCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</VerticalStackLayout>
Code-behind:
public partial class MainPage : ContentPage
{
public ObservableCollection<Notes> notedata;
public MainPage()
{
InitializeComponent();
//data service
generateData();
BindingContext = this;
}
public void generateData()
{
notedata = new ObservableCollection<Notes>()
{
new Notes(){Name = "alex", Num = "2323423"},
new Notes(){Name = "bloomberg", Num = "12323423"},
new Notes(){ Name = "ahmed", Num = "32323423"},
new Notes(){ Name = "abc", Num = "42323423"},
new Notes(){ Name = "umair", Num = "62323423"},
new Notes(){ Name = "etc", Num = "32323423"},
};
}
private void SearchBar_TextChanged(object sender, TextChangedEventArgs e)
{
//all you need to make a search
if (string.IsNullOrEmpty(e.NewTextValue))
{
list.ItemsSource = notedata;
}
else
{
list.ItemsSource = notedata.Where(x => x.Name.StartsWith(e.NewTextValue));
}
}
}

Can't submit on dropdown lazy loading

My form has 1 dropdown use AjaxLazyLoadPanel and 1 ajax button submit.
I click button submit that works only when the dropdown is finished loading.
Index.java
form.add(new AjaxLazyLoadPanel("lazy") {
private static final long serialVersionUID = 1L;
#Override
public Component getLazyLoadComponent(String markupId) {
Fragment fr = new Fragment(markupId, "lazyContentFragment", Index.this);
fr.add(dropdown());
return fr;
}
});
form.setOutputMarkupId(true);
form.add(new AjaxButton("search") {
#Override
protected void onError(AjaxRequestTarget target) {
target.add(feedback);
}
#Override
protected void onSubmit(AjaxRequestTarget target) {
//do something...
}
});
Index.html
<form wicket:id="form">
<input wicket:id="textbox"/>
<div wicket:id="lazy"></div>
<button wicket:id="search"></button>
</form>
<wicket:fragment wicket:id="lazyContentFragment">
<select wicket:id="dropdown"></select>
</wicket:fragment>
Is there any way to submit the form without waiting for the dropdown finish loading.
By default wicket-ajax.js serializes the Ajax calls to the server. This is being done so that the second Ajax call is not called if its HTML element is being removed/painted by the first Ajax call.
In addition only one thread can manipulate a Page instance at a time at the server side!
To be able to make two Ajax calls at the same time you need to use different AjaxChannels for them. For example:
... = new AjaxButton("someId") {
#Override protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
{
attributes.setChannel(new AjaxChannel("customName"));
}
}
This way both Ajax requests will be fired to the server, but the AjaxButton's one will be processed only after the AjaxLazyLoadingPanel's one releases the lock on the page instance at the server side. There is no way to execute two threads on the same page instance (i.e. having the same pageId).
If you're dropdown's choices are taking so long to load, you might want to make your AjaxLazyLoadPanel asynchronous by overriding #isContentReady():
private String token;
private List<T> choices;
protected boolean isContentReady()
{
if (token == null) {
token = startLoadingChoices();
} else {
choices = checkChoicesLoaded(token);
}
return choices != null;
}
Since Wicket manages and serializes all pages, your page must not be held as a reference in a thread or a listener in central registry. Keep a token instead and ask for the loading result; #isContentReady() will be polled repeatedly until it returns true.

SelectedItem must always be set to a valid value

I have two viewmodel, on the first viewmodel i have a listbox:
<ListBox x:Name="MainMenu" toolkits:TiltEffect.IsTiltEnabled="True"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
ItemTemplate="{StaticResource MainMenu}"
ItemsSource="{Binding Categories}" Margin="0,97,0,0"
Tap="MainMenu_Tap">
In the second page, i have a listpicker
<toolkit:ListPicker Margin="0,153,0,0" Background="{StaticResource PhoneAccentBrush}" VerticalAlignment="Top"
ItemsSource="{Binding Categories}"
SelectedItem="{Binding Item}"
ItemTemplate="{StaticResource CategorySelector}"
FullModeHeader="Category"
FullModeItemTemplate="{StaticResource FullCategorySelector}"
BorderBrush="{StaticResource PhoneAccentBrush}"/>
What i want is when I navigate to second page, the selected item in the first page will be selected in the second page. But I always get the selected item must always set to a valid value when I navigate to second page.
first viewmodel
private CategoryModel _selectedItem = null;
public CategoryModel SelectedItem
{
get { return _selectedItem; }
set
{
if (_selectedItem == value)
{
return;
}
var oldValue = _selectedItem;
_selectedItem = value;
RaisePropertyChanged("SelectedItem", oldValue, value, true);
}
}
second viewmodel
private CategoryModel _item = null;
public CategoryModel Item
{
get { return _item; }
set
{
if (_item == value)
{
return;
}
var oldValue = _item;
_item = value;
// Update bindings, no broadcast
RaisePropertyChanged("Item");
}
}
EDIT
When I change the listpicker in the second page to Listbox, it works pretty well.
So this is an issue enter link description here. How should I do to get this thing work with the listpicker?
I think you're confusing views and viewmodels.
Because you're binding the selected item in XAML, when the XAML is parsed and the page created it's trying to bind to an item in a collection which hasn't been created yet. This is why the comments on the bug suggest a work around when setting this in code behind.
In your Tap handler on the first page, I assume that you're passing some details of the selected item to the second page. You could, therefore, remove the XAML binding of the selected item and in the OnNavigatedTo event handler on the second page set the binding in code, once you know the ItemsSource has been populated.
Alternatively, you could consider having the two pages share the same viewmodel instance.
ListPicker uses Items.IndexOf to get the index of item instance that should select.
If the instance does not match (it is not an object instance from the collection) the IndexOf will return -1 and the InvalidOperationException is thrown with the message: "SelectedItem must always be set to a valid value".
Override Equals method of the type in the collection and it will work as expected.
Example:
public override bool Equals(object obj)
{
var target = obj as ThisTarget;
if (target == null)
return false;
if (this.ID == target.ID)
return true;
return false;
}
Hope it helps

Search form that redirects to results page

Hello i have the following problem.
I have a search page lets call it search.xhtml and you can search for a bar-code. This value is unique so the result is always one or zero objects from the database
<p:panelGrid columns="1" style="margin:20px;">
<h:form>
<p:messages id="messages" globalOnly="true" showDetail="false" />
<p:message for="barcode" />
<p:inputText id="barcode" value="#{searchForm.barCode}"
required="true" requiredMessage="Value needed" />
<p:commandButton value="search"
action="#{searchForm.searchBarcode}" id="search"/>
</h:form>
</p:panelGrid>
This is the backingbean:
#ManagedBean
#ViewScoped
public class SearchForm extends BasePage {
private Long barCode;
#ManagedProperty("#{daoManager}")
public DaoManager daoManager;
public void setDaoManager(DaoManager daoManager) {
this.daoManager = daoManager;
}
public Long getBarCode() {
return barCode;
}
public void setBarCode(Long barCode) {
this.barCode = barCode;
}
public String searchBarcode() {
//request to dao to get the object
DataList<Data> data = daoManager.findbybarcode(barCode);
if (data.size() == 0) {
this.addMessage(FacesMessage.SEVERITY_ERROR,
"Not Found: " + barCode);
return null;
} else {
getFacesContext().getExternalContext().
getRequestMap().put("id", data.getId());
return "details";
}
}
So if i go to my details page which expect the parameter id this isnt send to the detail page.
backing bean details page:
#ManagedBean
#ViewScoped
public class DetailBean extends BasePage implements Serializable {
#PostConstruct
public void init() {
if (id != null) {
//Go on with the stuff
} else {
addMessage(FacesMessage.SEVERITY_ERROR,"Object not found");
}
}
}
What am i doing wrong? And is this wrong use of JSF? I know i can generate a list and the click on the result but thats not what i want. Also i can take the barcode from the first bean and pass it as a parameter but i want the details page only to accept the id from the objects. So is my thinking wrong? Or is there a solution to get it like this?
If I understand correctly, you wish to pass the ID of the barcode to the details page and yes this is possible.
getFacesContext().getExternalContext().getRequestMap().put("id", data.getId());
The following line is putting the ID parameter into the request that the client just sent you, but the navigation action to details will result in a different request. Try this instead:
return "details?faces-redirect=true&id=" + data.getId();
This will return an HTTP GET navigation action with the ID of the barcode passed as a request parameter in the request.

JSF - Bean level form validation for listbox

I have a form designed in JSF. I need to check if a listbox item is selected. I am doing bean-level validation. My code is:
<webuijsf:listbox style="margin-left:10px;" binding="#user$webreports$frequentvisitorscategories.listBox}"
id="listbox" items="#frequentvisitorscategories.listboxDefaultOptions.options}"
selected="#{user$webreports$frequentvisitorscategories.selectedItemCategory}" rows="10" styleClass="listbox" multiple="true" width="190" required="true" validatorExpression="#{frequentvisitorscategories.category_validate}" />
----------------------------------------------------------------------
public void categories_validate(FacesContext context, UIComponent component, Object value)
{ String selectedValue = (String) value;
if(selectedValue.equals(null))
{
((UIInput)component).setValid(false);
FacesMessage message = new FacesMessage("Please select a category");
context.addMessage(component.getClientId(context), message);
}
}
When I don't select something it does not show the validation message. Does anyone have any opinion about this?
Thanks in advance