Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uniform handling of JPA and DTO entities in standard list/detail views #2788

Closed
knstvk opened this issue Jan 23, 2024 · 2 comments · Fixed by #2819
Closed

Uniform handling of JPA and DTO entities in standard list/detail views #2788

knstvk opened this issue Jan 23, 2024 · 2 comments · Fixed by #2819
Assignees
Milestone

Comments

@knstvk
Copy link
Contributor

knstvk commented Jan 23, 2024

Currently due to #1330, #1357 and #1401 the create/edit actions and standard detail view behave differently for DTO entities:

  • The entity ID is not passed in URL route, which makes impossible to reload the entity in detail view as it is done for JPA.
  • The standard template for DTO detail view has ugly override of findEntityId() method which normally extracts ID from URL.

To simplify work with DTO entities loaded from external data sources the UI framework should make no difference between JPA and DTO entities. The only thing the developer should do for DTO is to define a load delegate.

The mechanism of working with in-memory-only DTO entities like security models needs to be updated accordingly.

@knstvk
Copy link
Contributor Author

knstvk commented Feb 5, 2024

Added methods

  • EntityStates.setNew(Object entity, boolean isNew) - manages the "New" state of the entity instance.
  • RemoveAction.setDelegate(), RemoveOperation.RemoveBuilder.withRemoveDelegate() - delegate to be invoked instead of DataManager to remove the entities from a storage
  • StandardDetailView.setReloadEdited(boolean) - whether the edited entity should be reloaded before setting to the data container. True by default.

Removed methods

  • Protected StandardDetailView.entityCanBeLoaded()
  • Protected DetailViewNavigationProcessor.isNeedToSetEntityToEdit()
  • Protected DetailViewNavigationProcessor.getEntityToEdit()

Behavior changes

The framework now doesn't make any difference between JPA and DTO entities when navigating to a detail view: it passes the entity ID in the route parameter. The detail view for DTO entity is supposed to get this ID and load the entity instance from some data storage using the load delegate. If the new constant is passed instead of ID, the view creates a new instance.

If the whole entity instance is passed instead of ID (e.g. when opening in dialog window), EntityStates.isNew() is used to distinguish between Edit and Create mode. Consequently, it's important to set the entity in the not-new state after loading it from a storage. For a DTO entity it can be done using the new EntityStates.setNew() method, for JPA entity it's done by the standard JPA data store implementation.

If the edited entity should not be reloaded from the data storage before setting to the data container, call setReloadEdited(false) in the detail view constructor or InitEvent handler. This is the case for DTO entities existing purely in memory and not mapped directly to external data, like Security or JMX Console model entities.

Recommendations

Opening DTO entity detail view

Previously, the framework automatically adjusted the process of opening detail views for DTO entities. In particular, when navigating, it didn't provide the entity ID in route parameters and instead passed the entity instance in AfterViewNavigationEvent handler.

If you have used this standard approach for navigation and want to keep it, you should replicate the old behavior in create/edit actions of the list view as in the following example for the Phone DTO entity:

@Autowired  
private ViewNavigators viewNavigators;
@ViewComponent  
private DataGrid<Phone> phonesDataGrid;

@Subscribe("phonesDataGrid.create")  
public void onPhonesDataGridCreate(final ActionPerformedEvent event) {  
    viewNavigators.detailView(phonesDataGrid)  
            .withViewClass(PhoneDetailView.class)  
            .withRouteParameters(RouteParameters.empty())  
            .withAfterNavigationHandler(afterViewNavigationEvent -> {  
                Phone phone = metadata.create(Phone.class);  
                afterViewNavigationEvent.getView().setEntityToEdit(phone);  
            })  
            .navigate();  
}  
  
@Subscribe("phonesDataGrid.edit")  
public void onPhonesDataGridEdit(final ActionPerformedEvent event) {  
    viewNavigators.detailView(phonesDataGrid)  
            .withViewClass(PhoneDetailView.class)  
            .withRouteParameters(RouteParameters.empty())  
            .withAfterNavigationHandler(afterViewNavigationEvent ->  
                    afterViewNavigationEvent.getView()  
                            .setEntityToEdit(Objects.requireNonNull(phonesDataGrid.getSingleSelectedItem())))  
            .navigate();  
}

As you can see, the action handler methods set the empty route parameters and pass the entity in AfterViewNavigationEvent handler.

Opening detail view in dialog window should work as before without changes in list view actions.

Both for the navigation and dialog mode, call setReloadEdited(false) in the detail view:

public PhoneDetailView() {  
    setReloadEdited(false);  
}

Setting non-new state for loaded DTO entities

To make sure the framework correctly distinguish new instances from already existing in a storage, call EntityStates.setNew(entity, false) for entities loaded from external storage.

Assigning ID to new DTO entities

If the new entity ID is assigned by the storage, set the ID to the original instance too to let the framework match the saved instance with the original one. For example:

public Task saveTask(Task task) {
    String url = task.getId() != null ? TASKS_BASE_URL + "/"  + task.getId() : TASKS_BASE_URL;
    ResponseEntity<Task> response = restTemplate.postForEntity(url, task, Task.class);
    Task savedTask = Objects.requireNonNull(response.getBody());
    if (task.getId() == null) {
        task.setId(savedTask.getId());
    }
    entityStates.setNew(savedTask, false);
    return savedTask;
}

@knstvk knstvk reopened this Feb 5, 2024
@knstvk
Copy link
Contributor Author

knstvk commented Feb 5, 2024

QA: test UI of add-ons using DTOs:

  • security roles and policies
  • JMX Console
  • Reports
  • BPM
  • Quartz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

3 participants