[Lift] Re: Advice for maintaining application state
because I haven't yet encountered a use case where they correspond to expected behaviour if the user happens to know how to work with a tabbed browser. -- You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to lift...@googlegroups.com. To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.
[Lift] Re: Advice for maintaining application state
Hi, I had a similar discussion on this list a while ago. http://groups.google.com/group/liftweb/browse_thread/thread/69898fb5191a074d I haven't found THE idiomatic answer in Lift. For now I'm using StatefulSnippets for the more complex cases; they work quite well. SessionVars are almost always a bad idea IMHO. For the simple cases I pass URL parameters around. The thing to remember is that the StatefulSnippet lifecycle is held together only by hidden fields whose value is posted back (as explained in the Lift book). So the user can easily work with different state sets (StatefulSnippets) in parallel on different tabs. Best wishes, Tim -- You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to lift...@googlegroups.com. To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.
[Lift] Re: Advice for maintaining application state
not sure if I posted the link: http://groups.google.com/group/liftweb/browse_thread/thread/69898fb5191a074d -- You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to lift...@googlegroups.com. To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.
[Lift] Mapper binding Deluxe
Hello fellow Heavylifters, the following small utility classes could be useful for those who develop somewhat classic data-oriented applications in Lift and with Mapper: http://wiki.github.com/tromberg/Winglet/ In short: * Do more with less template and snippet code * Only execute the parts of your snippet needed by the current template * Deal flexibly with empty fields * Display field errors on input forms by default * Display result lists in a table Feedback welcome (work in progress), Tim -- You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to lift...@googlegroups.com. To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.
[Lift] Re: csv request answer
I've been happy with the CSV support provided by h2database because that was already included in the PocketChange example http://www.h2database.com/html/tutorial.html#csv But there are over a dozen alternatives I think. Google for java csv library It's a good idea IMHO to use a library for handling all the special cases, escaping etc., and let the user choose some parameters (e.g. some country versions MS Excel expect semicolons instead of commas) -- You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to lift...@googlegroups.com. To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.
[Lift] Signup Form: Some error messages are hidden by default
Just discovered this issue (1.1-m8): The sign-up form will not display field errors by default. The user therefore gets the impression that the application has simply stopped responding. The validation I checked (and this may be the only one) was the unique e-mail address validation. If the e-mail address has already been used before, it sends a FieldError to S.error. Displaying the message would require a specific call to the Msg-Snippet for that field afaik; but this call is not generated by the localForm method. For now I will explicitly call the snippet for the email field in the surrounding template, so that the message will be displayed above the whole form. -- You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to lift...@googlegroups.com. To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.
[Lift] Re: Catch file upload exceptions?
Well LiftRules.exceptionHandler was new to me so thanks. Great way to deal with the really unexpected (like OutOfMemory). Would do in the short run. However, the other Tim is correct in that I (and probably other people) really want to handle this like similarly to a validation error on any other field. LiftRules.exceptionHandler catches so far outside, as far as I understand, that the only way to get back into the workflow would be to kindly ask the user to press the Back button. -- You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to lift...@googlegroups.com. To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.
[Lift] Re: Catch file upload exceptions?
Hi Tim, thanks for raising this, I did wrap the whole thing like so: LiftRules.maxMimeSize = 6 * 1024 * 1024 LiftRules.maxMimeFileSize = 5 * 1024 * 1024 LiftRules.handleMimeFile = (fieldName, contentType, fileName, inputStream) = { try { OnDiskFileParamHolder(fieldName, contentType, fileName, inputStream) } catch { case e:Exception = S.error(Failure in upload (file too large?). Details: + e.toString) new InMemFileParamHolder(, , , null) case _ = new InMemFileParamHolder(, , , null) } } and now I remember that I did manage to catch an exception once when I set the maxMimeFileSize to very low. Then I set it back to where it was and tested with a 7MB file. I think I understand now: The above will catch based on maxMimeFileSize. It will not work when the file you upload also exceeds the maxMimeSize, which is when you get the stack trace at the beginning of my post. So I suppose the solution is to set the maxMimeSize to Infinity. Marius, thanks for the hint. Was just a quick idea how to keep face with users in such a case, since I'm running on a virtual server, but it's clear such conditions should really be avoided by proper configuration and load testing. Best wishes, Tim -- You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to lift...@googlegroups.com. To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.
[Lift] Beware of using a body tag in a surrounded template, especially with nested surrounds
Hi, I think it is documented somewhere in the Liftbook that one should not use body tags inside a surround tag, but Lift doesn't seem to complain. I thought I would document the phenomenon I had here since people usually look in the mailing list first. I had used two nested surround templates, where both templates used a body tag. (The inner template would start by surrounding itself with the outer template, then bind the working content itself inside the body tag). Worked fine in Lift 1.0, but in 1.1, the working content would appear twice in the generated HTML, once at the very beginning, and then in the correct place inside the two nested surround templates. Long-term, it would be great to get an error message in this case. Regards, Tim -- You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to lift...@googlegroups.com. To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.
[Lift] Re: A Critique On Lift
override def validations = validPriority _ :: super.validations funny, I had stumbled on exactly the same line of code when beginning. Took me more than a day to understand what's going on. Especially because when you copied code from the PDF version of the Liftbook/Lift getting started guide, it would mess up spaces, so I would keep loooking for a _:: operator. The Scala guys have really pushed it a bit hard on the use of the underscore. At least four different uses: - it for defining anonymous functions like above - default value - matching placeholder whose value is ignored - use for constructing setter method names boolean functions (empty_?) --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: Tabbed browsing and session state
Naftoli, thanks for providing these insights into the inner workings of Stateful Snippets. The mapSnippet solution sounds interesting. I knew that snippets don't live on when not needed, but assumed that IF one is alive in the current session, you would always get that one instance. Hence at most one instance would be alive per snippet class and per session. What you seem to indicate is that the lifecycle is held together by redirects and such (and therefore page ids) rather than two requests being in the same session. I will experiment how that works with tabs. I guess one has to still be very careful since the user may go from the view initiating a snippet and open several tabs from there, whereafter you could still get unwanted interference between the tabs if you don't pay attention. Marius, thanks for your points Wizard code exciting.. since the wizardiness probably won't jump into my eye, if you happen to stumble on it in the next few days, could you perhaps post a classname here? That data needs to be preserved into a RequestVar, That is precisely what I meant by passing state on from a response to the next request :-) RequestVars are benign. AJAX Tabs Had briefly thought of that. Makes good sense with some types of apps. In this particular app I've already made other compromises to preserve standard behavior and let the user bookmark the current URL etc. Actually things would already be much simpler if browsers allowed you to address tabs by Javascript (close current tab, jump to search results tab). One could then keep the results list open in a user- friendly way. But apparently browsers have been getting quite restrictive on that, for obvious reasons. --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Tabbed browsing and session state
Hi, has anyone investigated or built a way to support tabbed browsing when there is considerable view/workflow state? Or have I missed an elegant way for this to be done in Lift? If I am not mistaken, Lift currently supports 1. Sending continuations (things that will need to be done) from one Request to the next in the form of FuncHolders that are attached to an ID that is unique for a sent page. This works perfectly in a tabbed environment, and you can send state this way, but the state doesn't live on in the response sent by the snippet that has received it. 2. Session variables that are global to the session, as well as 3. Stateful snippets that are instantiated at most once in each session. When assuming that your user knows how to browse tabbed, I can currently not imagine what 2. and 3. could be practically useful for. Very often though, it seems, people use these mechanisms and then later realize the mess they are in. E.g. (quite funny) http://www.highdots.com/forums/javascript/browser-tab-269390.html More serious: http://www.codeodor.com/index.cfm/2007/7/19/Why-tabbed-browsing-makes-holding-form-state-with-sessions-obsolete/1470 A simple example from an application I'm developing, and I'll bet more than half of all Web apps, is this: I have one page where users can search for a bunch of documents in a database (search form and result list are on the same page - let's not make it too complicated). Clicking on a result will navigate to the detail view of a document. I am sure sooner or later the customer will want a link on the detail view to go back to the search he came from. If I put the last search parameters used into Session Vars, this will not work if the customer uses several such result lists in parallel (not unrealistic in this case). He will always end up going back to the last search used, not the one that was used to open the detail view. Very bad behavior for example when the user has edited an item, and it then vanishes from his view. What would work is: put the search parameters into hidden fields and/ or URL parameters of the detail view. But you break your fingers doing that and it gets really messy when the detail view can be opened from several different context, and the conversation that takes you back to the original context has several steps. There is also a limit to what you can put into a URL, I believe. You use a framework because it saves you these kinds of headaches. It seems to me, Lift already has 80% of what it takes for a great solution. It is the mechanism mentioned under 1. Any tab that is still open calls home regularly to say I'm still here, don't forget me. This is good because otherwise we would have to consider any page ever sent to the client to still be open and come back to haunt us, demanding us to behave as if nothing had happened since. The existing mechanism would only need to allow the state to be passed on, with a delta, to the next response-request episode. --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: Tabs + menu builder
Jeppe We've made primary/secondary navigation where primary navigation is ... I did something similar..found it difficult to work with the default snippets. But found it hard, when writing my own group snippet, to identify the current Loc within the LocGroups Locs. So Jeppe, would be very interested in your code for that snippet. Mine is below (only slightly modified from Menu.group). But it's not yet quite there. I also had a fiendish problem (lift 1.0.2 scala 2.7.5) when trying to use attributes li:class=x li_item:class=y on the tag. Isn't that a standard technique? Does it work for everyone else? For me, Scala chokes on that with a syntax error (further experiments suggest that it doesn't like two attributes with same name and different prefix, perhaps also because BOTH prefixes are from an unknown namespace). Of course it doesn't show you the exact error but faults with an IllegalArgumentException on scala.io.Source, but this has been discussed elsewhere. Anyway, that's why I have the new unprefixed attributes in the code below. def menuGroup(template: NodeSeq): NodeSeq = { val a_attrs = S.prefixedAttrsToMetaData(a) val li_attrs = S.attr(normalclass) match { case Full(c: String) = new UnprefixedAttribute(class, c, Null); case _ = Null } val li_sel_attrs = S.attr(selectedclass) match { case Full(c: String) = new UnprefixedAttribute(class, c, Null); case _ = Null } val currentLoc = LiftRules.siteMap.open_!.findLoc (S.request.open_!) openOr null for (group - S.attr(group).toList; siteMap - LiftRules.siteMap.toList; loc - siteMap.locForGroup(group); link - loc.createDefaultLink; linkText - loc.linkText) yield { val a = a href={link}{linkText}/a % a_attrs var li: Elem = li{a}/li if (loc == currentLoc) li % li_sel_attrs else li % li_attrs } } --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Mapper: Why is every SQL query sent twice? (second time with NULL parameters)
Dear all, on every findAll request, lift seems to send the query twice (tested 1.0 and 1.0.2, working from the PocketChangeApp example on h2database, and using DB.addLogFunc((query, time) = Log.info(query + : + time + ms))) for logging 1. e.g. right after login; this must be framework code (ProtoUser): INFO - prep91: SELECT users.firstname, users.lastname, users.email, users.locale, users.timezone, users.password_pw, use rs.password_slt, users.superuser, users.validated, users.uniqueid, users.id FROM users WHERE email = ? {1: 't...@rombe rg.be'};:14ms INFO - prep91: SELECT users.firstname, users.lastname, users.email, users.locale, users.timezone, users.password_pw, use rs.password_slt, users.superuser, users.validated, users.uniqueid, users.id FROM users WHERE email = ? {1: NULL};:65ms same on practically all other statements 2nd example: INFO - prep110: SELECT DISTINCT resourcestopics.resourcetype, resourcestopics.resourceid, resourcestopics.topicid FROM resourcestopics WHERE resourcetype = ? AND resourceid = ? {1: 2, 2: 3};:0ms INFO - prep110: SELECT DISTINCT resourcestopics.resourcetype, resourcestopics.resourceid, resourcestopics.topicid FROM resourcestopics WHERE resourcetype = ? AND resourceid = ? {1: NULL, 2: NULL};:27ms INFO - Service request (POST) /editProject.html took 748 Milliseconds my code: findAll(By(ResourcesTopics.resourceType, resourceType), By (ResourcesTopics.resourceId, resourceId)) Is this desired behavior? Am only I seeing this? It also seems that the useless query takes much more time than the real query.. Tried to compare with one of the most recent milestone (1.1-M5) but this one only throws exceptions on me, starting from boot, so I suppose there have been too many changes I am not aware of. Thanks, Tim --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---