LiftView and SiteMap

63 views
Skip to first unread message

lester

unread,
Nov 7, 2010, 3:49:45 PM11/7/10
to Lift
I have a question related to URL rewriting and LiftView. As far as I
understand, all URLs must be specified in SiteMap (otherwise, these
URLs wouldn't be available to access). But what if some part of URL is
dynamic?

For example, I would like to visit Contact's profile page via URL
like: http://some_host/some_app/contacts/contact_name, where
'contact_name' is a dynamic part of URL. I have a LiftView (named
'ContactView' with 'nickName' string mapped to function) to get user's
details from database by nickname. So I added following PF to
LiftRules to implement URL rewriting:

LiftRules.statelessRewrite.append(NamedPF("ContactNameRewrite"){
case RewriteRequest(ParsePath(List("contacts", contactName), _,
_, _), _, _) => {
RewriteResponse(ParsePath(List("ContactView", "nickName"), "",
false, true), Map("contactName" -> contactName))
}
})

But how should this dynamic URL be added to SiteMap?

David Pollak

unread,
Nov 7, 2010, 7:10:00 PM11/7/10
to lif...@googlegroups.com
You don't have to mess with explicit rewriting.  Just do:

Menu.param("View Contact", "View Contact", Contact.find _, contact => contact.firstName.is) / "contact"

The Contact.find is a function that takes a String and returns a Box[T] where T is the type of the thing that the menu refers to.  The second function takes a T and returns a String.

For more information, see http://www.assembla.com/wiki/show/liftweb/SiteMap

Also, what does this have to do with LiftView?


--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.




--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Blog: http://goodstuff.im
Surf the harmonics

lester

unread,
Nov 8, 2010, 9:01:42 PM11/8/10
to Lift
David, thank you for reply!

I didn't know about Menu.param def. It have been introduced since Lift
2.1, right?

Actually, I've already had snippet to bind contact to template. It
works when user access corresponding action via link on UI. But this
time I tried to implement case when user enters URL with contact's
name directly to the browser (some kind of user-friendly URL). This
snippet uses RequestVar to store and process existing Contact (like in
JPA Example from Lift's source code repository). So I was trying to
parse URl, extract contact name from it, find corresponding Contact in
database, then construct RequestVar(contact) and finally redirect to
existing snippet.

You are right - it was mad desicion to place part of that stuff to
LiftView. I've just tried to test how views work.

On 8 ноя, 03:10, David Pollak <feeder.of.the.be...@gmail.com> wrote:
> You don't have to mess with explicit rewriting.  Just do:
>
> Menu.param("View Contact", "View Contact", Contact.find _, contact =>
> contact.firstName.is) / "contact"
>
> The Contact.find is a function that takes a String and returns a Box[T]
> where T is the type of the thing that the menu refers to.  The second
> function takes a T and returns a String.
>
> For more information, seehttp://www.assembla.com/wiki/show/liftweb/SiteMap
>
> Also, what does this have to do with LiftView?
>
>
>
>
>
>
>
>
>
> On Sun, Nov 7, 2010 at 12:49 PM, lester <lester.m...@gmail.com> wrote:
> > I have a question related to URL rewriting  and LiftView. As far as I
> > understand, all URLs must be specified in SiteMap (otherwise, these
> > URLs wouldn't be available to access). But what if some part of URL is
> > dynamic?
>
> > For example, I would like to visit Contact's profile page via URL
> > like:http://some_host/some_app/contacts/contact_name, where
> > 'contact_name' is a dynamic part of URL. I have a LiftView (named
> > 'ContactView' with 'nickName' string mapped to function) to get user's
> > details from database by nickname. So I added following PF to
> > LiftRules to implement URL rewriting:
>
> > LiftRules.statelessRewrite.append(NamedPF("ContactNameRewrite"){
> >      case RewriteRequest(ParsePath(List("contacts", contactName), _,
> > _, _), _, _) => {
> >        RewriteResponse(ParsePath(List("ContactView", "nickName"), "",
> > false, true), Map("contactName" -> contactName))
> >      }
> >    })
>
> > But how should this dynamic URL be added to SiteMap?
>
> > --
> > You received this message because you are subscribed to the Google Groups
> > "Lift" group.
> > To post to this group, send email to lif...@googlegroups.com.
> > To unsubscribe from this group, send email to
> > liftweb+u...@googlegroups.com<liftweb%2Bunsu...@googlegroups.com >
> > .
> > For more options, visit this group at
> >http://groups.google.com/group/liftweb?hl=en.
>
> --
> Lift, the simply functional web frameworkhttp://liftweb.net
> Beginning Scalahttp://www.apress.com/book/view/1430219890

David Pollak

unread,
Nov 9, 2010, 9:08:39 AM11/9/10
to lif...@googlegroups.com
On Mon, Nov 8, 2010 at 6:01 PM, lester <leste...@gmail.com> wrote:
David, thank you for reply!

I didn't know about Menu.param def. It have been introduced since Lift
2.1, right?

Actually, I've already had snippet to bind contact to template. It
works when user access corresponding action via link on UI. But this
time I tried to implement case when user enters URL with contact's
name directly to the browser (some kind of user-friendly URL). This
snippet uses RequestVar to store and process existing Contact (like in
JPA Example from Lift's source code repository). So I was trying to
parse URl, extract contact name from it, find corresponding Contact in
database, then construct RequestVar(contact) and finally redirect to
existing snippet.

You don't need to do this with RequestVars.  With the code I showed you, the current Loc is typed as Contact (in your case) the the current instance of the Contact is associated with the Loc (Loc.currentValue).

Or if you want to do it by hand, see http://scala-tools.org/mvnsites/liftweb-1.0/lift-examples-parent/lift-example/scaladocs/net/liftweb/example/lib/WikiStuff$object.html

 
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.




--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890

lester

unread,
Nov 9, 2010, 9:29:46 PM11/9/10
to Lift
Thanks for explanation and link. I do understand that now there is an
easier way. Do you know whether any article or source code with more
detailed example of using Menu.param exist (it seems this function
haven't been described yet on Lift Wiki page dedicated to SiteMap)?
It's purpose, parameters and result are clear for me with your
comments and scaladoc, but if possible, I would like to see some
examples.

On 9 ноя, 17:08, David Pollak <feeder.of.the.be...@gmail.com> wrote:
> On Mon, Nov 8, 2010 at 6:01 PM, lester <lester.m...@gmail.com> wrote:
> > David, thank you for reply!
>
> > I didn't know about Menu.param def. It have been introduced since Lift
> > 2.1, right?
>
> > Actually, I've already had snippet to bind contact to template. It
> > works when user access corresponding action via link on UI. But this
> > time I tried to implement case when user enters URL with contact's
> > name directly to the browser (some kind of user-friendly URL). This
> > snippet uses RequestVar to store and process existing Contact (like in
> > JPA Example from Lift's source code repository). So I was trying to
> > parse URl, extract contact name from it, find corresponding Contact in
> > database, then construct RequestVar(contact) and finally redirect to
> > existing snippet.
>
> You don't need to do this with RequestVars.  With the code I showed you, the
> current Loc is typed as Contact (in your case) the the current instance of
> the Contact is associated with the Loc (Loc.currentValue).
>
> Or if you want to do it by hand, seehttp://scala-tools.org/mvnsites/liftweb-1.0/lift-examples-parent/lift...
> > <liftweb%2Bunsu...@googlegroups.com<liftweb%252Bunsubscribe@googlegroup s.com>>

lester

unread,
Nov 11, 2010, 7:09:49 AM11/11/10
to Lift
So, now there are two possibilities to create a parametrized Loc:

1. "Old" way - to create custom Loc, extending net.liftweb.sitemap.Loc
class and overriding rewrite method

There is and example of this implementation:
http://scala-tools.org/mvnsites/liftweb-1.0/lift-examples-parent/lift-example/scaladocs/net/liftweb/example/lib/WikiStuff.scala.html#Some(52)

2. "New", more simple approah - to use net.liftweb.sitemap.Menu.param
method

I have a question, related to second approach: while it clear, that I
will have got persisted entity from DB by "dynamic" part of URL
(Contact by name in my case), how this object can be passed to
snippet, responsible for binding to concrete template and generating
response?

On 9 ноя, 17:08, David Pollak <feeder.of.the.be...@gmail.com> wrote:
> On Mon, Nov 8, 2010 at 6:01 PM, lester <lester.m...@gmail.com> wrote:
> > David, thank you for reply!
>
> > I didn't know about Menu.param def. It have been introduced since Lift
> > 2.1, right?
>
> > Actually, I've already had snippet to bind contact to template. It
> > works when user access corresponding action via link on UI. But this
> > time I tried to implement case when user enters URL with contact's
> > name directly to the browser (some kind of user-friendly URL). This
> > snippet uses RequestVar to store and process existing Contact (like in
> > JPA Example from Lift's source code repository). So I was trying to
> > parse URl, extract contact name from it, find corresponding Contact in
> > database, then construct RequestVar(contact) and finally redirect to
> > existing snippet.
>
> You don't need to do this with RequestVars.  With the code I showed you, the
> current Loc is typed as Contact (in your case) the the current instance of
> the Contact is associated with the Loc (Loc.currentValue).
>
> Or if you want to do it by hand, seehttp://scala-tools.org/mvnsites/liftweb-1.0/lift-examples-parent/lift...
> > <liftweb%2Bunsu...@googlegroups.com<liftweb%252Bunsubscribe@googlegroup s.com>>

Jeppe Nejsum Madsen

unread,
Nov 11, 2010, 8:07:00 AM11/11/10
to lif...@googlegroups.com
On Thu, Nov 11, 2010 at 1:09 PM, lester <leste...@gmail.com> wrote:
> So, now there are two possibilities to create a parametrized Loc:
>
> 1. "Old" way - to create custom Loc, extending net.liftweb.sitemap.Loc
> class and overriding rewrite method
>
> There is and example of this implementation:
> http://scala-tools.org/mvnsites/liftweb-1.0/lift-examples-parent/lift-example/scaladocs/net/liftweb/example/lib/WikiStuff.scala.html#Some(52)
>
> 2. "New", more simple approah - to use net.liftweb.sitemap.Menu.param
> method
>
> I have a question, related to second approach: while it clear, that I
> will have got persisted entity from DB by "dynamic" part of URL
> (Contact by name in my case), how this object can be passed to
> snippet, responsible for binding to concrete template and generating
> response?

Look at Loc.currentValue. It will contain the Box[Entity]

Or you can add a Loc.ValueTemplate parameter to your Loc...

/Jeppe

lester

unread,
Nov 11, 2010, 8:21:27 AM11/11/10
to Lift
Thank you, Jeppe!

Yes, It's clear now. I should have been more attentive - David had
already written about this method on Loc in this thread.

On 11 ноя, 16:07, Jeppe Nejsum Madsen <je...@ingolfs.dk> wrote:
> On Thu, Nov 11, 2010 at 1:09 PM, lester <lester.m...@gmail.com> wrote:
> > So, now there are two possibilities to create a parametrized Loc:
>
> > 1. "Old" way - to create custom Loc, extending net.liftweb.sitemap.Loc
> > class and overriding rewrite method
>
> > There is and example of this implementation:
> >http://scala-tools.org/mvnsites/liftweb-1.0/lift-examples-parent/lift...)

lester

unread,
Nov 15, 2010, 10:15:23 AM11/15/10
to Lift
Well, one more question :s)

At the moment user is able to reach contact details from list of
contact UI screen, where corresponding contact is retreived from db by
primary key and then placed into RequestVar. But as pointed above, I
also would like for user to get contact details directly via url.
Following your advices, I've added loc generated by Menu.param to
SiteMap in Boot.scala:

Boot.scala
---------------------------------------------------------------------------------------------------------------------------------------------------
val contactAddMenuItem
= Menu(Loc("addContact", "contact" :: "add" :: Nil, "Add
Contact"))
val contactViewByNickNameMenuItem
= Menu.param[Contact]("viewContact",
"View Contact",
(nickName : String) => {
logger.debug("Nickname from URL:
%s".format(nickName))
Model
.createNamedQuery[Contact]
("findContactByNickName", "nickName" -> nickName )
.findOne
},
contact => contact.nickName
) / "contact"
val contactMenu
= Menu(Loc("listContacts", "contact" :: "list" :: Nil, "List
Contacts"), contactAddMenuItem, contactViewByNickNameMenuItem)
val menus = homeMenu :: contactMenu :: Nil
LiftRules.setSiteMap(SiteMap(menus : _*))


I'd like to use the same snippet to process contact for both cases: 1)
via id from app; 2) directly via url by nickname. So I added helper
function to Contact.scala which checks whether contact is presented in
Loc.currentValue. If it is not presented, contact is extracted from
RequestVar and code below works as before (I have been using JPA as a
persistence engine, but I'm planning to migrate to Lift's Mapper):

Contact.scala
------------------------------------------------------------------------------------------------------------------------------------------------------
object contactVar extends RequestVar[Contact](new Contact())

def contact = {
val locBoxed = SiteMap.findLoc("viewContact")
(locBoxed : @unchecked) match {
case Full(loc) => {
val contactBoxed = loc.currentValue
(contactBoxed : @unchecked) match {
case Full(c : Contact) => c
case Empty => contactVar.is
}
}
}
}

def add(xhtml : NodeSeq) : NodeSeq = {
def doAdd() = {
if(contact.nickName.length == 0) {
logger.error(() => "Invalid lastName")
S.error("emptyNickName", "The contact's nick name cannot be
blank")
} else {
try {
logger.debug("addContact() - Conact id: " +
contact.contactId)
Model.mergeAndFlush[Contact](contact)
redirectTo("list.html")
} catch {
case ee : EntityExistsException => error("That contact
already exists")
case pe : PersistenceException => error("Error adding
contact"); logger.error(() => "Contact add failed", pe)
}
}
}

val currentId = contact.contactId

bind("contact", xhtml,
"id" -> SHtml.hidden(() => contact.contactId = currentId),
"nickName" -> SHtml.text(contact.nickName, contact.nickName
= _),
"firstName" -> SHtml.text(contact.firstName,
contact.firstName = _),
"lastName" -> SHtml.text(contact.lastName, contact.lastName
= _),
"email" -> SHtml.text(contact.email, contact.email = _),
"birth" -> SHtml.text(dateFormatter.format(contact.birth),
setBirth(_, contact)) % ("id"->"birth"),
"submit" -> SHtml.submit("Save", doAdd)
)
}

Here is the template to display contact details:

/contact/add.html
---------------------------------------------------------------------------------------------------------------------------------------------------
<lift:surround with="default" at="content">
<table>
<lift:ContactOperations.add form="POST">
<contact:id/>
<tr><td>Nick name:</td><td><contact:nickName/></td></tr>
<tr><td>First name</td><td><contact:firstName/></td></tr>
<tr><td>Last name:</td><td><contact:lastName/></td></tr>
<tr><td>Email: </td><td><contact:email/></td></tr>
<tr><td>Birth date:</td><td><contact:birth/></td></tr>
<tr><td colspan="2"><contact:submit/></td></tr>
<script type="text/javascript">
Date.format = 'dd.mm.yyyy';
jQuery(function () {
jQuery('#birth').datePicker({startDate:'00010101'});
})
</script>
</lift:ContactOperations.add>
</table>
</lift:surround>

When contact is accessed from app, details page is successfully
displayed. But when I try to access it via URL, following error
message is returned: The Requested URL '/app/contact/nickname' was not
found on this server. Log shows that corresponding contact was
retreived from database by nickname, but Contact.scala snippet hasn't
been reached.

How do you think, what the reason of this error is?

Thank you.
Reply all
Reply to author
Forward
0 new messages