I have some trouble using javaFX to fill a tableColumn with some data according to a selected index from an other table.
the Table starts empty.
and then I want to fill it when the user press a button. (so far, so good)
here's what the button controller looks like :
#FXML
private void handleNextRequest() {
int selectedIndex = headingTable.getSelectionModel().getSelectedIndex();
if (selectedIndex >= 0)
mainApp.updateEntity(headingColumn.getCellData(selectedIndex));
entityTable.setItems(mainApp.getEntity());
}
So this calls a function from the main class which update my observable list.
The selectedIndex parameter is used to determine which data I have to load in the list (those data are located on a database which I can access via a web service, hence the "api" (which works fine)).
So here's what this function looks like :
public void updateEntity(String header){
try {
this.entity.clear();
int i = 0;
while(header != heading.get(i).getName()){
i++;
}
api.getEntity(new URL(heading.get(i).getURL()), this.entity, primaryStage);
} catch(MalformedURLException e){}
}
And up to this point everything is functional. when pressing the button the function is called properly and the observable list (entity) is updated correctly. (checked and re-checked)
and then... boom.
the "setItems" function (back to the button controller) doesn't seems to like whatever I've done and throw a NullPointerException.
If someone could help me understand what the problem might be here I would be delighted !
EDIT :
here's the initialize code that I have :
I have this in the initialize method :
#FXML
private void initialize() {
headingColumn.setCellValueFactory(CellData -> CellData.getValue().nameProperty());
entityColumn.setCellValueFactory(CellData -> CellData.getValue().nameProperty());
showTableDetails(null);
headingTable.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> showTableDetails(newValue));
}
and both entityTable and entityColumn declared properly.
Okay I figured it out.
Pretty dumb mistake but the fx:id="entityTable" was missing in the fxml file.
thank you for helping me realizing that !
Related
I'm creating a media player in JavaFX. In one of my methods, I've created a way to search for metadata in a Media-file and then display it in ImageView. Works fine first time, but as soon as I want to call it again using another Media object, the image doesn't show up. I'm a bit confused and inexperienced, but I think that perhaps I need to reset/stop the listener before going to next object in line?
So my question is! How do you remove the listener when "image" has been found, what do you type to make it happen?
If you think that there's another reason why my image wont display the second time, please let me know as well.
Thanks on purpose.
private void displayAlbumCover (){
// Will start to show a blank CD
File file = new File("src/sample/images/blank_cd.jpeg");
Image image = new Image(file.toURI().toString());
albumCoverView.setImage(image);
// However if an album cover is found in the meta-data it will be displayed
ObservableMap<String,Object> meta_data=me.getMetadata();
meta_data.addListener((MapChangeListener<String, Object>) ch -> {
if(ch.wasAdded()){
String key=ch.getKey();
Object value=ch.getValueAdded();
switch(key){
case "image":
albumCoverView.setImage((Image)value);
break;
}
}
});
}
ObservableMap has removeListner method. You can keep the listener instance to variable and then remove it later.
private MapChangeListener<String, Object> listener;
private void displayAlbumCover (){
// ...
this.listener = //...
meta_data.addListener(listener);
}
private void removeListener() {
me.getMetadata().removeListener(this.listener);
}
https://docs.oracle.com/javase/8/javafx/api/javafx/collections/ObservableMap.html#removeListener-javafx.collections.MapChangeListener-
Okay so i am very rusty on my java and even more on javafx. so i got a choicebox "categoryDrop" that when the value of the choicebox change i want to trigger this event that then takes the value of the choicebox and compare to an object "Folder" categorylist wich is an attribute it has.
here is my code
#FXML
private void folderByCategory(ActionEvent event) {
System.out.println("här1");
TreeItem<DocumentObject<?>> treeRoot = new TreeItem<>(new Folder());
for (Folder folder : logic.getFolderList()) {
if(f.getCategoryList().contains(categoryDrop.valueProperty())){
System.out.println("här2");
TreeItem<DocumentObject<?>> newFolders = new TreeItem<>(folder);
for(FileReference file : folder.getFileList()){
System.out.println(file.getName());
TreeItem<DocumentObject<?>> fileNode = new TreeItem<>(file);
newFolders.getChildren().add(fileNode);
}
treeRoot.getChildren().add(newFolders);
treeRoot.setExpanded(true);
}
treeNav.setRoot(treeRoot);
}
}
But then when i looked in scenebuilder i didnt see any good way to implement the method so it triggers when it changes. Anyone know a better way to do this? should i use a listener instead maybe?
ChoiceBox has an onAction property, so in FXML you can simply assign this controller method to this property:
<ChoiceBox fx:id="categoryDrop" onAction="#folderByCategory" />
Unfortunately, the current version of Scene Builder does not support this property, so you cannot set this directly from Scene Builder. There is a current issue filed for this.
Some workarounds are:
Edit the FXML manually to add the onAction attribute, as above.
Use a ComboBox instead of a ChoiceBox. The functionality is similar (though not identical) and a ComboBox will likely do what you need. Scene Builder does support the onAction property of a ComboBox.
Register the handler in the controller's initialize() method instead. All you need is
#FXML
private ChoiceBox<...> categoryDrop ;
public void initialize() {
categoryDrop.setOnAction(this::folderByCategory);
// existing code ...
}
#FXML
private void folderByCategory(ActionEvent event) {
// existing code...
}
In this app that i am building i have in one stage a TableView and a few buttons. When i click on one of the buttons it opens a new window that is made of TextFields and a "OK" button. When i click on the OK, i need to insert that data into a Table.
So, i know how to insert a row into a TableView from a controller that controlles that TableView, but now i need to insert it from controller of another window. I tried everything, and it doesent work. I tried to get an instance of TableController and pass the data to its method, then i tried to pass ObservableList to a NewWindowController, and that also doesent work. I'm out of ideas. Can someone help me with this, i would appreciate it. Thank you.
Part of the code:
public class MainController {
#FXML public TableView<Film> tabel;
public TableView<Film> getTabel(){
return tabel;
}
}
newWindow'sController:
public UnosController(){
#FXML protected void insert(ActionEvent e){
Film film = new Film(funosNaziv.getText(), funosZanr.getText(),Integer.valueOf(funosGodina.getText()));
TableView<Film> tabel = mainController.getTabel();
ObservableList<Film> data = tabel.getItems();
data.add(film);
}
}
That is my last try. Doesent work.
I have been trying to add a remove button so that it removes a selected row in my tableview. My problem is slightly different from those i have found elsewhere. My problem lies behind the fact that in my application i have used 1 FXML file as the basis of several different interfaces. When i initialize 1 of these and use the functionality of the remove button it removes the tableView rows fine and how it is supposed to. But when i initialize a second interface (still using the same FXML and henceforth same variable names) it only lets me delete items in the 2nd tableView and not in the first. I have a good idea as to why this is, but i do not know how to fix it.
Here are a few methods i have tried:
public void removeProject(ActionEvent event){
int index = projectTable.getSelectionModel().getSelectedIndex();
if(index >=0){
projectTable.getItems().remove(index);
}else
//Show warning
}
}
Another different approach:
public void removeProject(ActionEvent event){
ObservableList<Project> currentlySelected, allProjects;
currentlySelected = projectTable.getSelectionModel().getSelectedIndex();
allProjects = projectTable.getItems();
currentlySelected.forEach(allProjects::remove);
}
Also please keep in mind that both of these methods work fine until i initialize a second tableView. After this point the value i get from both my ObservableList<Project> currentlySelected and my int Indexare -1 when i am trying to select a row in a table which isn't the most recent initialization of the interface. Sorry if it sounds a bit confusing but it is a bit confusing, if i can clear anything up ill add an edit later
Cheers
Edit 1:
Here is an example where i am trying to remove from the table based on which interface it is in currently:
ObservableList<Project> itemsSelected;
switch(counter){
case 1:
itemsSelected = projectTable.getSelectionModel().getSelectedItems();
itemsSelected.forEach(projTableStorage.getProj1()::remove);
break;
case 2:
itemsSelected = projectTable.getSelectionModel().getSelectedItems();
itemsSelected.forEach(projectTableStorage.getProj2()::remove);
break;
A few things to note:
The projectTableStorage.getProj() is used to store all of the data in each table, the returned value is an ObservableList and i use this to set the items of the table whenever that interface is loaded so the data is not lost when swapping between interfaces, perhaps there is more efficient ways to go about it, this is just how i did it
There are 7 of these interfaces, i am just testing with 2 to make testing shorter and simpler for now at least
Edit 2:
Loading FXML files:
public AnchorPane initLayouts(FXMLLoader loader, AnchorPane projectLayout, MainLayoutController mainLay) throws IOException{
loader = new FXMLLoader();
loader.setLocation(getClass().getResource("/control/view/ProjectLayout.fxml"));
loader.setController(mainLay);
projectLayout = (AnchorPane) loader.load();
return projectLayout;
}
in MainLayoutController:
public AnchorPane loadLayout(AnchorPane projectLayout, Project project, FXMLLoader loader)throws IOException{
projectLayout = project.initLayouts(loader, projectLayout, this);
return projectLayout;
}
load layout is called whenever a button is pressed
Edit 3:
Here is the 'removeProjec' code again
public void removeProject(ActionEvent event){
ObservableList<Project> itemsSelected, currentProject;
itemsSelected = getProjectTable().getSelectionModel().getSelectedItems();
itemsSelected.forEach(getProjectTable().getItems()::remove);
System.out.println("value of itemsSelected is : " + itemsSelected);
}
and my project table storage:
ObservableList<Project> project1= FXCollections.observableArrayList();
ObservableList<Project> project2 = FXCollections.observableArrayList();
public void setProject1(Project project){
project1.add(project);
}
public void setProject2(Project project){
project2.add(project);
}
public ObservableList<Project> getProject1(){
return project1;
}
public ObservableList<Project> getProject2(){
return project2;
}
And also just in case the getProjectTable method(Tried with and without annotation):
#FXML
public TableView<Project> getProjectTable(){
return projectTable;
}
Edit 4:
public void createNewProjectLayout(ActionEvent event) throws IOException{
if(event.getTarget() == newProjectLayoutButton1){
projectLayout1 = loadOrReloadProjectLayout(newProjectLayoutButton1, project1, projectLayout1, 1);
setTable(Counter);
}else if(event.getTarget() == newProjectLayoutButton2){
projectLayout2 = loadOrReloadProjectLayout(newProjectLayoutButton2, project2, projectLayout2, 2);
setTable(Counter);
}
A few things to note:
The loadOrReload is simply to load the file the first time it is clicked using the loadLayout method previously mentioned, and then reload the result of loadLayout for the next time it is pressed
The setTable is used to set any data stored previously in the table to be put in the table again using the observable lists from the ProjectTableStorage class
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.