This gets called when a button is clicked
#Override
public void onFavoriteIconClicked() {
viewModel.isFavoriteExist(test.getId()).observe(getViewLifecycleOwner(), new Observer<Boolean>() {
#Override
public void onChanged(Boolean aBoolean) {
viewModel.isFavoriteExist(test.getId()).removeObserver(this);
if (aBoolean) {
binding.addToFavorite.setImageResource(R.drawable.non_fav);
viewModel.delete(test);
} else if (getActivity() != null) {
Test test2 = new Test(test.getId(), test.getName());
viewModel.insert(test2);
binding.addToFavorite.setImageResource(R.drawable.fav);
}
}
});
}
If the test object exists in the Favorite database, I have to delete it. After deleting, it calls this again (since it observed a chane) and inserts it again.
It keeps looping infinitely. Is there a better way to implement this or stop this?
It seems like some business logic has entered your view (Activity) class.
Since LiveData & Room are meant to be used when receiving updates about Database changes is needed, and your use of the DB is not requiring constant updates, I would suggest going with a more direct approach.
First, Remove the use of LiveData from your Database. Use simple return values.
Your view (Activity/Fragment) can then tell the view model that a button was clicked.
#Override
public void onFavoriteIconClicked() {
viewModel.onFavoriteClicked()
}
The view will observe the view model in order to receive the correct icon to show.
Something like:
viewModel.favoriteIcon.observe(getViewLifecycleOwner(), new Observer<Integer>() {
#Override
public void onChanged(Integer iconResId) {
binding.addToFavorite.setImageResource(iconResId)
}
}
Now the viewModel can handle the logic (or better add a Repository layer - See Here)
Upon click, Check if entry exist in DB.
If exists: remove it from DB and set favoriteIcon value:
favoriteIcon.setValue(R.drawable.non_fav)
If doesn't exist: Add it to DB and set favoriteIcon value.
favoriteIcon.setValue(R.drawable.fav)
For a good tutorial about using Room & LiveData - as well as doing so using the View/ViewModel/Repository pattern, check this link
Related
sorry if this is a convoluted question. Working on creating an app for a college course and I'm running into (what appears to be) a race condition in my OnCreate method.
TL;DR - sometimes my spinner populates and I can get an index from it. Sometimes it's not populated yet when trying to get a specific index. Details and code below.
The app is a "course scheduler" for a college student.
I'm creating an Activity that displays existing course information and allows you to edit it. In the OnCreate method for this Activity, I am filling a spinner for "Mentors" for the course and a spinner for which "Term" the course belongs in. This information is being pulled from a Room DB.
I have a seperate activity for a new course and for editing a course. For the "new course" activity, everything works fine. I getAllMentors() or getAllTerms() successfully and fill the spinner list.
For the "Edit Course" Activity, there's an extra step involved and it seems to be causing me some issues.
When editing a course, I pass the intent from the originating Activity with all the necessary EXTRAS. This is successful.
In OnCreate for EditCourseActivity, I do the following:
I get the mentorID from the EXTRA that's passed in from the originating Activity.
I access my MentorViewModel and call my getAllMentors() method which returns LiveData> of all mentors in the db.
because it returns LiveData, I use an observer and loop through the LiveData adding the Name of each mentor to a List and the
entire mentor to a List.
I populate my spinner with the information in List full of mentor names.
then I do a for loop, looping through List looking for one that has the same id as what I grabbed form the EXTRA in step 1.
If I find a match in that list, I call a getMentorName() method to snag their name as a string.
I have a methond getIndex(spinner, string) that will loop through the provided spinner, trying to find a match for the string that's
passed in (mentors name) that I grabbed that should match the ID of
the mentor assigned to the course. This method returns index location
of the matched string in the spinner.
I set the spinner selection to the index found.
I do basically the same process for term.
Me being a new developer, I'm not used to OnCreate running the code synchronously.
Because of this, it appears that I have a race condition somewhere between populating the List of mentor names that populates the spinner, and calling my getIndex() method.
Sometimes the spinner is populated and getIndex works properly and sets the correct mentor. Sometimes the spinner is empty and my getIndex() returns -1 (which it should do in a no-find situation) that populates the spinner with the first item in the list (once it's populated).
protected void onCreate(Bundle savedInstanceState) {
//////////////////////////Handling Mentor spinner menu/////////////////////////////////////////////////
int mentorId = courseData.getIntExtra(EXTRA_COURSE_MENTOR_ID, -1);
final ArrayAdapter<String> sp_CourseMentorAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, mentorNameList);
sp_CourseMentorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
sp_CourseMentor.setAdapter(sp_CourseMentorAdapter);
final MentorViewModel mentorViewModel = ViewModelProviders.of(this).get(MentorViewModel.class);
//Mentor test = mentorViewModel.getMentorById(mentorId);
mentorViewModel.getAllMentors().observe(this, new Observer<List<Mentor>>() {
#Override
public void onChanged(#Nullable List<Mentor> mentorList) {
if (mentorList != null) {
for (Mentor m : mentorList) {
mentorNameList.add(m.getMentor_name());
mentorListMentor.add(m);
}
}
sp_CourseMentorAdapter.notifyDataSetChanged();
}
});
for(Mentor m: mentorListMentor){
if (m.getMentor_id()==mentorId){
String test = m.getMentor_name();
int spinnerSelectionM2 = getIndexM(sp_CourseMentor, test);
sp_CourseMentor.setSelection(spinnerSelectionM2);
}
}
Is there a way to get them to run asynchronously? Somehow to get the observer doing my getAllMentors() to complete first and populate the spinner, THEN have the for loop run?
Or a better way to handle this?
Thanks in advance.
Room always runs the code on a separated thread, not the Main/UI thread. You can change that behavior with
allowMainThreadQueries()
after initializating your database. This will make the query run first, populate your list and then run your for-loop code. I do not recommend this approach, since it is a bad practice to make queries on the UI thread.
You have two options:
Change your foor loop to a function and call it after adding the values from the observer:
mentorViewModel.getAllMentors().observe(this, new Observer<List<Mentor>>() {
#Override
public void onChanged(#Nullable List<Mentor> mentorList) {
if (mentorList != null) {
for (Mentor m : mentorList) {
mentorNameList.add(m.getMentor_name());
mentorListMentor.add(m);
}
lookForMentor();
}
}
});
private void lookForMentor() {
for(Mentor m: mentorListMentor){
if (m.getMentor_id()==mentorId){
String test = m.getMentor_name();
int spinnerSelectionM2 = getIndexM(sp_CourseMentor, test);
sp_CourseMentor.setSelection(spinnerSelectionM2);
}
}
}
Put the for inside the observer, change the Room DAO to return a List and use LiveData on your own viewmodel:
MentorViewModel.java:
MentorViewModel extends ViewModel {
private MutableLiveData<List<Mentor>> _mentorsLiveData = new MutableLiveData<List<Mentor>>();
public LiveData<List<Mentor>> mentorsLiveData = (LiveData) _mentorsLiveData;
void getAllMentors(){
//room db query
_mentorsLiveData.postValue(mentorsList);
}
}
EditActivity.java:
mentorsViewModel.getAllMentors();
mentorViewModel.mentorsLiveData.observe(this, new Observer<List<Mentor>>() {
#Override
public void onChanged(#Nullable List<Mentor> mentorList) {
mentorsListMentor.addAll(mentorList);
sp_CourseMentorAdapter.notifyDataSetChanged();
for(Mentor m: mentorListMentor){
if (m.getMentor_id()==mentorId){
String test = m.getMentor_name();
int spinnerSelectionM2 = getIndexM(sp_CourseMentor, test);
sp_CourseMentor.setSelection(spinnerSelectionM2);
}
}
}
}
});
I am kinda new to Android programming. I am building a simple application and I have a "follow-unfollow" concept on it. What I simply want is, if the current user follows the user he/she is exploring, I want him/her to see "unfollow" button. If not following, there should be a "follow" button. On my UserProfileActivity class I have a method called onPrepareOptionsMenu() and inside this method I can set the buttons.
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem follow = menu.findItem(R.id.action_follow);
MenuItem unfollow = menu.findItem(R.id.action_unfollow);
Bundle bundle = this.getIntent().getExtras();
if(isFollowing(bundle.getString("userid")) == true){
follow.setVisible(false);
unfollow.setVisible(true);
}
else{
follow.setVisible(true);
unfollow.setVisible(false);
}
return super.onPrepareOptionsMenu(menu);
}
Also, I have another method called isFollowing() and it returns a boolean "true" if current user follows the other user, it returns "false" if not. It is the simplest way that I have thought to solve this issue.
public boolean isFollowing(String userID){
isFollowingResult = false;
firebaseDatabase = FirebaseDatabase.getInstance();
databaseReference = firebaseDatabase.getReference();
final DatabaseReference followingData = databaseReference.child("followingData");
followingData.child(currentUser.getUid()).child(userID).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists())
isFollowingResult = true;
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return isFollowingResult;
}
When I run this, and when I click on a user's profile onPrepareOptionsMenu() method is called and inside it isFollowing() method is called. The problem is, it does not wait for the isFollowing() method to run and finish running and it immediately sees it as "false" and always shows "follow" button. Any suggestion would be appreciated.
Thanks.
Make follow and unfollow the fields of the class. Then change their visibility in onDataChange(DataSnapshot) method.
There are multiple issues with the code.
Firstly, your isFollowing() function is setting up the listener, on the data field, but it'll only get called when the data changes. In this case, you may only want to read the data once:
https://firebase.google.com/docs/database/android/read-and-write#read_data_once
Secondly, the use of a listener implies asynchronicity. Meaning, you'll need to wait until you get the callback later in order to get the value you want.
The ideal solution in order to maintain responsiveness of your app is to maintain a local "copy" of the value in your database with a listener that constantly updates that value. That way, you can query the state of your variable quickly (since it's stored/replicated locally) and still be up to date with your database (with the listener).
This will also prevent each "read" from going all the way to the service and back and also remove the need for your UI to wait to render correctly (accurately).
So I've come to a point where I need to implement an SQLite database for my app.
Following "The Busy Coder's guide to Android Development" I have created a DatabaseHelper class that extends SQLiteOpenHelper.
One of my use cases is to run a query against the database and display the results on a ListView within a Fragment (I use fragments from the support library).
From what I understand, using managedQuery() is not really appropriate and even if it were it isn't recommended due to the fact that some of the logic encapsulated inside this method is actually executed on the main thread, specifically reQuery() which to my understanding is performed when the Activity is restarted.
So I've been trying to get acquainted with the Loader class for the first time, only to see this:
"The only supplied concrete implementation of a Loader is CursorLoader, and that is only for use with a ContentProvider"
My initial thought was to implement my own content provider and perhaps prevent other apps from getting access to it, then I read the following in the ContentProvider documentation via developer.android.com:
"You don't need a provider to use an SQLite database if the use is entirely within your own application."
I've also been playing with this:
https://github.com/commonsguy/cwac-loaderex
Yet I am not familiar with this project, and not sure if it can be used on a production environment.
So, right now all I can think of is creating a bunch of AsyncTask instances within my Fragment and manage their lifecycle appropriately, make sure they're cancelled whenever needed and whatnot.
Are there any other options?
I think implementing content provider is a good idea, no matter that data will not be accessible outside of the application. It provides very modern interface and from my experience makes your application error prone to database locking issues and other db-specific problems.
I've implemented it in my latest project and I was very happy to use it.
You can extend Loader class in order to perform other Async work such as loading directly from your DB.
Here is an example of that
Edit: added A better example of Loader usage.
Finally managed to find the tutorial that really helped me understand how things work.
By extending the loader class you can avoid messing with content observers and its quite easy to implement (at last)
the modifications that need to be taken in your code are
Add implementation of LoaderManager.LoaderCallbacks<D>, where D is your data list (snippet 1)
Create your loader class, copy snippet 2, and add the loading of your data from the DB
Finally call the loaders 1 call for init and then for refreshing the restart call. (snippet 2 & 3)
Snippet 1: How to "link" the loader with your fragment:
public static class AppListFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<List<SampleItem>> {
public Loader<List<SampleItem>> onCreateLoader(int id, Bundle args) {
//...
return new SampleLoader (getActivity());
}
public void onLoadFinished(Loader<List<SampleItem>> loader, List<SampleItem> data) {
// ...
mAdapter.setData(data);
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
// ...
}
public void onLoaderReset(Loader<List<SampleItem>> loader) {
// ...
mAdapter.setData(null);
// ...
}
/* ... */
}
Snippet 2: Schema of your custom loader: (I have double commented the observer things as I believe that it is quite difficult to implement it from the beginning and you can simple recall the loader without messing with that automated refreshing)
public class SampleLoader extends AsyncTaskLoader<List<SampleItem>> {
// We hold a reference to the Loader’s data here.
private List<SampleItem> mData;
public SampleLoader(Context ctx) {
// Loaders may be used across multiple Activitys (assuming they aren't
// bound to the LoaderManager), so NEVER hold a reference to the context
// directly. Doing so will cause you to leak an entire Activity's context.
// The superclass constructor will store a reference to the Application
// Context instead, and can be retrieved with a call to getContext().
super(ctx);
}
/****************************************************/
/** (1) A task that performs the asynchronous load **/
/****************************************************/
#Override
public List<SampleItem> loadInBackground() {
// This method is called on a background thread and should generate a
// new set of data to be delivered back to the client.
List<SampleItem> data = new ArrayList<SampleItem>();
// TODO: Perform the query here and add the results to 'data'.
return data;
}
/********************************************************/
/** (2) Deliver the results to the registered listener **/
/********************************************************/
#Override
public void deliverResult(List<SampleItem> data) {
if (isReset()) {
// The Loader has been reset; ignore the result and invalidate the data.
releaseResources(data);
return;
}
// Hold a reference to the old data so it doesn't get garbage collected.
// We must protect it until the new data has been delivered.
List<SampleItem> oldData = mData;
mData = data;
if (isStarted()) {
// If the Loader is in a started state, deliver the results to the
// client. The superclass method does this for us.
super.deliverResult(data);
}
// Invalidate the old data as we don't need it any more.
if (oldData != null && oldData != data) {
releaseResources(oldData);
}
}
/*********************************************************/
/** (3) Implement the Loader’s state-dependent behavior **/
/*********************************************************/
#Override
protected void onStartLoading() {
if (mData != null) {
// Deliver any previously loaded data immediately.
deliverResult(mData);
}
// Begin monitoring the underlying data source.
////if (mObserver == null) {
////mObserver = new SampleObserver();
// TODO: register the observer
////}
//// takeContentChanged() can still be implemented if you want
//// to mix your refreshing in that mechanism
if (takeContentChanged() || mData == null) {
// When the observer detects a change, it should call onContentChanged()
// on the Loader, which will cause the next call to takeContentChanged()
// to return true. If this is ever the case (or if the current data is
// null), we force a new load.
forceLoad();
}
}
#Override
protected void onStopLoading() {
// The Loader is in a stopped state, so we should attempt to cancel the
// current load (if there is one).
cancelLoad();
// Note that we leave the observer as is. Loaders in a stopped state
// should still monitor the data source for changes so that the Loader
// will know to force a new load if it is ever started again.
}
#Override
protected void onReset() {
// Ensure the loader has been stopped.
onStopLoading();
// At this point we can release the resources associated with 'mData'.
if (mData != null) {
releaseResources(mData);
mData = null;
}
// The Loader is being reset, so we should stop monitoring for changes.
////if (mObserver != null) {
// TODO: unregister the observer
//// mObserver = null;
////}
}
#Override
public void onCanceled(List<SampleItem> data) {
// Attempt to cancel the current asynchronous load.
super.onCanceled(data);
// The load has been canceled, so we should release the resources
// associated with 'data'.
releaseResources(data);
}
private void releaseResources(List<SampleItem> data) {
// For a simple List, there is nothing to do. For something like a Cursor, we
// would close it in this method. All resources associated with the Loader
// should be released here.
}
/*********************************************************************/
/** (4) Observer which receives notifications when the data changes **/
/*********************************************************************/
// NOTE: Implementing an observer is outside the scope of this post (this example
// uses a made-up "SampleObserver" to illustrate when/where the observer should
// be initialized).
// The observer could be anything so long as it is able to detect content changes
// and report them to the loader with a call to onContentChanged(). For example,
// if you were writing a Loader which loads a list of all installed applications
// on the device, the observer could be a BroadcastReceiver that listens for the
// ACTION_PACKAGE_ADDED intent, and calls onContentChanged() on the particular
// Loader whenever the receiver detects that a new application has been installed.
// Please don’t hesitate to leave a comment if you still find this confusing! :)
////private SampleObserver mObserver;
}
Snippet 3: How to call the loader for the first time (ONLY)
// Initialize a Loader with an id. If the Loader with this id is not
// initialized before
getLoaderManager().initLoader(LOADER_ID, null, this);
Snippet 4: For refreshing data (recalling the query)
// Check if the loader exists and then restart it.
if (getLoaderManager().getLoader(LOADER_ID) != null)
getLoaderManager().restartLoader(LOADER_ID, null, this);
Reference:
Snippet 1 : usage of loader extracted from here
Snippet 2 : here for more info and logic read throughout the hole article
Snippet 3 & 4: are just loader usage.
Full code of these is also uploaded by the creator on github
I recommend OrmLite library, a lightweight Object Relational Mapping that can work for Android. This library will make your life easier . You don't need to create or update database by hand, you don't need to focus on managing database connection, all queries select, insert, update will be easier with a DAO approach (usually you don't need to write your own sql query) and a lot of features. They have some examples that you can start with.
And if you want to use the Loader, there is a ORMLite Extras , additional functionality for ORMLite available on github (You can use the support package which is compatible with support android library). Here is an example usage on my previous project:
public class EventsFragment extends Fragment implements LoaderCallbacks<Cursor>{
private static final int LOADER_ID = EventsFragment.class.getName().hashCode();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getLoaderManager().initLoader(LOADER_ID, null, this);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View layoutRoot = inflater.inflate(
R.layout.fragment_events, null);
lvEvents = (ListView) layoutRoot.findViewById(R.id.lvEvents);
adapter = new EventAdapter(getActivity(), null, null);
lvEvents.setAdapter(adapter);
return layoutRoot;
}
#Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
try {
PreparedQuery<Event> query = getDatabaseHelper().getEventDao().getQuery();
return getDatabaseHelper().getEventDao().getSQLCursorLoader(query );
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) {
adapter.swapCursor(cursor);
try {
adapter.setQuery(getDatabaseHelper().getEventDao().getQuery());
} catch (SQLException e) {
e.printStackTrace();
}
}
#Override
public void onLoaderReset(Loader<Cursor> arg0) {
adapter.swapCursor(null);
}
private OrmliteDatabaseHelper getDatabaseHelper(){
return ((MainActivity)getActivity()).getDatabaseHelper();
}
}
The adapter
public class EventAdapter extends OrmliteCursorAdapter<Event>{
public EventAdapter(Context context, Cursor c, PreparedQuery<Event> query) {
super(context, c, query);
}
#Override
public void bindView(View itemView, Context context, Event item) {
TextView tvEventTitle = (TextView) itemView.findViewById(R.id.tvEventTitle);
TextView tvEventStartDate = (TextView) itemView.findViewById(R.id.tvEventStartDate);
tvEventTitle.setText(item.getTitle());
tvEventStartDate.setText(item.getFormatStartDate());
}
#Override
public View newView(Context context, Cursor arg1, ViewGroup arg2) {
LayoutInflater inflater = LayoutInflater.from(context);
View retView = inflater.inflate(R.layout.event_item_row, arg2, false);
return retView;
}
}
And a custom Dao which provides PreparedQuery for cursor adapter above:
public interface IEventDao extends Dao<Event, Integer>{
PreparedQuery<Event> getQuery() throws SQLException;
OrmliteCursorLoader<Event> getSQLCursorLoader(Context context, PreparedQuery<Event> query) throws SQLException;
}
public class EventDao extends AndroidBaseDaoImpl<Event, Integer> implements IEventDao{
public EventDao(ConnectionSource connectionSource) throws SQLException {
super(connectionSource, Event.class);
}
public EventDao(ConnectionSource connectionSource,
DatabaseTableConfig<Event> tableConfig) throws SQLException {
super(connectionSource, tableConfig);
}
#Override
public PreparedQuery<Event> getQuery() throws SQLException{
return queryBuilder().prepare();
}
}
Hope this can help!
If your database contains thousands of records consider madlymad's answer
If not keep it stupid and simple, use SQLiteOpenHelper and create a method that returns you your data as array of strings or define your one objects.
Also use custom/regular CursorAdapter or ArrayAdapter.
I use the SQLiteOpenHelper to create my database. I have made Java classes for all the tables and when I get the data from my database I put it in an ArrayList. The ArrayList I then load into the adapter of the Listview.
I've got the following code:
public Button getBtnSubmit(com.vaadin.ui.Button.ClickListener l) {
if (null != l) {
btnSubmit.addListener(l);
}
return btnSubmit;
}
public Table getTableCompany(HeaderClickListener hl) {
if (null != hl) {
tableCompany.addListener(hl);
}
return tableCompany;
}
I would like to add a listener that fires when I select a (different) row in the table.
This so that I can refresh some other controls with the table data, which listener should I use?
addListener is deprecated now. Use the following instead.
table.addItemClickListener(new ItemClickEvent.ItemClickListener() {
#Override
public void itemClick(ItemClickEvent itemClickEvent) {
System.out.println(itemClickEvent.getItemId().toString());
}
});
I would go for ItemClickListener:
table.addListener(new ItemClickEvent.ItemClickListener() {
#Override
public void itemClick(ItemClickEvent event) {
//implement your logic here
}
});
edit: For Vaadin 7+, use addItemClickListener method instead of addListener.
You want to add a ValueChangeListener
If you use the ValueChangeListener don't forget to set
table.setImmediate(true);
This means that the browser will report a change on selection immediately. If you don't set this your listener is not called.
Read https://vaadin.com/book/-/page/components.table.html, section 5.15.1 "Selecting Items in a Table". You want to add a Property.ValueChangeListener.
Many of these answers are both correct, and incorrect.
If you need to get the selected items in response to the click, register a ValueChangeListener. Calling getValue() to retrieve the selection from the ItemClickListener might be 1 item behind in a MultiSelect list. For example, the set of items won't include/exclude the item triggering the callback. You will not have a reference to the clicked item however.
If you simply want to respond to a click on an item, and do not need to consider the current selection state, register an ItemClickListener instead. This way you will know what item was actually clicked.
I have a :
Client Class
ListView
TextField
I need to populate my ListView in order to form a table:
WORKING CODE:
clientModel = new LoadableDetachableModel() {
#Override
protected Object load() {
return Client.getClientListByCompanyName(searchClientInput.getValue());
}
};
searchClientInput.setModel(new Model<String>());
searchClientInput.add(new AjaxFormComponentUpdatingBehavior("onkeyup") {
#Override
protected void onUpdate(AjaxRequestTarget target) {
target.add(clientListViewContainer);
}
});
clientListView = new ListView<Client>(CLIENT_ROW_LIST_ID, clientModel) {
#Override
protected void populateItem(ListItem<Client> item) {
Client client = item.getModelObject();
item.add(new Label(CLIENT_ROW_COMPANY_CNPJ_ID, client.getCompanyName()));
item.add(new Label(CLIENT_ROW_COMPANY_NAME_ID, client.getCompanyCnpj()));
}
};
clientListViewContainer.setOutputMarkupId(true);
clientListViewContainer.add(clientListView);
add(clientListViewContainer);
Now, in my HTML, I have a TextField. Whenever an user types something in this TextField, a select will be made in the database with whatever he typed. So for each word, a select is made, and the table needs to be updated. I am guessing I will need to use AJAX and possibly a Model. I'm kind of lost about how I can do this, if someone can provide me examples I would be very grateful.
EDIT: New code that is throwing exception: Last cause: Attempt to set model object on null model of component: searchClientForm:searchClientInput
EDIT 2: Ok so the exception was that my TextField didn't had a model to bind data to. So what I did was: searchClientInput.setModel(new Model<String>());
I also had a problem with the event. Using onkeydown was working, but not as intended. I had Company Name 1-4. If I typed Company Name 1, I would need to press one key again so the table would get updated. With onkeyup this don't happens. Thanks for the help.
You could give the ListView a LoadableDetachableModel which provides the selected clients matching your TextField's value.
Use an AjaxFormComponentUpdatingBehavior on your TextField which add a parent of the ListView to the request target (don't forget #setOutputMarkupId().
I believe the best way to perform what you want (which is repainting a table/list at each input change --> DB access) is with a DataView and a DataProvider.
A DataView is just like the ListView component except it uses an IDataProvider to get the data you want to present. You are able to implement the DataProvider so it accesses your DB, and you can add restrictions (where clauses) to the DataProvider.
[this is more like pseudo-code]
public final class MyDataProvider<T> extends SortableDataProvider<T> {
// ...
Set filters;
// filters is the set where the restrictions you want to apply are stored
...
#Override
public Iterator<T> iterator(int first, int count) {
// DAO (Data Access Object) access to DB
// ...
return dao.findByRestrictions(filters).iterator();
}
...
}
Now on the ajax event on your input component you are able to update the filter being used in the DataProvider, and in the the next repaint of the DataView, the provider will "pull" the data matching the restrictions defined in the filter.
Hope it helps. Best regards.