Primefaces dataTable issues - datatable

What is the method to refresh data on subsequent pages - second page, third page, etc - of a Primefaces dataTable using the LazyDataModel method?
Also, if I select an item in a dataTable to view its detail on another page, then came back to the dataTable using either the browser's Back button or implement JavaScript's history.back() method, it seems that the dataTable always reset its position to the first page instead of going back to the page the user was on. How can I force the dataTable to stay on the last viewed page?
My codes for lazy loading are:
private final class LazyLoader extends LazyDataModel<BookModel>
{
private static final long serialVersionUID = 1L;
public LazyLoader(String sort, String category, String operator, String input) {
setListing(getBookService().getListing(sort, category, operator, input));
}
#Override
public List<BookModel> load(int first, int pageSize, String sortField, boolean sortOrder, Map<String, String> filters) {
return getListing();
}
}
And for the Submit method is:
public String Submit()
{
sort = sortBean.getSort();
category = categoryBean.getCategory();
operator = operatorBean.getOperator();
input = searchBean.getInput();
lazyModel = new LazyLoader(sort, category, operator, input);
lazyModel.setRowCount(listing.size());
return null;
}
I'm using #ViewScoped for listing the book records as well as showing detail of a book record.
Does anyone has similar issues with Primefaces dataTable?

Keep using #ViewScoped. You should not use #SessionScoped unless you have real needs for it.
To remember the last page, you have to set the first attribute of the load method. You can do that with request parameters. Something like: yourview.xhtml?f=3 .
About the refreshing, the thing is that you are using a lazy loader but you're loading everything at once... Your load method is the one that should do the query on demand, that is, page by page.

Does pagination work for you without lazy loading? I would verify that works as expected before you jump into the hardest case.
If you want your dataTable to remember the last pagination after you navigate away from the JSF page then you need to make your managed bean SessionScoped. The lifecycle of the ViewScoped managed bean ends after navigation leaves the view.

In order to keep the selected page you have to do 2 things.
First make the managedBean session scoped.
Second set a binding between the datatable and a UIData object. In your backend bean for example put
private UIData filasUIData = null;
public UIData getFilasUIData() {
return filasUIData;
}
public void setFilasUIData(UIData filasUIData) {
this.filasUIData = filasUIData;
}
Now in your data table
<ice:dataTable
binding="#{yourBean.filasUIData}"
that´s all.

Related

Spring - how do I get the page to refresh after a form is submitted?

Using spring boot, I have a timetable page.
You can click a square, which opens up a small form with which you can add or remove the class objects from the timetable (which are saved in a database).
My issue is, when you click 'add' (or remove), while it successfully adds/removes that object from the timetable, you have to refresh the page in order to see the class be added/removed on the timetable. It appears like nothing happens when add is clicked from the user's perspective (until they manually refresh the page).
The post method redirects back to the timetable page get. I tried having it redirect to another mini get method, which then redirected back to the original timetable page; but from the browser side it still didn't look like anything was happening - just remaining on the same page with the original problem. Would love to hear a potential solution, thanks!
Edit: Here's an example of my get and post methods:
#GetMapping("/timetable/{id}/{semId}")//This method displays timetable.
public String timetable(#PathVariable(value = "id") String id, Model model,
#PathVariable(value = "semId") String semId) {
//code..
model.addAttribute("x", x);
return "timetable";
}
#PostMapping("/timetable/{id}/{semId}")
public String timetablePost(#ModelAttribute TimetableClassDto dto, #PathVariable(value = "id") String id,
Model model, #PathVariable(value = "semId") String semId) {
//code..
return "redirect://timetable/00/" + semId;
}
Are you supposed to have two // in your redirect? I have something similar in my code and it works fine. However, I create a url first then return that. Also, make sure your get mapping is properly filling out the object based on the new parameters gotten from the redirect.
Use the following:
??
String url = "redirect://timetable/00/" + semId;
return url;

Using session objects from parent class in component

During my battle with tapestry 5 I created some setup which does not work, and I don't know why. I found few work-arounds, but still I would like to know why initial idea failed.
I have parent abstract page class with application-wide auth procedure:
public abstract class AuthPage {
#SessionState
protected UserAuth user;
public Object onActivate(){
if(user==null)
return LoginForm.class;
else if(user.getLoggedIn().equals(Boolean.FALSE))
return LoginForm.class;
else
return null;
}
}
Then I have index page class, using auth class as aprent:
public class Index extends AuthPage
{
}
This part works smoohtly - when user SSO is initialized then I got Index content, otherwise it goes to LoginForm. Now the problematic part - Index uses a layout component which takes care of showing personalized header and menu. Its logic looks like that:
public class Layout extends AuthPage
{
#Property
private Boolean loggedIn;
#Property
private String userName;
#SetupRender
public boolean checkNames(){
if(user==null){
loggedIn = false;
userName = "unlogged";
}
else if(user.getLoggedIn().equals(Boolean.FALSE)){
loggedIn=false;
userName = "unlogged";
}
else{
loggedIn = true;
userName = this.user.getUsername();
}
return true;
}
}
The idea was simple - session object user from AuthPage should be available in Layout, and used on setup-render stage to get user name and rising the flag for rendering menu etc. From my point of view everything should work, but in practice Layout class didn't get user object from session (despite that it was initialized for sure, because Index renders its content).
So my question is - why Layout class don't see UserAuth object stored in session, but gets it as null instead?
************ little update:
I've refactore layout to that shape:
public class Layout
{
#SessionState
protected UserAuth user;
#Property
private Boolean loggedIn;
#Property
private String userName;
#SetupRender
public boolean checkNames(){
if(user==null){
loggedIn = false;
userName = "unlogged";
}
else if(user.getLoggedIn().equals(Boolean.FALSE)){
loggedIn=false;
userName = "unlogged";
}
else{
loggedIn = true;
userName = this.user.getUsername();
}
return true;
}
}
and it works as I want - Layout (executet from Index page as component) takes user attribute from session, performs checkNames and sets up all properties properly. For me there is no technical difference between initial and second implementation, but somehow when user is defined in parent class is always set to null (no matter what is stored in session). The question is - why it works that way?
Layout is a component, not a page, and onActivate() is page event which won't be fired when the Layout component renders. For a component (Layout) to extend a page (AuthPage) does not make sense. I'm surprised that Tapestry allows it to tell the truth.
On another note, Tapestry has so many features (including filters, class transformations and mixins) that inheritance is almost always not required. Although quite complex, you might find the diagram at the bottom of this page useful. In particular you may be interested in the ComponentRequestFilters and PageRenderRequestFilters.
Here's a couple of options for securing pages:
Howard Lewis Ship's blog - Howard is the creator of Tapestry
Tynamo tapestry security
What version of tapestry are you using? In tapestry 5.3.1 and earlier, instance variables must be private. Only version 5.3.2+ supports protected and package private.
http://tapestry.apache.org/page-and-component-classes-faq.html#PageAndComponentClassesFAQ-Whydomyinstancevariableshavetobeprivate%3F
Ok, I solved the problem! Answer is a little bit unexpected for me, but well - everything seems to work fine now. The trick is the way I was refering to the user attribue ot AuthPage class. I used
this.user
to refere to it from child classes. It turned out that I've got it as null everywhere, even in pages (I had an impression that it worked correctly in Index page, but in fact it was just used in AuthPage class). The solution was to refere by:
super.user
When I switched to such referencing style suddenly every page and component started to work properly and got correct values from session. I'll just take it as it is, but if someone knows why it works that way and no other - I'll appreciate sharing this knowledge with me.

Spring MVC SessionAttributes with ModelAttribute usage

I am trying to learn Spring MVC recently. It seems that i did not understand well the functionality of #SessionAttributes and #ModelAttribute annotations.
This is a part of my controller:
#SessionAttributes({"shoppingCart", "count"})
public class ItemController {
#ModelAttribute("shoppingCart")
public List<Item> createShoppingCart() {
return new ArrayList<Item>();
}
#ModelAttribute("count")
public Integer createCount() {
return 0;
}
#RequestMapping(value="/addToCart/{itemId}", method=RequestMethod.GET)
public ModelAndView addToCart(#PathVariable("itemId") Item item,
#ModelAttribute("shoppingCart") List<Item> shoppingCart, #ModelAttribute("count") Integer count) {
if(item != null) {
shoppingCart.add(item);
count = count + 2;
}
return new ModelAndView(new RedirectView("showAllItems"));
}
Basically there is a jsp listing all the items. Wenn user click "addToCart" for a specific item, this item will be added to the shoppingCart list. I better explain my understanding of this controller first and you can tell me what i do not get.
First time when the ItemController is called, the createShoppingCart and createCount methods will be executed and the return parameters will be saved in session under names "shoppingCart" and "count". When the user calls the url ".../addToCart/1", addToCart method will be called. Since i need there in the method signature 2 values from session, the controller will look in the session whether the values are already there. Yes they are.. At this time shoppingCart is an empty list, and count is 0. In the method body, the selected item will be added to list, count will be 2. The jsp will be displayed again.
The problem is, jsp can see that the list shoppingCart is now not empty. but the count is still 0. When i add Items to basket, I can see on jsp that the shoppingCart is filled with items, but the value of count is always 0.
Actually there is no any difference between shoppingCart and count objects.. i dont get it why it behaves like this. I first doubted that the count type was primitive int, then i changed it to Integer typ, still the problem is not solved.
You don't change count (You can't in fact), you assign to it. So the model still points to the old value. You would have to add the new value manually.
myModelAndView.add("count", count);
But why bothering with count if you can use warenkorb.size anyway?

MVC - Dealing with the model and events on the page

I have what is probably a basic question regarding how to structure an MVC page.
Assume this is my model:
public class MyModel
{
int ProductId
List<ParameterTable> ParameterTables
...
[other properties]
...
}
ProductId initially won't have a value, but when its value is selected from a DropDownList it will trigger an event that retrieves the List items associated with that product.
My problem is when I do this AJAX call to get the parameter tables I'm not sure how to handle the response. I've only seen examples where people then manually inserted this data into the page via the jquery. This would mean handling displaying the data in your view (for the first time loading the page) and in the jquery (whenever it changes).
I am wondering if there's a way to somehow pass back a model of sorts that binds my return value of List into my page without needing to specify what to do with each value.
Would I have to have the changing of the ProductId DropDownList trigger an ActionResult that would reload the whole page to do this instead of a JsonResult?
You could return a partial view with your ajax call.
Controller action:
public ActionResult Filter(int productId) {
var product = _repository.Find(productId);
if (Request.IsAjaxRequest())
{
return PartialView("_Product", product);
}
else
{
return View(product);
}
}

MVC3 Dropdown repopulates fine even on error postback, but suggestion on how to make it efficient - without introducing error

I can't post my code at the moment as I am on the road and out of office. Here is my code greatly simplified. I don't think I have a syntax issue as such - I think I need a clarification or, a pointer.
Model:
[Validaton attribute]
class Data
{
[Required]
int xx
..
}
ViewModel:
class VM
{
SelectItem Items;
int ItemID;
Data dt = new Data();
public VM
{
Items =... Load from db...
}
}
View
#Textbox xx (etc)
#DropDownFor(VM.ItemID, VM.Items
Controller
void Index
{
VM vm = new VM();
//I also default some items here works well - I get a nice form all loaded.
return View(vm);
}
[POST]
void Index(VM stuffPosted)
{
If ModelState.OK)(
{
All good. We direct to another page with Redirect To Action
}
else
{
return View(stuffPosted)
//This is the problem case where I need an efficiency
}
PROBLEM
The form loads fine with the Index method. On postback submit, the ID comes back correctly in ItemID. If there is an error - the ModelState sends back the view...and all the selection list(s) are in place, with selections. Oh joy! BUT, in the Postback method, the VM stuffposted instance class invokes the constructor and there is another (expensive) db call....to reload the collections..then the 'ViewState' seems to load the selected IDs over the top of it...
If I refactor the code so as there is no expensive reload of the collections...then I get the classic error along the lines of
int32 but expecting IEnumerable and my original form does not show ...
I am missing something, clearly..how to reload the collection from 'ViewState' (page) for reuse in an error situation, and not need a db call.
You could use SessionState to avoid the database trip to repopulate the dropdown. Populate the SessionState on the first GET request, and then load from it when you need to repopulate the dropdown (on error) on POST. However be wary of the size of the collection you are storing in SessionState - you may be better going to the database after all to re-load the data.

Resources