DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

The Latest JavaScript Topics

article thumbnail
jQuery, Each() and Async Gets
One of the things to keep in mind when using jQuery is that nothing is a blocking call. Sure, there is a certain sequence to when things operate. But, to be safe, you should always assume that step two will happen during step one. No where is this more evident than when retrieving content from a URL and inserting that content in your page. The temptation is to write code that looks something like this $.each(json, function(index, entry) { jQuery.get(entry['url'], function(html) { // insert the HTML here. } } The problem with this is that jQuery.get is an asynchronous call. This means that once the get has fired, the each loop will continue. This can cause all kinds of trouble for you, including having a complete iteration skipped, or if you are doing some kind of concatenation prior to inserting the HTML, having HTML for one iteration showing up in the middle of another. Not exactly what you had in mind, eh? But there is a fix. Use the ajax call instead and specify async:false to force the call to complete before allowing another call. $.each(json, function(index, entry) { jQuery.ajax({ url: directory + '/' + entry['url'] , success: function(html) { // insert the HTML here. } }, async: false }); Note too that using ajax without the async: false is the same as just using get.
December 3, 2009
by Dave Bush
· 16,226 Views
article thumbnail
Fluent Navigation in JSF 2
In this article, the third in a series covering JavaServer Faces (JSF) 2.0 features contributed by Red Hat, or which Red Hat participated in extensively, you'll discover that getting around in a JSF 2 application is much simpler and requires less typing. With improved support for GET requests and bookmarkability, which the previous article covered, JSF 2 is decidely more nimble. But not at the cost of good design. JSF no longer has to encroach on your business objects by requiring action methods to return navigation outcomes, but can instead reflect on the state of the system when selecting a navigation case. This article should give you an appreciation for how intelligent the navigation system has become in JSF 2. Read the other parts in this article series: Part 1 - JSF 2: Seam's Other Avenue to Standardization Part 2 - JSF 2 GETs Bookmarkable URLs Part 3 - Part 4 - Part 5 - Three new navigation variants are going to be thrown at you in this article: implicit, conditional and preemptive. These new options are a sign that the JSF navigation system is becoming more adaptable to the real world. There's also a touch of developer convenience thrown in. Implicit navigation is particularly useful for developing application prototypes, where navigation rules just get in the way. This style of navigation interprets navigation outcomes as view IDs. As you move beyond prototyping, conditional navigation removes the coupling between the web and transactional tier because the navigation handler pulls information from your business components to select a navigation case. Preemptive navigation, which you were introduced to in the last article, can use either implicit navigation or declarative navigation rules to produce bookmarkable URLs at render time. Leveraging the navigation system to generate bookmarkable URLs allows JSF to add GET support while maintaining consistent, centralized navigation rules. Even with these new options, there's no telling what requirements your application might have for navigation. Thus, in JSF 2, you can finally query and modify the navigation cases; a new API has been introduced in JSF 2 that exposes the navigation rule set. Before we get into customizations, let's find out how these new variants make the navigation system more flexible and help prepare the user's next move. Hopefully you won't need those customizations after all. Flexible navigation choices The declarative navigation model in JSF was a move away from the explicit navigation "forward" selection by the action in Struts. Navigation transitions in JSF, which get matched based on current view ID, logical outcome and/or action expression signature, are described in the JSF descriptor (faces-config.xml) using XML-based rules. The matched transition indicates the next view to render and whether a client-side redirect should proceed rendering. Here's a typical example: /guess.xhtml #{numberGuessGame.guess} correct /gameover.xhtml While the JSF navigation model is clearer and arguably more flexible than in Struts, two fundamental problems remain. First, the action method is still required to return a navigation directive. The directive just happens to be a more "neutral" string outcome rather than an explicit type (i.e., ActionForward), but the coupling is just as tight and you loose type safety in the process, so is it really an improvement? The other issue is that you must define a navigation case to match that outcome, even in the simplest cases, which can be really tedious. So you can't make the argument that the navigation model is less obtrusive or more convenient. It's just stuck somewhere in between. To sum it up, the JSF navigation model is not flexible enough. It needs to accommodate different development styles better and it needs to be more self sufficient. On the one hand, your style or development phase may dictate waiving the declarative navigation rule abstraction. On the other hand, you may want to completely decouple your business objects from the navigation model, eradicating those arbitrary return value directives. JSF 2 gives you this broad range of options, and even let's you settle for a happy medium. The first option is provided by implicit navigation and the second conditional navigation. With implicit navigation, you can even use the current model without having to define the navigation rule right away. Let's unbox these two new alternatives, starting with implicit navigation. Implicit navigation JSF will post a form back to the current view (using the POST HTTP method) whenever the user performs an action, such a clicking a command button (hence the term "postback"). In the past, the only way to get JSF to advance to another view after the action is invoked (i.e., following the Invoke Application phase) was to define a navigation case in faces-config.xml. Navigation cases are matched based on the EL signature of the action method invoked and the method's return value converted to a string (the logical outcome). To cite an example, assume the user clicks on a button defined as follows: The preview() method on the bean named commandHandler returns a value to indicate the outcome of processing: public String preview() { // tidy, translate and/or validate comment return "success"; } These two criteria are joined in a navigation case that dictates which view is to be rendered next. /entry.xhtml #{commentHandler.preview} success /previewComment.xhtml If no navigation case can be matched, all JSF knows to do is render the current view again. So without a navigation case, there is no navigation. A quick shorthand, which is present in Seam, is to have the action method simply return the target view ID directly. In this case, you're effectively treating the logical outcome value as a view ID. This technique has been adopted in JSF 2 as implicit navigation. It's improved since Seam because you can choose to drop the view extension (e.g., .xhtml) and JSF will automatically add it back on for you when looking for a view ID. Therefore, it's no more invasive than the string outcome values you are currently returning. Implicit navigation comes into play when a navigation case cannot be matched using the existing mechanism. Here's how the logic outcome is processed in the implicit navigation case: Detect the presence of the ? character in the logical outcome If present, capture the query string parameters that follow it the ? character The special query string parameter faces-redirect=true indicates that this navigation should be issued using a client-side redirect If the logical outcome does not end with a file extension, append file extension of current view ID (e.g., .xhtml) If the logical outcome does not begin with a /, prepend the location of current view id (e.g., /, /admin/, etc.) Attempt to locate the template for the view ID If the template is found, create a virtual navigation case that targets the resolved view ID If the template is not found, skip implicit navigation Carry out the navigation case If the navigation case is not a redirect, build and render the target view in the same request If the navigation case is a redirect, build a redirect URL, appending the query string parameters captured earlier, then redirect to it Implicit navigation can be leveraged anywhere a logical outcome is interpreted. That includes: The return value of an action method The action attribute of a UICommand component (e.g., ) The outcome attribute of a UIOutcomeTarget (e.g., ) The handleNavigation() method of the NavigationHandler API Here's an example of the navigation to the preview comment view translated into implicit navigation. The return value is automatically decorated with a leading / and a trailing .xhtml. public String preview() { // tidy, translate and/or validate comment return "previewComment"; } The /previewComment.xhtml view will be rendered in the same request. If you want to redirect first, add the following flag in the query string of the return value: public String preview() { // tidy, translate and/or validate comment return "previewComment?faces-redirect=true"; } You can accomplish any navigation scenario using implicit navigation that you can today with a formal navigation case defined in faces-config.xml. Implicit navigation is designed as the fall-through case (after the explicit navigation rules are consulted). If it fails (i.e., the template cannot be located), and the JSF 2 ProjectStage is set to development, a FacesMessage is automatically generated to warn the developer of a possible programming error. Implicit navigation is great for prototyping and other rapid development scenarios. The major downside of implicit navigation is that you are further tying your business objects into the navigation model. Next we'll look conditional navigation, which provides an alternative that keeps your tiers loosely coupled. Conditional navigation Implicit navigation spotlights how invasive it is to put the onus on your business object to return a logic outcome just to make JSF navigation happy (and work). This coupling is especially problematic when you want to respond to user interface events using components in your business tier, a simplified architecture that is supported by both Seam and Java EE 6 to reduce the amount of glue code without increasing coupling. What would be more "logical" is to invert the control and have the navigation handler consult the state of the bean to determine which navigation case is appropriate. The navigation becomes contextual rather than static. That's what conditional navigation gives you. Conditional navigation introduces a condition as a new match criteria on the navigation case. It's defined in the element as a child of and expressed using an EL value expression. The value expression is evaluated each time the navigation case is considered. For any navigation case that matches, if a condition is defined, the condition must resolve to true for the navigation case to be considered a match. Here's an example of a conditional navigation case: #{registration.register} #{currentUser.registered} /account.xhtml As you can see, the condition doesn't necessarily have to reference a property on the bean that was invoked. It can be any state reachable by EL. Conditional navigation solves a secondary problem with the JSF navigation model, one of those little annoyances in JSF that was tedious to workaround. In JSF 1.2 and earlier, if your action method is a void method or returns a null value, interpreted in both cases as a null outcome, the navigation is skipped entirely. As a result, the current view is rendered again. The only workaround is to override the navigation handler implementation and change the behavior. That really throws a wrench in being able to cut the glue code between your UI and transactional tier. That changes with the introduction of conditional navigation. Since the condition provides either an alternative, or supplemental, match criteria to the logical outcome, navigation cases that have a condition are consulted even when the logical outcome is null or void. When the outcome is null, you can emulate switch statement to match a navigation case, switching on the condition criteria: #{identity.login} #{currentUser.admin} /admin/home.xhtml #{identity.login} #{currentUser.vendor} /vendor/home.xhtml #{identity.login} #{currentUser.client} /client/home.xhtml If you intend to simply match the null outcome in any case, you can use a condition that is verily true (which, admittedly, could be improved in JSF 2.1): #{identity.logout} #{true} /home.xhtml You can also use this fixed condition to provide a fall-through case. But wait, there's more! Having to itemize all the possible routes using individual navigation cases causes death by XML (a quite painful death). What if you wanted to delegate the decision to a navigation helper bean or involve a scripting language? There's good news. You can! The target view ID can be resolved from an EL value expression. Let's return to the login example and use a helper bean to route the user using one navigation case: #{identity.login} #{navigationHelper.userHomeViewId} Oh my goodness, how much nicer is that? The navigation helper can encapsulate the logic of inspecting the currentUser bean and determining the correct target view ID. In this section, we looked at two additional ways a navigation case is matched, increasing the overall flexibility of the navigation model. Implicit navigation maps logical outcomes directly to view IDs and conditional navigation reflects on contextual data to select a navigation case without imposing unnecessary coupling with the transactional tier. We're still looking at the same fundamental navigation model, though. In the next section, you'll see the navigation model used in a new role, and in a new place in the JSF life cycle, to generate bookmarkable links. Anticipating the user's next move Up to this point, the navigation handler only comes into play on a postback. Since user interface events trigger a "postback" to the current view, as mentioned earlier, the navigation handler kicks in after the Invoke Application phase to route the user to the next view. JSF 2 introduces a completely new use of the navigation handler by evaluating the navigation rules during the Render Response phase. This render-time evaluation is known as preemptive (or predetermined) navigation. Preemptive navigation The spec defines preemptive navigation as a mechanism for determining the target URL at Render Response, typically for a hyperlink component. The current view ID and specified outcome are used to determine the target view ID, which is then translated into a bookmarkable URL and used as the hyperlink's target. This process happens, of course, before the user has activated the component (i.e., click on the hyperlink). In fact, the user may never activate the component. The idea is to marry the declarative (or implicit) navigation model with the support for generating bookmarkable links. Based on what was just described, you should now understand why you declare the target view ID in an attribute named outcome on the new bookmarkable component tags (and why those components inherit from a component class named UIOutcomeTarget). You are not targeting a view ID directly, but rather a navigation outcome which may be interpreted as a view ID if the matching falls through to implicit navigation. Let's consider an example. Assume that you want to create a link to the home page of the application. You could define the link using one the new bookmarkable link component: This definition would match the following navigation case if it existed: * home /home.xhtml Of course, with implicit navigation available, this navigation case would be redundant. We could exclude it and the result would be the same. Home But if the target view ID depends on the context, such as the user's credentials, you might choose to reintroduce the navigation case to leverage conditional logic as we did earlier. In either case, the key is that the target view ID is not hard-coded in the template. As it turns out, you've already been using preemptive navigation when you explored bookmarkability in the last article. But there's a critical part of preemptive navigation that we haven't yet fully explored: the assembly of the query string. As it turns out, this topic also applies to redirect navigation rules. In a sense, preemptive navigation has the same semantics as redirect navigation rules because both produces URL that lead to a non-faces request. The only difference is that a bookmarkable URL is a deferred request, whereas a redirect happens immediately. In both cases, the payload in the query string is an essential part of the URLs identity. Building the query string As a result of the new GET support in JSF 2, there are now a plethora of ways to tack on values to the query string. Options can collide when heading into the navigation funnel. What comes out on the other side? There's a simple conflict resolution algorithm to find out. Each parameter source is given a precedence. When a conflict occurs, meaning two sources define the same parameter name, the parameter from the source with the highest precedence is used. The query string parameters are sourced using the following order of precedence, from highest to lowest: Implicit query string parameter (e.g., /blog.xhtml?id=3) View parameter (defined in the of the target view ID) Nested in UIOutcomeTarget (e.g., ) or UICommand component (e.g., ) Nested within the navigation case element in faces-config.xml Granted, this appears to be a lot of options. Don't worry, we'll walk you through the cases in which you would use each option in this article. We recommend you choose a single style of providing navigation parameters that best suits your architecture and keep the others in the back of your mind, so that when an edge case comes up, you can tap into their power. In the last article, you learned that you can use view parameters to let JSF manage the query string for you. Instead of using view parameters, you could just tack on the query string yourself when building a link to a blog entry. You could even abstract the parameter away from the view and define it in the navigation case instead, but it again it presents a challenge to tooling: permalink /entry.xhtml?id=#{blog.entryId} A nested would also work here, especially if you want to centralized your parameters. In terms of navigation, the most important point to emphasize here is that you can finally add query string parameters to a redirect URL in the navigation rules. This need likely appears in your existing applications. No longer do you have to resort to using the programmatic API to issue a redirect with a query string payload. Let's consider the case of posting a comment to an entry. This example demonstrates the case when you are submitting a form and want to redirect to a bookmarkable page which displays the result of submitting the form: #{commentHandler.post} /entry.xhtml id #{blog.entryId} Note: Don't confuse with a UIViewParameter. Think of it more as a redirect parameter (the tag should probably be called not , something to address in JSF 2.1). There are now plenty of options to pass the user along with the right information. But the spec can't cover everything. That's why you can now query the navigation rule base at runtime to do with it what you like. Peeking into the navigation cases You've now seen a number of ways in which the navigation cases themselves have become more dynamic. Regardless of how dynamic they are, the fact remains that once you ship the application off for deployment, the navigation cases that you defined in faces-config.xml are set in stone. That's no longer the case in JSF 2. A new navigation handler interface, named ConfigurableNavigationhandler, has been introduced that allows you to query and make live modifications to the registered NavigationCase objects. Not that you necessarily want to make changes in production. Having a configurable navigation rule set means that you can incorporate a custom configuration scheme such as a DSL or even a fluent, type-safe navigation model from which rules can be discovered at deployment time. In short, the navigation rule set is pluggable, and it's up to you what to plug into it. NavigationCase is the model that represents a navigation case in the JSF API. When JSF starts up, the navigation cases are read from the JSF descriptor, encapsulated into NavigationCase objects and registered with the ConfigurableNavigationHandler. You can retrieve one of the registered NavigationCase objects by the action expression signature and logical outcome under which it is registered. NavigationCase case = navigationHandler.getNavigationCase( facesContext, "#{commandBoard.post}", "success"); You can also access the complete navigation rule set as a Map>, where the keys are the values. Map> cases = navigationHandler.getNavigationCases(); You can use this map to register your own navigation cases dynamically. For example, a framework might read an alternative navigation descriptor (such as Seam's pages descriptor) and contribute additional navigation cases. With an individual NavigationCase object in hand, you can either read its properties or use it to create an action or redirect URL, perhaps to feed into your own navigation handler. There are a lot of possiblities here. The slightly awkward part is how you reference this new API (ConfigurableNavigationHandler). The default NavigationHandler implementation in a standard JSF implementation must implement this interface. But you still have to cast to it when you retrieve it from the Application object, as follows: ConfigurableNavigationHandler nh = (ConfigurableNavigationHandler) FacesContext.getCurrentInstance() .getApplication().getNavigationHandler(); Obviously, something to revisit in JSF 2.1. Once you get a handle on it, the navigation model is your oyster. You can define new ways to navigate or use it to generate bookmarkable URLs in your own style. Forging ahead The JSF navigation model had the right idea in spirit, but lacked a couple of elements that would allow it to truly realize loose coupling, it's required use slowed down prototyping, and you had no control to query or modify the navigation rule set at runtime. Your going to find that in JSF 2, the navigation system is much more flexible. You could argue that it finally accomplishes its original goals. For prototype applications, you can get navigation working without touching the faces-config.xml descriptor with implicit navigation. Just use a view ID, with or without an extension, as the logical outcome and away you go. As the application matures, you can establish a clean separation between JSF and your transactional tier by using conditional navigation to select a navigation case. You can trim the number of navigation cases by defining the target view ID as a value expression and having JSF resolve the target view ID from a navigation helper bean. If the design of your application calls for bookmarkable support, you can leverage the navigation handler in its new role to produce bookmarkable URLs at render time. In JSF 2, it's a lot easier to route the user around the application. While that may be good for some applications, other applications never advance the user beyond a single page. These single page applications transform in place using Ajax and partial page updates. The next article in this series will open your eyes to how well Ajax and JSF fit together, and what new Ajax innovations made their way into the spec.
November 2, 2009
by Dan Allen
· 139,469 Views · 2 Likes
article thumbnail
JSF 2 GETs Bookmarkable URLs
JSR-314: JavaServer Faces (JSF) 2.0 demonstrates a strong evolution of the JSF framework driven by de facto standards that emerged out of the JSF community and participating vendor's products. This article, the second installment in covering JSF 2.0 features contributed by Red Hat, or which Red Hat participated in extensively, covers the new features that bring formalized GET support to a framework traditionally rooted in POST requests. The primary building blocks of this support are view parameters and a pair of UI components that produce bookmarkable hyperlinks. Both features incubated in Seam and, therefore, should be familiar to any Seam developer. They are also features for which the JSF community has passionately pleaded. Author's Note: Many thanks to Pete Muir, who played a pivotal role as technical editor of this series. Read the other parts in this article series: Part 1 - JSF 2: Seam's Other Avenue to Standardization Part 2 - JSF 2 GETs Bookmarkable URLs Part 3 - Part 4 - Part 5 - Every user session must start somewhere. JSF was designed with the expectation that the user always begins on a launch view. This view captures initial state and allows the user to indicate which action to invoke by triggering a UI event, such as clicking a button. For instance, to view a mortgage loan, the user might enter its id into a text box and then click the "Lookup" button. The assumption that this scenario is the norm is surprising since it overlooks that fact that the web was founded on the concept of a hyperlink. A hyperlink points to a resource (URI), which may already contain the original state and intent, such as to view a mortgage loan summary. There's no need to bother the user with a launch view in this case. While hyperlinks are most often used in web sites, they apply to web applications as well (see this blog entry for a discussion about the difference between a web site and a web application). Hyperlinks support reuse by serving as an exchange language in composite applications. One application can link to a resource in another application in lieu of having to duplicate its functionality. In fact, the request may be coming from a legacy application that isn't even web-based. In that case, you'll likely be plopping the user into the web application somewhere in the middle. As it turns out, this situation is quite common. When you visit a blog, do you start on the search screen to find an entry to read? Not likely. More times than not, you click on a link to view a specific blog entry. The point to take away from this discussion is that initial requests (referred to as non-faces requests in JSF) can be just as important as form submissions (faces requests), whether in a web site or web application. In the past, JSF has struggled to support the scenario cited above, placing much more emphasize on faces requests. JSF 2 rectifies this imbalance by introducing view parameters and hyperlink-producing UI components. View parameters allow the application to respond to a resource request by baking formal processing of request parameters into the JSF life cycle for both GET and POST requests. View parameters are not limited to consuming data. They are bi-directional. JSF 2 can propagate the data captured by view parameters when generating bookmarkable URLs, with complementary behavior for redirect URLs produced by redirect navigation cases. We'll start by examining view parameters, how they are defined and how they are worked into the JSF life cycle. You'll then discover how they work in tandem with the new hyperlink-producing components and the navigation handler to bring "bookmarkable" support to JSF. Introducing view parameters The API documentation describes a view parameter, represented by the javax.faces.component.UIViewParameter component class, as a declarative binding between a request parameter and a model property. The binding to the model property is expressed using an EL value expression (e.g., #{blog.entryId}). If the expression is omitted, the request parameter is bound instead to a request-scoped variable with the same name. Here's a simple example of a view parameter that maps the value of a request parameter named id to the JavaBean-style property named entryId on a managed bean named blog. Assuming the entryId property on the blog managed bean is of type long, a value of 9 will be assigned to the property when the following URL is requested: http://domain/blog/entry.jsf?id=9 But wait, there's more! The value of the request parameter is first converted and validated before being assigned to the model property. This behavior should sound familiar. That's because it mirrors the processing of form input bindings on a faces request. In a way, view parameters turn the query string into an alternative form submission. And like form inputs, view parameters are also processed during faces requests. The complete view parameter life cycle is covered later when we look at view parameter propagation Before going any further, it's important to point out that view parameters are only available when using the new View Declaration Language (VDL), a standardized version of Facelets. The primary reason is because the JSR-314 EG agreed that no new features should be made to support JSP since it's deprecated as a view handler in JSF 2. Perhaps you are thinking... Isn't this already possible? If you are savvy JSF developer, you're perhaps aware that it's already possible to map a request parameter value to a model property. The assignment is declared by referencing an element of the #{param} map in a element of a managed bean declaration. For instance, you could alternatively map the id request parameter to the blog managed bean in faces-config.xml as follows: blog com.acme.Blog entryId #{param['id']} The similiarities end there. View parameters go above and beyond this simple assignment by providing: View-oriented granularity (the property mapping in the managed bean definition is global to the application) Custom converters and/or validators (along with failure messages) Bi-directionality It's hard to say which feature is the most important, but bi-directionality is certainly the most unique. Since view parameters are a mapping to a JavaBean-style property, the value can be read from the property and propagated to the next request using either the query string or the UI component tree state (depending on the type of request). You are going to find out how useful this bi-directionality can be later on in the article. Suffice to say, while the property mapping in the managed bean definition works, it's pretty anemic. View parameters are far more adequate and robust in contrast. Pertaining to the topic in this article, view parameters are the key to bringing bookmarkable support to JSF. And since bookmarks link to specific views, so must view parameters. It's all in the view As you may have guessed, view parameters are view-oriented. That means they somehow need to be associated with one or more views (as opposed to being linked to a managed bean, for instance). Up to this point, however, there was no facility for associating extensible, non-rendering metadata with a JSF view. So the EG first had to find a place within the UI component tree to stick metadata like view parameters. That led to the introduction of the metadata facet of UIViewRoot. The next section will introduce this new facet and how it's used to host view parameters for a particular view, or even a set of views. Then we get into how view parameters get processed in the JSF life cycle. The view metadata facet View parameters provide information about how request parameters should be handled when a view is either requested or linked to. The view parameters are not rendered themselves. Therefore, we say that they are part of the view's metamodel and described using metadata. So the question is, "Where should this metadata live?" It turns out that a JSF view, which is represented at the root by the javax.faces.component.UIViewRoot component class, already accommodates some metadata. Currently, this metadata consists of string values to define settings such as the locale, render kit, and content type of the view, and method expressions that designate view-specific phase observers. For example: ... While values can be assigned to these metadata properties explicitly in Java code, more often they are assigned declaratively using corresponding attributes of the component tag. But neither UIViewRoot or it's component tag can accommodate complex metadata--that is, metadata which cannot be described by a single attribute. That's were the view metadata facet comes in. The view metadata facet is a reserved facet of UIViewRoot, named javax_faces_metadata, that can hold an arbitrarily complex branch of UI components that provide additional metadata for a view. Facets are special because they are ignored by a UI component tree traversal, requiring an imperative request to step into one of them. This aspect makes a facet an ideal candidate for tucking away some metadata for the view that can be accessed on demand. The view metadata facet looks like any other facet in the UI component tree. It must be declared as a direct descendant of within a view template as follows: ... ... Note: If you are using Facelets, you may not be familiar with the tag since it's optional in Facelets. When you add it to your template, it must be the outer-most component tag, but it does not have to be the root of the document. Since the view metadata facet is a built-in facet, and is expected to be heavily used, the alias tag was introduced as a shorthand for the formal facet definition shown above: ... ... We now have a place in the UI component tree to store metadata pertaining to the view. But why define the metadata in the view template? It's all about reuse and consistency. Describing view metadata with UI components There are two important benefits to defining the metadata within the view template. First, it circumvents introducing yet another XML file with its own schema that developers would have to learn. More importantly, it allows us to reuse the UI component infrastructure to define behavior, such as registering a custom converter or validator, or to extract common view parameters into an include template. Since we're using UI components to describe the view metadata, then it makes sense to treat the UIViewParameter like any other input component. In fact, it extends UIInput. That allows us to register custom converters and validators on a UIViewParameter without any special reservations. Here's an example: Note: Later in this series you'll learn that like input components, view parameters can enforce constraints defined by Bean Validation annotations (or XML), making the explicit validation tags such as this unnecessary. But there is one caveat to embedding the view metadata in the template. Without special provisions, extracting the metadata would require building the entire view (i.e., UI component tree). Not only would this be expensive and unnecessary if the intent is not to render the view, it could also have side effects. When the component tree is built, value expressions in Facelets tag handlers get evaluated, potentially altering the state of the system. To prevent these counteractions, the view metadata facet is given special treatment in the specification. Specifically, it must be possible to be extract and built it separately from the rest of the component tree. Earlier, I mentioned that view parameters are only available in Facelets, and not JSP, because of an executive decision. There's also a technical reason why view parameters rely on Facelets. Only Facelets can provide the necessary separation between template parsing and component tree construction that allows a template fragment to be processed in isolation. The result of this operation is a genuine UI component tree, represented by UIViewRoot, that contains only the view metadata facet and its children. For all intents and purposes, it's as though the view template only contained this one child element. Using the following logic, it's possible to retrieve the metadata for an arbitrary view at any point in time. This data mining will come in to play later when we talk about view parameter propagation. String viewId = "/your_view_id.xhtml" FacesContext ctx = FacesContext.getCurrentInstance(); ViewDeclarationLanguage vdl = ctx.getApplication().getViewHandler() .getViewDeclarationLanguage(ctx, viewId); ViewMetadata viewMetadata = vdl.getViewMetadata(ctx, viewId); UIViewRoot viewRoot = viewMetadata.createMetadataView(ctx); UIComponent metadataFacet = viewRoot.getFacet(UIViewRoot.METADATA_FACET_NAME); At this point you could retrieve the UIViewParameter components, which are children of the facet, to perhaps access the view parameter mappings. More likely, though, you'll be looking for your own custom components so you can execute custom behavior before the view is rendered (e.g., view actions). The extraction of the view metadata is very clever because, while it only builds a partial view, it still honors Facelets compositions. That means you can put your metadata into a common template and include it. Using some creative arrangement, you can apply common metadata to a pattern of views. Here's an example: ... ... You've learned that defining a view metadata facet provides the following services for JSF: Arbitrarily complex metadata, which can reuse existing component infrastructure Metadata is kept with the view, or in a shared template, instead of in an external XML file Can be extracted and processed without any side effects (idempotent) Common metadata declarations can be shared across multiple views Now that you are well versed in the view metadata facet, it's time to work out a concrete example of view parameters in practice. We'll look at how to enforce preconditions and load data on an initial request using information from the query string. Then you'll learn how that information gets propagated as the user navigates to other views. Weaving parameters into the life cycle This article has alluded several times to the use case of loading a blog entry from a URL by passing the value of the id parameter to our managed bean on an initial request. Let's allow this scenario to play out. Here's the URL the user might request coming into the site: http://domain/blog/entry.jsf?id=9 We'll start by asking what we do with the value once it is assigned to the entryId property of the blog managed bean. One approach is to load the entry lazily as soon as it's referenced in the UI. #{blog.blogEntry.title} #{blog.blogEntry.content} Here's what the managed bean would look like to support this approach: @ManagedBean(name = "blog") public class Blog { private Long entryId; private BlogEntry blogEntry; public Long getEntryId() { return entryId; } public void setEntryId(Long entryId) { this.entryId = entryId; } public BlogEntry getBlogEntry() { if (blogEntry == null) { blogEntry = blogRepository.findEntry(entryId); } return blogEntry; } } Of course, it doesn't make any sense to display an entry without an id (and could even lead to a NullPointerException). So we should really make the id request parameter required. We'll also add a message if it is missing. ... In the case a required request parameter is missing, you can display the error message using the tag. Conversion and validation failures are recorded as global messages since there's no view element with which to associate. But these preconditions still don't stop the view from being rendered if a request parameter is missing or invalid. What we need is a way to execute an initialization method that parallels an action invocation on a postback. That would allow us to get everything sorted before the user sees a response. View initialization While view parameters provide the processing steps from retrieving the request value to updating the model, they do not furnish the action invocation and navigation steps that are part of the faces request life cyle. That means you have to fall back to lazy loading the data as the view is being rendered (i.e., encoded). You are also missing a definitive point to fine tune the UI component tree programmatically before it's encoded. Fortunately, another new feature in JSF 2, system events, makes it possible to perform a series of initialization steps before view rendering begins. Systems events notify registered listeners of interesting transition points in the JSF life cycle at a much finer-grained level than phase listeners. In particular, we are interested in the PreRenderViewEvent, which is fired immediately after the component tree is built (but not yet rendered). If the word "registered" evokes dreadful memories of XML descriptors, fear not. Observing the event we are interested in is just a matter of appending one or more elements to the view metadata facet. The tag has two required attributes, type and listener. The type attribute is the name of the event to observe derived by removing the Event suffix from the end of the event class name and decaptializing the result. We are only interested in one event, preRenderView. The listener attribute is a method binding expression pointing to either a no-arguments method or a method that accepts a SystemEvent. ... We can use this method to retrieve the blog entry before the view is rendered. public void loadEntry() { blogEntry = blogRepository.findEntry(entryId); } If the entry cannot be found, you could add conditional logic to the view to display an error message: The blog entry you requested does not exist. Ideally, it would be better not to display the view at all. You can force a navigation to occur using the NavigationHandler API. public void loadEntry() { try { blogEntry = blogRepository.findEntry(entryId); } catch (NoSuchEntryException e) { FacesContext ctx = FacesContext.getCurrentInstance(); ctx.getApplication().getNavigationHandler() .handleNavigation(ctx, "#{blog.loadEntry}", "invalid"); } } The only problem is that the listener method is going to be invoked even if the view parameter could not be successfully converted, validated and assigned to the model property. Once again, there's a JSF 2 feature to the rescue. You can use the new isValidationFailed() method on FacesContext to check whether a conversion or validation failure occurred while processing the view parameters. public void loadEntry() { FacesContext ctx = FacesContext.getCurrentInstance(); if (ctx.isValidationFailed()) { ctx.getApplication().getNavigationHandler() .handleNavigation(ctx, "#{blog.loadEntry}", "invalid"); return; } // load entry } So far we have dealt with a trivial string to long conversion. But view parameters allow you to represent more complex data, as long as you have a converter that can marshal the value from (and to) a string. Let's assume that we want to allow the user to look at blog entries that fall within a range of dates. The before and after dates can be encoded into the URL as follows: /entries.jsf?after=2007-12-31&before=2009-01-01 Those values can then be converted to Date objects using the converter tag and assigned to Date properties on a managed bean as follows: We again use a PreRenderViewEvent listener to load the data before the page is rendered, in this case filtering the collection of blog entries to be displayed. Emulating the behavior of an action-oriented framework, which the previous examples have demonstrated, is one use of the PreRenderViewEvent. Another is to act as a life cycle callback for programmatically creating or tweaking the UI component tree after it is "inflated" from the view template. Perhaps you want to build part of the tree dynamically from a data structure. Accomplishing this in JSF would require "binding" a bean property to an existing UI component, declared using an EL value expression in the binding attribute of the tag. But this approach is really ugly because you have to put the tree-appending logic in either the JavaBean property getter or setter, depending on whether the view is being created or restored. The PreRenderViewEvent offers a much more definitive and self-documentating hook. As you've seen, it's finally possible to respond to a bookmarkable URL in JSF (without pain or brittle code). But, up to this point, all we've done is take, take, take. For bookmarkable support to be complete, we need to be able to create bookmarkable URLs. That brings us to the topic of parameter propagation. Push the parameters on If view parameters were only capable of accepting data sent through the query string of the URL, even considering the built-in conversion and validation they provide, they really wouldn't be all that helpful. What makes them so compelling is that they are bi-directional, meaning they are also propagated to subsequent requests, and rather transparently. The subsequent request may be a faces request, which targets the current view, or a non-faces request to a view that has view parameters, which translates into a bookmarkable URL. A request for a bookmarkable URL can come from either a link in the page or a redirect navigation event. We'll look at how view parameters get propagated in all of these cases in this section. Saved by the component tree Let's return to the blog entry view and consider what happens if we have a comment form below the post. The comment form might be defined as follows: Notice that there is no reference to the id of the blog entry in this form. Assuming that the blog entry is not stored in session scope (or a third-party conversation), how will the handler know which entry the comment should be linked? This is where view parameter propagation blends with component tree state saving. When encoding of the view (i.e., rendering) is complete, the view parameter values are tucked away in the saved state of the UI component tree. When the component tree is restored on a postback, such as when the comment form is submitted, the saved view parameter values are applied to the model. This allows view parameters to tie in nicely with the existing design of JSF. The initial state supplied to the view parameters by the URL can be maintained as long as the user interacts with the view (e.g., triggers faces requests through user interface events). You can think of view parameters as an elegant replacement for hidden form fields in this case. If the user bookmarked the URL after posting a comment, however, the reference to the blog entry would be lost. That's because after a POST request, the browser location does not contain a query string. Here's what the user would see: http://domain/blog/entry.jsf If we are following best practices, we'll want to implement the Post/Redirect/Get pattern anyway. That gives us a opportunity to repopulate the query string of the URL. In the past, this would have required an explicit call to the redirect() method of ExternalContext inside the action method. FacesContext.getCurrentInstance().getExternalContext().redirect("/entry.jsf?id=" + blog.getEntryId()); This explicit (and intrusive) call was necessary because the navigation case did not provide any way to append parameters to the query string. Now, view parameters can take care of this for us. We can tell JSF to encode the view parameters of the target view ID into the redirect URL by enabling the include-redirect-params attribute on the element. /entry.xhtml #{commentHandler.post} #{view.viewId} We'll get into navigation more in the next article in this series. Let's talk about those regular old hyperlinks in the page. We want those to be bookmarkable as well. That means the state needs to be encoded into the URL they point to. Once again, view parameters come into play. Bookmarkable links Let's now assume we want to create a bookmarkable link (permalink) to the current blog entry. You can link directly to another JSF view using the outcome attribute of the new hyperlink-producing component tags, and . These component tags are represented by the component class javax.faces.component.UIOutcomeTarget. (The reason the attribute is named outcome and not viewId will be explained in the next article. For now, just know that the value of the outcome attribute can be a view ID). Both of these component tags support encoding the view parameters into the query string of the URL as signaled by the includeViewParams attribute. Here's how the permalink is defined: The default value of includeViewParams is false. Since it's set to true, the view parameters are read in reverse and appended to the query string of the link. Here's the HTML that this component tag generated, assuming an entry id of 9: Permalink The link is produced using the new getBookmarkableURL() method on the ViewHandler API. This method calls through to the encodeBookmarkableURL() on the ExternalContext API to have the session ID token tacked on, if necessary. These methods complement the getRedirectURL() and encodeRedirectURL() methods on ViewHandler and ExternalContext, respectively. In a servlet environment, the implementations happen to be the same, but the extra methods serve as both a statement of intent and an extension point for environments where a link URL and a redirect URL are handled differently, such as a portlet. Notice that the context path of the application (/blog) is prepended to the path, the extension is changed from the view suffix (.xhtml) to the JSF servlet mapping (.jsf) and the query string contains the name and value of the view parameter read from the model. If you had used an tag, you would have had to do all of these things manually. That's exactly why the EG felt it was necessary to introduce this component. We can do one better. If the outcome attribute is absent, the current view ID is assumed. So we can shorten the tag to this: If you want the link to appear as a button, you can use the component tag instead. However, note that JavaScript is required in this case to update the browser location when the button is clicked, as you can see from the generated HTML: Permalink View parameters come in especially handy when the number of parameters to keep track of increases. For instance, let's consider the case when a user is searching for entries using a query string in a particular category and wants to paginate through the results. In this case, we are dealing with at least three parameters: Yet the link to these search results still remains as simple as the permalink to an entry: This component tag will produce HTML similar to this: Refresh What if we want to link back to the previous page? In that case, we cannot allow the view parameter named page be automatically written into the query string since that will just link us to the current page. We need an override. Fortunately, it's easy to override an encoded view parameter. You simply use the standard tag, just as you would if you were defining a new query string parameter: View parameters that are encoded into links to the current view ID are pretty intuitive. Where things get tricky is when we use view parameters on a link to a different view ID. This requires putting on your thinking cap and doing some reasoning. View parameter handoff When a request is made for a URL, and in turn a JSF view ID, the view parameters defined in that view are used to map request parameters to model properties. But when the view parameters are encoded into a bookmarkable URL, the mappings are read from the target view ID. That's why it's especially important to be able to extract the view metadata from a template without having building a full component tree, as mentioned earlier. Otherwise, you would end up building a component tree for every view that is linked to in the current view. That would be very costly. Let's consider a use case. Suppose that we want to create a link from the search results to a single entry. We would define the link as follows: #{_entry.title} #{_entry.excerpt} The question to ask yourself is this. "Are the search string, category and page offset included in the URL for the entry?". I hope you said "No". The reason is because when the URL for the entry link is built, the component tag reads the view parameter mappings defined in the /entry.xhtml template. The only parameter mapped in that template is entry id. In order to preserve the filter vector, the view parameters defined in the /entries.xhtml view need to also be in the /entry.xhtml template. Aha! Since these are shared view parameters, we should define them in a common template: We can then include that template in each view that needs to preserve these view parameters: ... Keep in mind that if the user navigates to the entry after performing a search, the URL for the entry shown in the browser's location bar will now contain the filter vector. But if you want the user to be able to return to the search filter (without using the back button), that's what you want. You can always provide a simple permalink to bookmark just the entry. Even though you are now defining view parameters in each of the views, that doesn't mean the URL will become littered with empty query string parameters when they are not in use. View parameters are only encoded (i.e., added to the query string) if the value is not null. Otherwise, there is no trace of the view parameter. You have now learned how view parameters are propagated during a postback, on a redirect and into a bookmarkable URL. The main benefit of this process is that it is transparent. You don't have to worry about each and every request parameter that comprises the state in the query string of the URL. Instead, JSF interprets the view parameter metadata defined in the template of the target view and automatically appends those name/value pairs to the URL when you activate this feature. Bookmark it View parameters serve as an alternative to storing state in the UI component tree, provide a starting point for the application, help integrate with legacy applications, assert preconditions of views, make views bookmarkable, and, with help of the new UIOutcomeTarget components or the enhancement to the redirect navigation case, produce links to those bookmarkable views. This article began by introducing you to the view metadata facet, which is a general facility for defining a view's metamodel that reuses the existing UI component infrastructure. You learned that view parameters and PreRenderViewEvent listeners are the first standard implementations of view metadata. You saw how the combination of these two features allow you to capture initial state from URL query string, validate preconditions and load data for a view all before the view is rendered. Finally, you learned how view parameter values are propagated to subsequent requests. This series continues by taking a deeper look at the navigation enhancements made in JSF 2 and explaining how those changes tie into the bookmarkability that you learned about in this article. So bookmark and check back again soon!
October 29, 2009
by Dan Allen
· 103,193 Views
article thumbnail
Creating a Custom JSF 1.2 Component - With Facets, Resource Handling, Events and Listeners
I occasionally create custom JavaServer Faces components. Just enough to sort of remember what the steps are, but not nearly frequently enough to quickly put a new component together. This article demonstrates the quick step approach to creating a new custom component in the old fashioned way (that means: it is not a Facelets template based or an ADF Faces 11g Declarative Component). Its primary purpose is to help me quickly retrace my steps. But perhaps it will benefit some of you as well. The Shuffler component I will develop supports facets. It will render its facet children - one after the other. Which one is rendered first can be indicated through an attribute facetOrder (values normal, reverse and random), which is EL enabled. A shuffler-method-expression can optionally be set to provide the Shuffler with a shuffle-order-processor: the method is invoked with the list of facets to shuffle and will return it in the order in which to render the children. The component can render with a shuffle icon that when pressed causes the children to be shuffled. The Shuffler component allows registration of Shuffle Event Listeners, custom listeners that are informed whenever the shuffle event occurs. An example of how the Shuffler can be used inside a JSF page: Some elements of custom JSF components that are explicitly discussed in this article: dynamic attributes of type ValueExpression (EL enabled) attributes of type MethodExpression (also EL enabled) facets (custom) events and listeners Bare essentials for custom JSF components A custom JSF component is represented by a Java Class - one that extends from UIComponentBase. An instance of this class is created whenever a new page is rendered that contains the component (and for each occurrence of the component in the page, a new instance of the class is created). The component class holds the attributes that are set by the page developer and that determine the behavior and appearance of the component. The component class has the internal logic of the component and it deals for example with events and listeners. This class may also render the markup (HTML) for the component - though it is a better practice to leave the actual rendering to a Renderer class. public class Shuffler extends UIComponentBase { ... } Most custom JSF component will also have an associated Renderer class, that extends from Renderer. Note that some components will not actually be rendered (such as Listeners, Iterators or Parameters) and therefore will not have a Renderer class. The Renderer is not only responsible for rendering the HTML, it will also inspect (decode) the incoming request from the browser to see whether the request parameter map contains values that are of interest to the component - that indicate for example that a value has been entered or set on the component(’s representation in the browser) or an action has been executed against it. Note that one JSF component may have multiple Renderers, for example for different channels and protocols (to render a representation of the component in plain XML, in WML, in JavaFX or XUL) or for different user agents (Firefox, Internet Explorer) or themes (professional user, internet surfer). public class ShufflerRenderer extends SuperRenderer { ... } JavaServer Faces pages can be created in various ways - including programmatically, using Facelets and using JSP pages. The latter option, through JSP, is still the most common one, though that is about to change with JSF 2.0 favoring Facelets. Page developers using JSPs will describe the JSF component tree that will need to be instantiated in memory for rendering a certain View using a plain JSP page. The tags in the JSP page are normal JSP tags - described by TLD (tag library descriptors) - corresponding to JSF components and therefore JSF component classes. Every JSF component that needs to be used in JSPs has to have a corresponding JSP tag-class, one that will typically extend from UIComponentELTag (or just from TagSupport when no JSF component is added to the component tree for a certain tag, for example when that tag represents a listener or parameter). The Tag Class specifies which JSF Component it represents. It also indicates which Renderer should be used to render the component. This means that one component can have multiple JSP tags associated with it, each providing a different way of rendering the component. Note: the renderer can also be specified dynamically - taking user preferences or characteristics into account public class ShufflerTag extends UIComponentELTag { public static final String COMPONENT_TYPE = "nl.amis.jsf.UIShuffler"; public static final String RENDERER_TYPE = "nl.amis.jsf.ShufflerRenderer"; public String getComponentType() { return COMPONENT_TYPE; } public String getRendererType() { return RENDERER_TYPE; } ... } Tags representing JSF components need to be described in TLD files (Tag Library Descriptors) just like any other JSP tag.The entry in the TLD defines the tag label to use in the page, whether the tag can contain child-tags, some descriptive meta data and every attribute that can be configured in the tag. For each attribute the TLD-entry specifies the type, whether it is required and if the attribute can contain an EL expression passing in a value or an EL expression passing in a method; in the latter case, the entry also prescribes the signature of the method: ShufflerLib 1.0 ShufflerLib /nl.amis,jsf/ShufflerLib Writes a DIV element that contains the facets in a specific order. shuffler nl.amis.jsf.shuffler.ShufflerTag JSP id false true rendered false boolean binding false javax.faces.component.UIComponent styleClass false java.lang.String .... JSF components need to be registered in a special faces-config.xml file (special in the sense that it is not the faces-config.xml that drives a web application but rather one that acts like a repository of components and their renderers. Note however that all entries in this special faces-config.xml is merged together with the ‘normal’ faces-config.xml. That means in turn that while the special file is primarily seen as the registry of components, it can also configure PhaseListeners, Navigation Rules (hard to see the value in that) and Managed Beans (which can be very useful). The component registration in faces-config.xml consists of a component type that is associated with a the component class. nl.amis.jsf.UIShuffler nl.amis.jsf.shuffler.Shuffler Renderers can also be registered in this file. A renderer entry registers a renderer-type (corresponding to the value returned by the getRendererType() method in the tag class) associated with the RendererClass. Based on the value (rendererType) returned by the tag class, the correct class to instantiate can be determined from this entry: nl.amis.Shuffler nl.amis.jsf.ShufflerRenderer nl.amis.jsf.shuffler.ShufflerRenderer Implementing the Classes: Component, Renderer and TagHandler The TagHandler ShufflerTag is the intermediary between the world of JSP pages (and the Servlet/JSP engine that translates the JSP file into a servlet class) and the JSF realm. Every tag in the JSP page needs to be turned into its corresponding JSF representation. The tag handler needs to override the setProperties() method inherited from the UIComponentELTag class; this method takes all the values set on the tag attributes in the page and passes them onwards to the Component. In our initial case, the tag is used in JSPs like this: ... other content The styleClass attribute is the only one we defined - id and rendered are defined on every JSP-tag based on JSF’s UIComponentELTag. Thye styleClass attribute is also the only attribute we need to take responsibility for in the tag class, by providing a setter method that sets a private member and by passing the value of that private member to the component in the setProperties() method. The code for the ShufflerTag class now becomes: package nl.amis.jsf.shuffler; import javax.el.ValueExpression; import javax.faces.component.UIComponent; import javax.faces.webapp.UIComponentELTag; public class ShufflerTag extends UIComponentELTag { public static final String COMPONENT_TYPE = "nl.amis.jsf.UIShuffler"; public static final String RENDERER_TYPE = "nl.amis.jsf.ShufflerRenderer"; private ValueExpression styleClass; public String getComponentType() { return COMPONENT_TYPE; } public String getRendererType() { return RENDERER_TYPE; } protected void setProperties(UIComponent component) { super.setProperties(component); processProperty(component, styleClass, Shuffler.STYLECLASS_ATTRIBUTE_KEY); } public void release() { super.release(); styleClass= null; } protected final void processProperty(final UIComponent component, final ValueExpression property, final String propertyName) { if (property != null) { if(property.isLiteralText()) { component.getAttributes().put(propertyName, property.getExpressionString()); } else { component.setValueExpression(propertyName, property); } } } public void setStyleClass(ValueExpression styleClass) { this.styleClass = styleClass; } } We cater for the fact that styleClass can contain a ValueExpression - as all attributes can, starting from JSF 1.2. In the method processProperty we check whether the string passed for styleClass is a literal string or should be considered an EL expression. In the latter case, we pass a ValueExpression to the component, otherwise a ‘normal’ attribute. Also note that the super class takes care of the attributes id, rendered and binding. However, we do have to specify them in the tag-library. The component class in our case leads a pretty comfortable life: the tag handler informs him of all the attribute values and the actual rendering is left to a special Renderer class. The component is a pretty passive element in this simple example: package nl.amis.jsf.shuffler; import javax.faces.component.UIComponentBase; import javax.faces.context.FacesContext; public class Shuffler extends UIComponentBase { public static final String FAMILY = "nl.amis.Shuffler"; public static final String STYLECLASS_ATTRIBUTE_KEY = "styleClass"; public String getFamily() { return FAMILY; } @Override public Object saveState(FacesContext facesContext) { Object values[] = new Object[2]; values[0] = super.saveState(facesContext); values[1] = this.getAttributes().get(STYLECLASS_ATTRIBUTE_KEY); return values; } @Override public void restoreState(FacesContext facesContext, Object state) { Object values[] = (Object[])state; super.restoreState(facesContext, values[0]); this.getAttributes().put(STYLECLASS_ATTRIBUTE_KEY, values[1]); } } The only really useful thing the component does is implementing the saveState and restoreState methods. These methods play an important part in turning the state of the component into a serializable object array and restoring that state of the component in the RestoreView phase, based on the serialized array. The Tag Handler specifies in its getRendererType() method that the renderer to use for this component when using the shuffler tag, is one called nl.amis.jsf.ShufflerRenderer. In the faces-config.xml file, we have indicated that this renderer type is associated with the class nl.amis.jsf.shuffler.ShufflerRenderer that extends Renderer. The renderers in JSF can override methods like encodeBegin(), encodeEnd(), encodeChildren() and decode() - the latter only when we have to process the incoming request, looking for new values set on or events that occurred on the component. In our case, we initially will simply have the ShufflerRenderer render a DIV element with a class attribute (based on the styleClass attribute). The DIV will allow the children of the Shuffler component to render - by not overriding the encodeChildren() method. package nl.amis.jsf.shuffler; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.render.Renderer; public class ShufflerRenderer extends Renderer { @Override public void encodeBegin(final FacesContext facesContext, final UIComponent component) throws IOException { super.encodeBegin(facesContext, component); final ResponseWriter writer = facesContext.getResponseWriter(); writer.startElement("DIV", component); String styleClass = (String)attributes.get(Shuffler.STYLECLASS_ATTRIBUTE_KEY); writer.writeAttribute("class", styleClass, null); } @Override public void encodeEnd(final FacesContext facesContext, final UIComponent component) throws IOException { final ResponseWriter writer = facesContext.getResponseWriter(); writer.endElement("DIV"); } } Next steps - working with facets The Shuffler component is created to dynamically (re)order its child contents. It will do so using facets. The content you want this component to shuffle is passed in two or more facets. The facets are named using string representations of integers, so for example: ... content ... content ... content Facets are automatically supported on JSF components. The getFacets() method is available inside the Shuffler component class and will return a collection of facet UIComponents. Facets are special children for a JSF component: the framework will never render the contents of facets on its own. It is up to the component to determine when and how to render the contents of its facets. So, there is some work to do for the ShuffleRenderer class. But first we need to add support for the new facetOrder attribute. Adding an attribute means: adding an attribute entry in the TLD adding support for processing the attribute in the Tag Handler (a setter and a line of code in setProperties()) adding the attribute in saveState() and restoreState() in the Component class Here we go: In the tld entry, add: facetOrder false java.lang.String In the tag-handler class ShufflerTag add: private ValueExpression facetOrder; public void setFacetOrder(ValueExpression facetOrder) { this.facetOrder = facetOrder; } and in setProperties(): processProperty(component, facetOrder, Shuffler.FACETORDER_ATTRIBUTE_KEY); Finally in the component class Shuffler , add: public static final String FACETORDER_ATTRIBUTE_KEY = "facetOrder"; @Override public Object saveState(FacesContext facesContext) { Object values[] = new Object[3]; values[0] = super.saveState(facesContext); values[1] = this.getAttributes().get(STYLECLASS_ATTRIBUTE_KEY); values[2] = this.getAttributes().get(FACETORDER_ATTRIBUTE_KEY); return values; } @Override public void restoreState(FacesContext facesContext, Object state) { Object values[] = (Object[])state; super.restoreState(facesContext, values[0]); this.getAttributes().put(STYLECLASS_ATTRIBUTE_KEY, values[1]); this.getAttributes().put(FACETORDER_ATTRIBUTE_KEY, values[2]); } The Shuffler also needs to make the facets available to the renderer, in the order that is prescribed by the facetOrder attribute. This attribute supports three values: normal, reverse and random. public List getOrderedFacets(FacesContext facesContext) { // allowable values: normal (default) and reverse // the normal order of the facets is determined by ordering the facets by name (assuming the facetnames are string representations of integers) // create a sorted list with the integers representing the facets List facetIndexValues = new ArrayList(); List facetNames = new ArrayList(getFacets().keySet()); for (String facetName : facetNames) { facetIndexValues.add(new Integer(facetName)); } Collections.sort(facetIndexValues); // create the list of facets corrresponding to the sorted list of facet index values List orderedFacets = new ArrayList(); for (Integer index : facetIndexValues) { orderedFacets.add(getFacets().get(index.toString())); } // depending on the value for the facetOrder attribute, we may need to reorganize the orderedFacets list String facetOrder = (String)this.getAttributes().get(Shuffler.FACETORDER_ATTRIBUTE_KEY); if ("reverse".equalsIgnoreCase(facetOrder)) { Collections.reverse(orderedFacets); } else if ("random".equalsIgnoreCase(facetOrder)) { Collections.shuffle(orderedFacets); } else if ("normal".equalsIgnoreCase(facetOrder)) { // need to do nothing as with normal the order returned by getFacets() is the correct one } return orderedFacets; } The ShufflerRenderer will have to do the real work. It will retrieve the facets - in the proper order - from the Shuffler Component class and ask JSF to render them. package nl.amis.jsf.shuffler; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.render.Renderer; import javax.faces.component.UIComponent; public class ShufflerRenderer extends Renderer { @Override public void encodeBegin(final FacesContext facesContext, final UIComponent component) throws IOException { super.encodeBegin(facesContext, component); final ResponseWriter writer = facesContext.getResponseWriter(); writer.startElement("DIV", component); String styleClass = (String)attributes.get(Shuffler.STYLECLASS_ATTRIBUTE_KEY); writer.writeAttribute("class", styleClass, null); List orderedFacets = ((Shuffler)component).getOrderedFacets(facesContext); for (UIComponent facet:orderedFacets) { facet.encodeAll(facesContext); } } @Override public void encodeEnd(final FacesContext facesContext, final UIComponent component) throws IOException { final ResponseWriter writer = facesContext.getResponseWriter(); writer.endElement("DIV"); } } With these changes, we can now add real content to the Shuffler and have it rendered, in the order we specified - which can be random. Also note that we can use an EL expression to have the facetOrder dynamically derived: facetOrder="#{bean.liveFacetOrder}" id="s1" > ... content ... content ... content Downloading Resources The next step in our exploration of the development of custom JSF components is the addition of resources like images and JavaScript libraries. Note that in JavaServer Faces 2.0 a new facility is available, especially for this purpose. However, in our 1.2 setting we have to come up with something ourselves. That is not to say no solutions exist for JSF 1.2; almost every library comes with a form of resource handling. Then there is the Weblet framework that was introduced especially for this purpose. Another option leverages JSF itself: its capability through PhaseListeners to intercept a request, interpret the requested ViewId and optionally serve up an image or JS file in response to the request. This approach is proposed in JavaServer Faces, The Complete Reference by Ed Burns and Chris Schalk. I have slightly modified there code for my own purposes. However, the central idea clearly is theirs. My objective is to add an image to the Shuffler component. The next step will be to allow the user to click on the image and by doing so tgrigger a re-shuffle. But that part is for later, first add the image itself. The HTML rendered by the ShufflerRenderer needs to be extended with the IMG tag, that is easy enough. Less trivial is the value for the SRC attribute on the IMG tag. The change in the encodeBegin method in the ShufflerRenderer: writer.startElement("IMG", component); writer.writeAttribute("src", imageUrl( facesContext,SHUFFLE_IMAGE), null); writer.writeAttribute("alt", "Click to reshuffle", null); writer.writeAttribute("width", "20px", null); writer.endElement("IMG"); With SHUFFLE_IMAGE specified as: private static String SHUFFLE_IMAGE = "shuffleIcon.png"; The imageUrl() method is defined as follows private final static String IMAGE_PATH ="/faces/images/"; protected String imageUrl(FacesContext facesContext, String image) { ViewHandler handler = facesContext.getApplication().getViewHandler(); String imageUrl = handler.getResourceURL(facesContext, IMAGE_PATH + image); return imageUrl; } The URLs for images are now constructed to look like this: http://somehost:7101/CustomJSFConsumer/faces/images/shuffleIcon.png The request for the shuffleIcon.png that is sent by the browser should be intercepted by a component that knows how to handle it. Because of the /faces/ part, this request is sent to the FacesServlet and processed through the JSF lifecycle. The componoent to intercept it will be a phaseListener that fires after restore view. It inspects the ViewId. When the ViewId contains the predefined indicator ("/images/") it steps in and takes over processing of the request. It will find the name of the image that is requested by taking the part of the ViewId that comes after /images/. It will then locate the image file on the classpath (that works well for a component packaged in a jar file, it can have the images packaged in the jar file too), looking for a directory called /images/ - as specified by the IMAGE_PATH constant. It copies the image from the file to the outputstream after setting the content type. package nl.amis.jsf; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.URL; import java.net.URLConnection; import javax.faces.context.FacesContext; import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseId; import javax.faces.event.PhaseListener; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletResponse; public class ResourceServerPhaseListener implements PhaseListener { public ResourceServerPhaseListener() { super(); } public PhaseId getPhaseId() { return PhaseId.RESTORE_VIEW; } public void afterPhase(PhaseEvent event) { // If this is restoreView phase if (PhaseId.RESTORE_VIEW == event.getPhaseId()) { if (-1 != event.getFacesContext().getViewRoot().getViewId().indexOf(RENDER_IMAGE_TAG)) { // extract the name of the image resource from the ViewId String image = event.getFacesContext().getViewRoot().getViewId().substring(event.getFacesContext() .getViewRoot().getViewId().indexOf(RENDER_IMAGE_TAG) + RENDER_IMAGE_TAG.length()); // render the script writeImage(event, image); event.getFacesContext().responseComplete(); } } } public void beforePhase(PhaseEvent event) { } public static final String RENDER_IMAGE_TAG = "/images/"; public static final String IMAGE_PATH = "/images/"; private void writeImage(PhaseEvent event, String resourceName) { URL url = getClass().getResource(IMAGE_PATH + resourceName); URLConnection conn = null; InputStream stream = null; HttpServletResponse response = (HttpServletResponse)event.getFacesContext().getExternalContext().getResponse(); try { conn = url.openConnection(); conn.setUseCaches(false); stream = conn.getInputStream(); ServletContext servletContext = (ServletContext)FacesContext.getCurrentInstance().getExternalContext().getContext(); String mimeType = servletContext.getMimeType(resourceName); response.setContentType(mimeType); response.setStatus(200); // Copy the contents of the file to the output stream byte[] buf = new byte[1024]; int count = 0; while ((count = stream.read(buf)) >= 0) { response.getOutputStream().write(buf, 0, count); } response.getOutputStream().close(); } catch (Exception e) { String message = null; message = "Can't load image file:" + url.toExternalForm(); try { response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); } catch (IOException f) { f.printStackTrace(); } } } } PhaseListeners need to be configured in order to be active. This configuration usually is done in the faces-config.xml of the application. Fortunately, we can also configure the PhaseListener in the faces-config.xml file that we create for the custom component. This faces-config.xml is part of the jar file in which the custom component is shipped and deployed. Its contents are merged with the application’s own faces-config.xml. The registration of our PhaseListener looks like this: nl.amis.jsf.ResourceServerPhaseListener ... Triggering events on the custom component Time to take another big step. We will support clicking the image by the end user and turn that event into a reshuffle of the facets of the Shuffler component. In the next section we will not only act on that click ourselves, but also publish an event that others can listen to. We will have to add a JavaScript event listener in the HTML rendered for the Shuffler. This client side code is triggered when the image is clicked. It will submit the form - after it has added an input element to the DOM and set a value on it. Note: this approach to have a custom component trigger an event that can be received by the server side renderer class has been described in Pro JSF and Ajax: Building Rich Internet Components - by John R. Fallows and Jonas Jacobi, the guys who first introduced me to JavaServer Faces. The JavaScript for the Shuffler component looks like this: /** * The onclick handler for ShufflerRenderer. * * @param formClientId the clientId of the enclosing UIForm component * @param clientId the clientId of the Shuffler component */ function _shuffle_click( formClientId, clientId) { var form = document.forms[formClientId]; var input = form[clientId]; if (!input) // if the input element does not already exist, create it and add it to the form { input = document.createElement("input"); input.type = 'hidden'; input.name = clientId; form.appendChild(input); } input.value = 'clicked'; form.submit(); } The JavaScript is not be directly included in the page - as it is part of the jar file in which the Shuffler component is shipped. We need a way to attach this JavaScript (it is in a file called shuffle.js) to the page from within the custom component, or in this case rather its Renderer class. We extend the ResourceServerPhaseListener to also handle JavaScript resources, just like it can handle images. public class ResourceServerPhaseListener implements PhaseListener { public static final String RENDER_SCRIPT_TAG = "/js/"; public static final String RENDER_IMAGE_TAG = "/images/"; public static final String SCRIPT_PATH = "/js/"; public static final String IMAGE_PATH = "/images/"; public PhaseId getPhaseId() { return PhaseId.RESTORE_VIEW; } public void afterPhase(PhaseEvent event) { // If this is restoreView phase if (PhaseId.RESTORE_VIEW == event.getPhaseId()) { // if the request is for a JavaScript library if (-1 != event.getFacesContext().getViewRoot().getViewId().indexOf(RENDER_SCRIPT_TAG)) { // extract the name of the script from the ViewId String script = event.getFacesContext().getViewRoot().getViewId().substring(event.getFacesContext() .getViewRoot().getViewId().indexOf(RENDER_SCRIPT_TAG) + RENDER_SCRIPT_TAG.length()); // render the script writeScript(event, script); event.getFacesContext().responseComplete(); } ... image handling, same as before } } public void beforePhase(PhaseEvent event) { } private void writeScript(PhaseEvent event, String resourceName) { URL url = getClass().getResource(SCRIPT_PATH + resourceName); URLConnection conn = null; InputStream stream = null; BufferedReader bufReader = null; HttpServletResponse response = (HttpServletResponse)event.getFacesContext().getExternalContext().getResponse(); OutputStreamWriter outWriter = null; String curLine = null; try { outWriter = new OutputStreamWriter(response.getOutputStream(), response.getCharacterEncoding()); conn = url.openConnection(); conn.setUseCaches(false); stream = conn.getInputStream(); bufReader = new BufferedReader(new InputStreamReader(stream)); response.setContentType("text/javascript"); response.setStatus(200); while (null != (curLine = bufReader.readLine())) { outWriter.write(curLine + "\n"); } outWriter.close(); } catch (Exception e) { String message = null; message = "Can't load script file:" + url.toExternalForm(); try { response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); } catch (IOException f) { f.printStackTrace(); } } } private void writeImage(PhaseEvent event, String resourceName) { ... same as before } } The Renderer class is responsible for rendering the markup that will include the JavaScript resources to the page (the script element). We could have multiple occurrences of our custom component in a page. However, the JavaScript file shuffle.js should be loaded only once, to prevent excessive and completely pointless browser requests. In order to make that happen, the Renderer indicates to a method writeScriptResource that it has a JavaScript resource that should be included. This method verifies whether a script tag for downloading that same resource has already been added in the current request. If so, it will not add another script tag. If not [already included] then the tag is added with its src attribute referring to the proper PhaseListener controlled url: protected void writeScriptResource( FacesContext context, String resourcePath) throws IOException { Set scriptResources = _getScriptResourcesAlreadyWritten(context); // Set.add() returns true only if item was added to the set // and returns false if item was already present in the set if (scriptResources.add(resourcePath)) { ViewHandler handler = context.getApplication().getViewHandler(); String resourceURL = handler.getResourceURL(context, SCRIPT_PATH +resourcePath); ResponseWriter out = context.getResponseWriter(); out.startElement("script", null); out.writeAttribute("type", "text/javascript", null); out.writeAttribute("src", resourceURL, null); out.endElement("script"); } } private Set _getScriptResourcesAlreadyWritten( FacesContext context) { ExternalContext external = context.getExternalContext(); Map requestScope = external.getRequestMap(); Set written = (Set)requestScope.get(_SCRIPT_RESOURCES_KEY); if (written == null) { written = new HashSet(); requestScope.put(_SCRIPT_RESOURCES_KEY, written); } return written; } static private final String _SCRIPT_RESOURCES_KEY = ShufflerRenderer.class.getName() + ".SCRIPTS_WRITTEN"; With these helper methods in place, the ShufflerRenderer can be extended to include the client side click handling code: @Override public void encodeBegin(final FacesContext facesContext, final UIComponent component) throws IOException { super.encodeBegin(facesContext, component); final Map attributes = component.getAttributes(); final ResponseWriter writer = facesContext.getResponseWriter(); String formClientId = _findFormClientId(facesContext, component); String shuffleClientId = component.getClientId(facesContext); writeScriptResource(context, "shuffle.js"); writer.startElement("DIV", component); String styleClass = (String)attributes.get(Shuffler.STYLECLASS_ATTRIBUTE_KEY); writer.writeAttribute("class", styleClass, null); writer.startElement("SPAN", component); writer.writeAttribute("onClick", "_shuffle_click('" + formClientId + "'," + "'" + shuffleClientId + "')", null); writer.startElement("IMG", component); writer.writeAttribute("src", imageUrl( facesContext,SHUFFLE_IMAGE), null); writer.writeAttribute("alt", "Click to reshuffle", null); writer.writeAttribute("width", "20px", null); writer.endElement("IMG"); writer.endElement("SPAN"); List orderedFacets = ((Shuffler)component).getOrderedFacets(facesContext); for (UIComponent facet:orderedFacets) { facet.encodeAll(facesContext); } } protected void encodeResources(FacesContext context, UIComponent component) throws IOException { writeScriptResource(context, "shuffle.js"); } /** * Finds the parent UIForm component client identifier. * * @param context the Faces context * @param component the Faces component * * @return the parent UIForm or RichForm (for usage in ADF) client identifier, if present, otherwise null */ private String _findFormClientId(FacesContext context, UIComponent component) { if (component==null) { return null; } if (component instanceof UIForm || component.getClass().getName().endsWith("RichForm")) { return component.getClientId(context); } else { return _findFormClientId(context, component.getParent()); } } The image is wrapped in a SPAN and the onclick event handler is defined on that SPAN element (this allows us to later on add more clickable stuff to the SPAN). When the image is clicked, the _shuffle_click function is invoked - that was loaded from shuffle.js. The element is added to the form and the form is submitted. The HTML rendered from this renderer now looks like this:
October 7, 2009
by Wouter Van Reeven
· 45,493 Views
article thumbnail
Javascript Implementation Of Damerau-Levenshtein Distance
From Wikipedia: Damerau–Levenshtein distance is a "distance" (string metric) between two strings, i.e., finite sequence of symbols, given by counting the minimum number of operations needed to transform one string into the other, where an operation is defined as an insertion, deletion, or substitution of a single character, or a transposition of two characters. //based on: http://en.wikibooks.org/wiki/Algorithm_implementation/Strings/Levenshtein_distance //and: http://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance function levenshtein( a, b ) { var i; var j; var cost; var d = new Array(); if ( a.length == 0 ) { return b.length; } if ( b.length == 0 ) { return a.length; } for ( i = 0; i <= a.length; i++ ) { d[ i ] = new Array(); d[ i ][ 0 ] = i; } for ( j = 0; j <= b.length; j++ ) { d[ 0 ][ j ] = j; } for ( i = 1; i <= a.length; i++ ) { for ( j = 1; j <= b.length; j++ ) { if ( a.charAt( i - 1 ) == b.charAt( j - 1 ) ) { cost = 0; } else { cost = 1; } d[ i ][ j ] = Math.min( d[ i - 1 ][ j ] + 1, d[ i ][ j - 1 ] + 1, d[ i - 1 ][ j - 1 ] + cost ); if( i > 1 && j > 1 && a.charAt(i - 1) == b.charAt(j-2) && a.charAt(i-2) == b.charAt(j-1) ){ d[i][j] = Math.min( d[i][j], d[i - 2][j - 2] + cost ) } } } return d[ a.length ][ b.length ]; }
February 22, 2009
by kevin mcbob
· 2,923 Views
article thumbnail
Enabling the Hover-Over-Help Feature Using JSP Custom Tags
This article introduces a presentation-tier JEE framework designed to enable the hover-over-help feature without hard coding JavaScript modules in the JSPs in your web or portal applications. The framework is packaged as a jar file for distribution and includes a server-side RESTful web service component and a set of JSP tag handlers which inserts JavaScript and Cascading Style Sheet (CSS) code into HTML files at the page rendering phrase. The targeted users are java developers who only have preliminary experience on writing JavaScript code. 1. Introduction Mouseover is a standard JavaScript event that is raised when the user hovers the cursor over a particular HTML element on a web page. This event has been widely used in web applications for navigation or causing an image or text to change. Hover-over-help, also called hover-over bubble, refers to the process of showing a small popup window when a mouseover event occurs in the browser. The window contains a message to either help the user to use the application or provide additional explanation and data. It is a useful feature which can make your web pages more intuitive and user-friendly and can carry more information on a single page in rich internet applications (RIAs). There are some tips and samples to enable the feature on the internet. However, they are not effective in developing large-scale web applications. Some of caveats include the following: The message contents are directly hard-coded in JavaScript. They are tied-coupled with graphic user interface components, such as text labels in web pages. The re-usability is low. If the same message needs to appear on several pages, the message and the pertaining JavaScript code have to be copied and pasted to each of these pages. It is hard to maintain these duplications in multiple places. The messages are designed to be static and are typically created when the page is designed. It is difficult to insert runtime data into messages on the fly. Portal applications are not completely supported. The framework introduced in this article is designed to address these issues and provides a comprehensive but easy-to-use solution to java developers who don’t have extensive JavaScript experiences. It leverages the JSP taglibary technology, the Dojo framework, and RESTful web services. Reusability in both web and portal environments, performance, and reliability are the factors that have been taken into consideration. 2. Message bubbles and messages 2.1 Message bubbles A message bubble is a rectangle information window containing a message. Each bubble has one color-coded title compartment and one content compartment. The window is decorated by CSS definitions. The image below illustrates a sample message bubble. Figure 1 – a hover-over-help message bubble Bubbles are not viewable in the webpage until the user moves his/her mouse over action labels or images. In this case, the mouse cursor will turn into a finger-pointing hand and the pertaining bubble window will pop up near the hand. 2.2 Data structure The message contents vary from static to dynamic considerably. However, all messages can share a common data structure, which typically consists of four parts: • Message ID: This is the primary key in each message, which is represented as a unique number. Given any single message ID, the framework is able to identify one and only one message. • Message title: This is a character string displayed in the title compartment in a bubble window. • Message content: This is character string that refers to the detail of a help message. The message content can contain HTML elements. • Color code: This is a character string representing the background color scheme of a bubble window. The value must be one of the following strings: “infor”, “data”, “warning”, or “link”. 2.3 Message types Based on the dynamicity of the contents, we categorized all messages into three types. Each of the types is described in this section, with samples provided. 2.3.1 Static Messages This type of message is considered static because its contents always retain the same. A static message can be the definition of a concept, the explanation of a field, or the instruction of a user action. The same messages will appear to any user on the same page when the page is visited. Valid examples include: • This is a example of a static message. • Click on the button below to submit your request. 2.3.2 Variable Messages These messages contain replaceable variables which are represented by tokens. A token is a non-whitespace character string surrounded by “${” and “}”. The token will be completely replaced by its actual value at the page rendering time on the server. The value is based on user data, and normally will not change for a longer-than-user-session period. From users’ perspectives, the same user will see the same message with his customized values when browsing the same page, but different users may see different messages. The following are examples of valid messages in this category: • Your first name ${firstname}. • Your annual goal is to sell ${yourgoalnumber} cars. 2.3.3 Dynamic Messages The messages in this category are the most dynamic ones and contain variables represented by tokens, as well. However, the actual values are populated based on the data associated to the user session. If it happens that a user session is shared by multiple applications, these values may be changed even without a page refresh. This situation typically occurs in portal applications where multiple portlets can access the same user session. It is possible that the same user will read different messages at a different time on the same page. The examples are – • You have talked to ${anumber} clients today. • It is ${servertimestamp} now. 3. Design and implementation In the design, messages are cached at server-side to gain the maximum performance. Two strategies have been implemented to support the hover-over-help feature for each of the message types described in the previous section. 3.1 Message caching Messages can be persisted in a database or flat text file, but regardless of where they are physically stored, they will be loaded and cached at the web container when it starts. The framework includes a class – CacheIntializer which implements the ServerContextListener interface to perform the job. All you need to develop is your own message loader class to retrieve your messages from storage. Your class must implement the com.myuan.tags.bubblemsg.serverside.MessageLoader interface in the framework, and its name must be added as a ContextParameter in the web descriptor. Please refer to the installation section for more details. 3.2 Dojo and CSS Strategy This strategy is designed to support static and variable messages. It can be referred as the push strategy, since help messages are embedded as html elements and pushed to users’ browsers along with the whole web page. No client-server communication is needed once the page download is done. The values will be kept the same until the page is re-loaded again. This strategy relies on a Dojo module to recognize particular “span” elements (listed in list 1) in html files. For each individual message bubble, two span elements are required. The first one represents a visible graphical component in a page, while the second one is the message bubble which isn’t visible for most of the time. When you move the mouse over the first “span” area, an action will be triggered by the browser, and a Dojo method will make the message bubble popped up (illustrated in Figure 1). The “id” attribute in the first “span” element and the “connectid” attribute in the second one are the keys to tie the pair together. These two attributes must have the same value. The value is randomly generated by the custom tags in the framework, which guarantees that the strategy can support both web and portal applications. Households Household A household includes all the persons who occupy a housing unit. Four tag handlers have been developed to support static message and variable message scenarios, respectively. At page-rendering time, the handlers can retrieve help messages from the cache, replace variable tokens in the messages if any, and create and insert value-matched “span” html elements into the web pages. The complete process is detailed in the sequence diagram in figure 2. Figure 2 – sequence diagram for the Dojo and CSS strategy 3.2.1 hohcsstag This tag inserts CSS definitions in JSP pages at the rendering time. It should be placed in the page head element. In the WebSphere Portal environment, this tag should be added in the Default.jsp file. 3.2.2 hohstatictag hohstatictag is a custom tag built for the static message scenario. It has five attributes: msgid: This is the primary key used to identify a message from the cache. This attribute is mandatory. label: This is the text which will be associated with a mouse-over action to display the message. style: This is the style definition that will be used to decorate the label above. This is the place where the user could overwrite CSS arributes for the label. bubblestyle: This is the place where the user could overwrite CSS attributes for the message bubble. img: If you need to enable the hover-over on an image instead of a text label, you can use this attribute and pass the path to your image. If this attribute appears in the tag, the value in the “label” and the “style” attributes will be ignored. A static message example -- Msg_id Msg_title Msg_content Color_cd 1 Household A household includes all the persons who occupy a housing unit. Infor JSP Fragment where the hohstatictag is used – //text //image 3.2.3 hohvariabletag and hohparamtag hohvariabletag and hohparamtag work together to support variable messages. The “hohvariabletag” has the same four attributes as the “hohstatictag” tag. One “hohparamtag” tag holds one name-value pair. The tokens in the message will be replaced with the values passed in from the hohparamtag when html elements are generated. The following is an example of how to use the custom tag. A variable message example-- Msg_id Msg_title Msg_content Color_cd 2 Recognition My retention rate is ${rate} and my goal is ${goal}. data JSP Fragment where the hohvariabletag and hohparamtag are used together – 3.2.4 hohgenerictag This tag has been created to provide a unified API for both static and variable messages. This tag can replace the “hohstatictag” or “hohvariabletag” without any syntax change. But there will be a minor performance cost if you use this tag on the static-message scenario instead of the hohstatictag. 3.3 AJAX strategy AJAX (Asynchronous JavaScript and XML) is a powerful web development technique in creating interactive web applications. The AJAX strategy was developed for the dynamic message scenario. It is a pull-based model, since messages are retrieved by an AJAX module at runtime from the server. This approach can guarantee that the message contains the latest data. When a mouseover event is triggered from a web page, the AJAX module invokes the server-side RESTful web service endpoint. The server-side component picks up the correct message from the message cache, replaces all variable tokens in it with the most current user-session data, and sends the final message back to your browser in the XML format. Upon receiving the response, the AJAX module populates the pertaining information window and pops it up. The complete process is illustrated in the sequence diagram in figure 3. Figure 3 – Sequence diagram for the AJAX strategy One servlet class and three custom tags have been created to support this strategy, and they must be used together in the same page. 3.3.1 HelpMessagesServiceServlet This is an extension of the HTTPSerlvet class. It serves as the RESTful web service endpoint at the server-side. It processes the HTTP request from the client-side AJAX module, constructs the message with the latest data in user’s session, and sends the response in XML format back to the browser. A sample response is listed below. Household infor Household A household includes all the persons who occupy a housing unit. 3.3.2 hohajaxtag This tag inserts AJAX methods in a JSP which will be invoked by the hohdynatag below. It has two attributes. The namespace can be used to pass a portlet’s namespace into the tag in the portal environment. In a web application the value of the attribute can be left blank. The serviceurl attribute must be the valid URL for the HelpMessagesServiceServlet. In the WebSphere Portal environment, this tag should be added in the Default.jsp file. // Portal environment // Web environment 3.3.3 hohdynatag and hohdynaparamtag The hohdynatag has four attributes as same as the “hohstatictag” tag, and it will generate a “span” element. Each hohdynaparamtag establishes a mapping between one token in the message and one key used in user’s session to refer to the actual value. These mappings will be sent to the RESTful endpoint as HTTP parameters. When a user’s mouse moves over the area in a page represented by the span tag, the following events will occur sequentially at the client’s browser: • OnMouseOver events will be fired by the browser. • The Ajax method generated by the hohajaxtag will be invoked. The method will make an HTTP request to the RESTful service endpoint and will be waiting for the response. • Upon receiving the response, the AJAX method will parse it to get the actual help message. • The AJAX method will display the message bubble in the browser. Below is a valid utilization of these tags. 4. Installation The installation procedure is fairly straight forward. After adding the jar file – MYTagLib.jar -- into the /WEB-INF/lib directory in your web project, you can use the deployment descriptor editor in RAD/RSA to complete the installation: Develop your own message loader class and add a context parameter with the name “BubbleMsgLoaderClass” and the value as the fully qualified name of your class. Figure 4 – adding your class name as a Server Context Parameter Create a listener. The class name is: com.myuan.tags.bubblemsg.serverside.CacheInitializer, and the class is shipped in the TagLib.jar file. Figure 5 – creating the listener with the shipped class Add the servlet class in the framework into the deployment descriptor in your web project. The servlet class is also shipped in the MYTagLib.jar. Figure 6 – creating a servlet with the shipped class Define the taglib element in the /WEB-INF/web.xml file. Figure 7 – adding the tag lib references Define the tag extension in your JSP pages. The and the uri directive must match. The prefix identifies the tags in the tag library within the jsp page. For example: 5 Conclusion The framework packaged in the MYTagLib.jar can enable the hover-over-help feature in a web or portal application without adding any JavaScript/CSS code into JSP pages during the development phrase. This approach can make it simpler to create and maintain these pages. The framework has been tested in web applications running on WebSphere Application Server (WAS) 6.0 and 6.1, as well as in portal applications running on WebSphere Portal 5.1 and 6.0.
October 21, 2008
by Ming Yuan
· 39,559 Views · 1 Like
article thumbnail
Adding SWT Input Validation the Easy Way
Any input provided by a user in a GUI application must typically be validated in one way or another. There is a number of ways this gets done, while some applications have just ignored the matter altogether. When crafting an Eclipse RCP application, there are some help provided by SWT and JFace. We can add ModifyListeners and VerifyListeners to certain SWT widgets. JFace also provides ControlDecorations to help us indicate to the user where a problem with a specific input value exists. The problem is that these are at a low level, and we need to do a lot of "monkey"-coding just to add basic validation and error indication to a widget, and then we're not even touching the world of input masks. If you're like me, you want to concentrate on solving your business problem, and don't want to write lots of basic UI code over and over. This is where the RCP Toolbox is very useful. It provides a light-weight validation framework (among other features) that makes it much easier to add validation and input masks to SWT Text, Combo and CCombo widgets. The goal Let us have a look at how to define a basic wizard for creating a new Booking. This wizard must capture the following fields from the user: Name: the name of the booking, typically the name of the person making the booking. May not be empty, and preferably not less than three characters. Date: the date and time of the booking. Must be any time from the current time to the end of the year. Number of Persons: the number of persons to book for. Must be a number from 1 to 10. Telephone Number: the telephone number of the person making the booking. Must be in the form +(country code) (area code) number, e.g. +44 (33) 555-1111. And of course we want indicators next to each field when an error or warning condition exists in the field, as well as a message being written to the WizardDialog's message area. For fun we want the user to be able to get a quick-fix option on the date field for setting it to the current time. The Validation framework The RCP Toolbox provides a number of custom widgets and a easy to use validation framework. Adding validation starts with the ValidationToolkit class. This class gets instantiated to work with a specific type of contents, and is then used to create ValidatingField instances that can handle that type of contents. The rest of the framework deals with interfaces and default implementations to facilitate the validation of contents, definition of input masks, provision of quick-fixes, error-handling and conversion of the input text to specific class types. The WizardPage and the ValidationToolkits We start by first defining our BookingWizardPage class and instantiating the necessary ValidationToolkit instances. //Not all imports are shown import com.richclientgui.toolbox.validation.IFieldErrorMessageHandler; import com.richclientgui.toolbox.validation.ValidationToolkit; import com.richclientgui.toolbox.validation.converter.DateStringConverter; import com.richclientgui.toolbox.validation.converter.IntegerStringConverter; import com.richclientgui.toolbox.validation.string.StringValidationToolkit; public class BookingWizardPage extends WizardPage { private static final int DECORATOR_POSITION = SWT.TOP | SWT.LEFT; private static final int DECORATOR_MARGIN_WIDTH = 1; private static final int DEFAULT_WIDTH_HINT = 150; private StringValidationToolkit strValToolkit = null; private ValidationToolkit dateValToolkit = null; private ValidationToolkit intValToolkit = null; private final IFieldErrorMessageHandler errorMessageHandler; public BookingWizardPage() { super("booking.pageone","New Booking Entry", null); errorMessageHandler = new WizardPageErrorHandler(); } public void createControl(Composite parent) { final Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(new GridLayout(2, false)); strValToolkit = new StringValidationToolkit(DECORATOR_POSITION, DECORATOR_MARGIN_WIDTH, true); strValToolkit.setDefaultErrorMessageHandler(errorMessageHandler); intValToolkit = new ValidationToolkit(new IntegerStringConverter(), DECORATOR_POSITION, DECORATOR_MARGIN_WIDTH, true); intValToolkit.setDefaultErrorMessageHandler(errorMessageHandler); dateValToolkit = new ValidationToolkit(new DateStringConverter(), DECORATOR_POSITION, DECORATOR_MARGIN_WIDTH, true); dateValToolkit.setDefaultErrorMessageHandler(errorMessageHandler); //TODO: create ValidatingFields setControl(composite); } } The StringValidationToolkit class we instantiate in line 28 is a ValidationToolkit that deals specifically with ValidatingFields that have normal String contents. In line 32 we instantiate a typed instance of ValidationToolkit that will create ValidatingFields that only takes Integers as input. We must provide a way that the contents of the fields are converted from a String to the correct content type. This is done with a set of coverter classes provided by the framework. In lines 32 and 36 we specify a IntegerStringConverter and a DateStringConverter to convert Integer and java.util.Date values respectively. The framework makes use of the JFace org.eclipse.jface.fieldassist.ControlDecoration and related classes to indicate whether a field has an error or warning condition, whether it is a required field, and if there is a quick-fix available (by right-clicking on the decorator icon) for the current error or warning condition. The position of these decorator icons relative to the input widgets as well as the margin width between the decorator icon and the widget can be specified when constructing a ValidationToolkit. All the fields created by this ValidationToolkit instance will use the same settings for there decorator icons. In lines 9 - 10 we have defined some constants for the decorator position and margins, and we use this for constructing all the ValidationToolkit instances. Handling the error messages We also make use of an IFieldErrorMessageHandler to get feedback from the validation process. The validation framework will call these error handlers when error or warning conditions occur, and allow us to do something with those messages. By default these messages are only displayed in the tooltips of the decorator icons. A default error handler can be specified for each toolkit instance, or a separate handler can be set for each ValidatingField if so required. The inner class WizardPageErrorHandler implements the IFieldErrorMessageHandler interface and basically just set the messages on the WizardPage's message area. //inner class of BookingWizardPage class WizardPageErrorHandler implements IFieldErrorMessageHandler { public void handleErrorMessage(String message, String input) { setMessage(null, DialogPage.WARNING); setErrorMessage(message); } public void handleWarningMessage(String message, String input) { setErrorMessage(null); setMessage(message, DialogPage.WARNING); } public void clearMessage() { setErrorMessage(null); setMessage(null, DialogPage.WARNING); } } The actual error or warning messages are generated by the various IFieldValidator implementations (we'll get to those), and can easily be customized by implementing custom validators. Creating a simple ValidatingField Of course just having some toolkit instances does not help us much. We need actual input widgets that are being validated. The first step is to update the createControl method. public void createControl(Composite parent) { final Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(new GridLayout(2, false)); strValToolkit = new StringValidationToolkit(DECORATOR_POSITION, DECORATOR_MARGIN_WIDTH, true); strValToolkit.setDefaultErrorMessageHandler(errorMessageHandler); intValToolkit = new ValidationToolkit(new IntegerStringConverter(), DECORATOR_POSITION, DECORATOR_MARGIN_WIDTH, true); intValToolkit.setDefaultErrorMessageHandler(errorMessageHandler); dateValToolkit = new ValidationToolkit(new DateStringConverter(), DECORATOR_POSITION, DECORATOR_MARGIN_WIDTH, true); dateValToolkit.setDefaultErrorMessageHandler(errorMessageHandler); createNameField(composite); createDateField(composite); createNumberPersonsField(composite); createTelephoneNumberField(composite); setControl(composite); } Then we can look at creating our first validated input field that makes use of a SWT Text widget to capture the name of the person doing the booking. private void createNameField(Composite composite) { new Label(composite, SWT.NONE).setText("Booking Name:"); final ValidatingField nameField = strValToolkit.createTextField( composite, new IFieldValidator(){ public String getErrorMessage() { return "Name may not be empty."; } public String getWarningMessage() { return "That's a very short name..."; } public boolean isValid(String contents) { return !(contents.length()==0); } public boolean warningExist(String contents) { return contents.length() < 3; } }, true, ""); GridData gd = new GridData(SWT.LEFT, SWT.CENTER, false, false); gd.widthHint = DEFAULT_WIDTH_HINT; nameField.getControl().setLayoutData(gd); } Since this field works with String contents, we make use of the strValToolkit instance to create the field in line 4 above. We specify the parent composite that the input widget must be added to, the IFieldValidator that will be used to validate the field contents, whether this is a required field or not (thus whether the required decorator icon must be shown or not) and an initial empty string value for the field. Note that this call will also create a Text widget to be used for the field, but the API allows that you can create your own Text, Combo or CCombo instance and pass that to the toolkit to use when creating a new ValidatingField. An anonymous inner class implementation of IFieldValidator is specified in lines 5 - 23. We're doing some very basic validation checks in this example, but it is easy to implement validators that makes use of other heavy-weight business validation frameworks. Our validator will indicate an error condition if the contents of the field is empty (line 16), in which case the error message "Name may not be empty." will be displayed (line 8). This validator will also indicate a warning condition if the name field contains less than 3 characters (line 20) with the message "That's a very short name..." (line 12). In lines 24 - 26 we set the layout of the input widget on the composite. Dating an input mask Dates have always been a difficult input type to deal with. The easiest way for us developers to deal with them are to use widgets that pop up a calendar from where the user can choose a day, and possibly a time as well. However, this way of dealing with dates are not always a favourite with touch-typing end-users. They prefer some masked field where they only need to fill in the bits of the date that matter. Using the mouse should be restricted as far as possible. Luckily the RCP Toolbox provides a way of specifying input masks, as well as specific implementations of validators and converters for dealing with Dates. We want to create a field that only takes dates as input, where the date entered must be of the form yyyy-MM-dd HH:mm (e.g 2008-07-19 21:00), and fall in the date range starting at the current time and ending at the end of the year 2008. private void createDateField(Composite composite) { new Label(composite, SWT.NONE).setText("Booking Time:"); final Date endYear = getEndYearDate(); //we create a Date field that takes input of form yyyy-MM-dd HH:mm //and only allows values from now till the end of the year final ValidatingField rangedDateField = dateValToolkit.createTextField(composite, new RangedDateFieldValidator( DateFieldValidator.DATE_TIME_HHMM_DASH, dateValToolkit.getStringConverter(), new Date(), endYear), true, new Date()); GridData gd = new GridData(SWT.LEFT, SWT.CENTER, false, false); gd.widthHint = DEFAULT_WIDTH_HINT; rangedDateField.getControl().setLayoutData(gd); } Here we used the dateValToolkit instance to create the field as the contents of the field must be a java.util.Date. We specify an instance of RangedDateFieldValidator (provided by the framework) that makes use of the specified Date input mask pattern DateFieldValidator.DATE_TIME_HHMM_DASH (line 9) to validate the contents of the field. Other patterns are available, or custom ones can also be defined. The DateStringConverter specified when dateValToolkit was constructed will be used to convert from Dates to Strings and vice versa. In line 11 we specify the valid date range for the field, from the current date and time to the end of the year, and in line 13 we set the initial value of the field to the current time. Providing quick-fixes I found that developers using Eclipse RCP to develop their applications like to make the experience for the end-user as good as possible. So we should not stop at just validating input; we must also try and help them quickly fix mistakes, where possible. In this example we can do that by specifying a IQuickFixProvider. //add at end of createDateField(..) method //we add a quickfix that will set it to the current date rangedDateField.setQuickFixProvider(new IQuickFixProvider(){ public boolean doQuickFix(ValidatingField field) { field.setContents(new Date()); return true; } public String getQuickFixMenuText() { return "Set to current time"; } public boolean hasQuickFix(Date contents) { //would typically first check contents to determine if quickfix //is possible return true; } }); The above is a very simple quick-fixer. It always says it has a quick-fix available (line 17), where a more complex provider will first check the contents of the field to determine if there is a quick-fix. When the user performs the quick-fix, it just sets the contents of the field to the current date and time (line 6). When the validation framework detects there is an error condition on a field, it will see if there is a IQuickFixProvider available with a quick-fix. If this is the case, it will add the quick-fix option to a context-menu on the decorator icon with the text specified in line 11. All the user then needs to do is right-click on the decorator icon and select the quick-fix to perform. Validating Combos A Combo widget would be just the thing to use for capturing the number of persons for the booking. Our requirements say we must limit the number to a maximum of 10 people (and a minimum of 1 goes without saying). Once again we do not want to force the user to use numerous mouse-clicks or keystrokes to select the number, so we do not make the Combo read-only, and rather decide to add validation to it. private void createNumberPersonsField(Composite composite) { new Label(composite, SWT.NONE).setText("Number of persons:"); final ValidatingField numberPersonsField = intValToolkit.createComboField( composite, new StrictRangedNumberFieldValidator(1, 10){ public String getErrorMessage() { return "Bookings for groups bigger than 10 not allowed"; } public String getWarningMessage() { return null; } public boolean warningExist(Integer contents) { return false; } }, true, 2, new Integer[]{1,2,3,4,5,6,7,8,9,10}); GridData gd = new GridData(SWT.LEFT, SWT.CENTER, false, false); gd.widthHint = DEFAULT_WIDTH_HINT; numberPersonsField.getControl().setLayoutData(gd); } Here we make use of the intValToolkit.createComboField method to create a field containing a Combo widget with contents of type Integer. We specify a StrictRangedNumberFieldValidator (line 6) to ensure that the entered value only consists of digits and falls in the range 1 to 10. No warning conditions are checked. In line 22 we populate the Combo with a list of Integers from 1 to 10, and in line 21 we select a default value of 2. As easy as counting to 5. And don't forget the telephone number Freeform telephone number fields are used a lot, but unfortunately end-users can easily make mistakes in such fields. We want to force our user to input the telephone number in a specific form, thus at least preventing the cases where digits are missed. To do this, we make use of the framework's TelephoneNumberValidator. This validator allows telephone number to be entered in either international format (e.g. +44 (55) 555-5555) or in domestic format (e.g. (055) 555-5555). private void createTelephoneNumberField(Composite composite) { new Label(composite, SWT.NONE).setText("Contact Telephone Nr:"); final ValidatingField telephoneField = strValToolkit.createTextField( composite, new TelephoneNumberValidator(true), true, "+44 (55) 555-4321"); GridData gd = new GridData(SWT.LEFT, SWT.CENTER, false, false); gd.widthHint = DEFAULT_WIDTH_HINT; telephoneField.getControl().setLayoutData(gd); } We are using strValToolkit to create the field, since the contents will be managed as a String. Then we specify a TelephoneNumberValidator as the validator in line 7, with the true parameter indicating that we want to use the international format. In line 9 we provide an initial value. Conclusion This article describes a very simple example of how to add validation to SWT widgets using the RCP Toolbox. In a real-world application the actual business validations to be done might be more complex, but if this validation framework is used, the UI code related to validation would remain as simple as the code in these examples. This validation framework really made our development much easier, allowing us to concentrate on the business code. It is very easy to extend the framework with custom validators, converters, quick-fix providers and error handlers that ties into existing business code or other validation code, rules engines etc. Note that the examples in this article need Eclipse RCP 3.3 or 3.4 as well as RCP Toolbox v1.0.1, created and distributed by www.richclientgui.com.
July 21, 2008
by Herman Lintvelt
· 48,846 Views
article thumbnail
ASP.NET - Query Strings - Client Side State Management
Continuing the tour in the ASP.NET client side state management our current stop is the query string technique. You can read my previous posts in the state management subject in the following links: Client side state management introduction ViewState technique Hidden fields technique What are Query Strings? Query strings are data that is appended to the end of a page URL. They are commonly used to hold data like page numbers or search terms or other data that isn't confidential. Unlike ViewState and hidden fields, the user can see the values which the query string holds without using special operations like View Source. An example of a query string can look like http://www.srl.co.il?a=1;b=2. Query strings are included in bookmarks and in URLs that you pass in an e-mail. They are the only way to save a page state when copying and pasting a URL. The Query String Structure As written earlier, query strings are appended to the end of a URL. First a question mark is appended to the URL's end and then every parameter that we want to hold in the query string. The parameters declare the parameter name followed by = symbol which followed by the data to hold. Every parameter is separated with the ampersand symbol. You should always use the HttpUtility.UrlEncode method on the data itself before appending it. Query String Limitations You can use query string technique when passing from one page to another but that is all. If the first page need to pass non secure data to the other page it can build a URL with a query string and then redirect. You should always keep in mind that a query string isn't secure and therefore always validate the data you received. There are a few browser limitation when using query strings. For example, there are browsers that impose a length limitation on the query string. Another limitation is that query strings are passed only in HTTP GET command. How To Use Query Strings When you need to use a query string data you do it in the following way: string queryStringData = Request.QueryString["data"]; In the example I extract a data query string. The structure of the URL can look like url?data=somthing. After getting to data parameter value you should validate it in order not to enable security breaches. The next example is a code to help inject a query string into a URL: public string BuildQueryString(string url, NameValueCollection parameters){ StringBuilder sb = new StringBuilder(url); sb.Append("?"); IEnumerator enumerator = parameters.GetEnumerator(); while (enumerator.MoveNext()) { // get the current query parameter string key = enumerator.Current.ToString(); // insert the parameter into the url sb.Append(string.Format("{0}={1}&", key, HttpUtility.UrlEncode(parameters[key]))); } // remove the last ampersand sb.Remove(sb.Length - 1, 1); return sb.ToString(); } Summary To sum up the post, query string is another ASP.NET client side state management technique. It is most helpful for page number state or search terms. The technique isn't secured so avoid using it with confidential data. In the next post in this series I'll explain the how to use cookies.
July 20, 2008
by Gil Fink
· 77,633 Views
article thumbnail
Javascript Count Line Breaks
Returns the number of line breaks in a string. function lineBreakCount(str){ /* counts \n */ try { return((str.match(/[^\n]*\n[^\n]*/gi).length)); } catch(e) { return 0; } }
July 15, 2008
by Snippets Manager
· 12,321 Views
article thumbnail
GWT Basic Project Structure And Components
[img_assist|nid=3421|title=|desc=|link=url|url=http://www.manning.com/affiliate/idevaffiliate.php?id|align=left|width=208|height=388]The core of every GWT project is the project layout and the basic components required—host pages, entry points, and modules. To begin a GWT project, you need to create the default layout and generate the initial files. The easiest way to do this is to use the provided ApplicationCreator tool. Generating a project ApplicationCreator is provided by GWT to create the default starting points and layout for a GWT project. ApplicationCreator, like the GWT shell, supports several command-line parameters, which are listed in table 1. ApplicationCreator [-eclipse projectName] [-out dir] [-overwrite] [-ignore] className Table 1 ApplicationCreator command-line parameters Parameter Description -eclipse Creates a debug launch configuration for the named eclipse project -out The directory to which output files will be written (defaults to the current directory) -overwrite Overwrites any existing files -ignore Ignores any existing files; does not overwrite className The fully qualified name of the application class to be created To stub out an example calculator project, we’ll use ApplicationCreator based on a relative GWT_HOME path, and a className of com.manning.gwtip.calculator.client.Calculator, as follows: mkdir [PROJECT_HOME] cd [PROJECT_HOME] [GWT_HOME]/applicationCreator com.manning.gwtip.calculator.client.Calculator GWT_HOME It is recommended that you establish GWT_HOME as an environment variable referring to the filesystem location where you have unpacked GWT. Additionally, you may want to add GWT_HOME to your PATH for further convenience. We use GWT_HOME when referencing the location where GWT is installed and PROJECT_HOME to refer to the location of the current project. PATH SEPARATORS For convenience, when referring to filesystem paths, we'll use forward slashes, which work for two-thirds of supported GWT platforms. If you are using Windows, please adjust the path separators to use backward slashes. Running ApplicationCreator as described creates the default src directory structure and the starting-point GWT file resources. The standard directory structure Even though it's quite simple, the GWT layout is very important because the toolkit can operate in keeping with a Convention over Configuration design approach. As we’ll see, several parts of the GWT compilation process make assumptions about the default layout. Because of this, not everything has to be explicitly defined in every instance (which cuts down on the amount of configuration required). Taking a look at the output of the ApplicationCreator script execution, you will see a specific structure and related contents, as shown in listing 1. This represents the default configuration for a GWT project. Listing 1 ApplicationCreator output, showing the default GWT project structure: src src/com src/com/manning src/com/manning/gwtip src/com/manning/gwtip/calculator src/com/manning/gwtip/calculator/Calculator.gwt.xml src/com/manning/gwtip/calculator/client src/com/manning/gwtip/calculator/client/Calculator.java src/com/manning/gwtip/calculator/public src/com/manning/gwtip/calculator/public/Calculator.html Calculator-shell.sh Calculator-compile.sh The package name, com.manning.gwtip.calculator, is represented in the structure as a series of subdirectories in the src tree. This is the standard Java convention, and there are notably separate client and public subdirectories within. The client directory is intended for resources that will be compiled into JavaScript . Client items are translatable, or serializable, and will ultimately be downloaded to a client browser—these are Java resources in the source. The client package is known in GWT terminology as the source path. The public directory denotes files that will also be distributed to the client, but that do not require compilation and translation to JavaScript . This typically includes CSS, images, static HTML, and any other such assets that should not be translated, including existing JavaScript. The public package is known as the public path. Note that our client-side example does not use any server resources, but GWT does include the concept of a server path/package for server-side resources. Figure 1 illustrates this default GWT project layout. [img_assist|nid=4037|title=|desc=|link=none|align=none|width=293|height=284] ApplicationCreator generates the structure and a required set of minimal files for a GWT project. The generated files include the XML configuration module definition, the entry point Java class, and the HTML host page. These are some of the basic GWT project concepts. Along with the module definition, entry point, and host page, some shortcut scripts have also been created for use with the GWTShell and GWTCompiler tools. These scripts run the shell and compiler for the project. Table 2 lists all of the files created by ApplicationCreator: the basic resources and shortcut scripts needed for a GWT project. Table 2 ApplicationCreator-generated initial project files that serve as a starting point for GWT applications File Name Purpose GWT module file ProjectName.gwt.xml Defines the project configuration Entry point class ProjectName.java Starting class invoked by the module Host page ProjectName.html Initial HTML page that loads the module GWTShell shortcut invoker script ProjectName-shell.sh Invokes GWTShell for the project GWTCompiler shortcut invoker script ProjectName-compile.sh Invokes GWTCompiler for the project The starting points ApplicationCreator provides essentially wire up all the moving parts for you and stub out your project. You take it from there and modify these generated files to begin building a GWT application. If the toolkit did not provide these files via ApplicationCreator, getting a project started, at least initially, would be much more time consuming and confusing. Once you are experienced in the GWT ways, you may wind up using other tools to kick off a project: an IDE plugin, a Maven “archetype,” or your own scripts. ApplicationCreator, though, is the helpful default. The contents and structure that ApplicationCreator provides are themselves a working GWT “hello world” example. You get “hello world” for free, out of the box. "Hello world", however, is not that interesting. The connection of all the moving parts is what is really important; how a host page includes a module, how a module describes project resources, and how an entry point invokes project code. These concepts are applicable to all levels of GWT projects—the basic ones and beyond. Understanding these parts is key to gaining an overall understanding of GWT. Next, we’ll take a closer look at each of these concepts, beginning with the host page. Host pages A host page is the initial HTML page that invokes a GWT application. A host page contains a script tag that references a special GWT JavaScript file, Module.nocache.js. This JavaScript file, which the toolkit provides when you compile your project, kicks off the GWT application loading process. Along with the script reference that loads the project resources, you can also specify several GWT-related tags in the host page. These tag options are not present in the default host page created by ApplicationCreator, but it’s still important to be aware of them. The GWT tags that are supported in a host page are listed in table 3, as a reference. Table 3 GWT tags supported in host pages Meta tag Syntax Purpose gwt:module (Legacy, pre GWT 1.4.) Specifies the module to be loaded gwt:property Statically defines a deferred binding client property gwt:onPropertyErrorFn Specifies the name of a function to call if a client property is set to an invalid value (meaning that no matching compilation will be found) gwt:onLoadErrorFn Specifies the name of a function to call if an exception happens during bootstrapping or if a module throws an exception out of onModuleLoad(); the function should take a message parameter Thus, a host page includes a script reference that gets the GWT process started and refers to all the required project resources. The required resources for a project are assembled by the GWT compilation process, and are based on the module configuration. Modules GWT applications inhabit a challenging environment. This is partly because of the scope of responsibility GWT has elected to take on and partly because of the Internet landscape. Being a rich Internet-based platform and using only the basic built-in browser support for HTML, CSS, and JavaScript makes GWT quite elegant and impressive, but this combination is tough to achieve. Browsers that are “guided” by standards, but that don’t always stick to them, add to the pressure. Couple that environment with an approach that aims to bring static types, code standards, profiling and debugging, inheritance, and reuse to the web tier, and you have a tall order. To help with this large task, GWT uses modules as configuration and execution units that handle discreet areas of responsibility. Modules enable the GWT compiler to optimize the Java code it gets fed, create variants for all possible situations from a single code base, and make inheritance and property support possible. One of the most important resources generated by the ApplicationCreator is the Module.gwt.xml module descriptor for your project. This file exists in the top-level directory of your project’s package and provides a means to define resource locations and structure. In a default generated module file, there are only two elements: and . An element simply includes the configuration for another named GWT module in the current definition, and defines a class that kicks things off and moves from configuration to code. Table 4 provides an overview of the most common GWT module descriptor elements. Table 4 A summary of the most common elements supported by the GWT module descriptor Module element Description Identifies additional GWT modules that should be inherited into the current module Specifies which EntryPoint class should be invoked when starting a GWT project Identifies where the source code that should be translated into JavaScript by the GWT compiler is located Identifies where assets that are not translatable source code, such as images and CSS files, are located
July 14, 2008
by Schalk Neethling
· 32,320 Views
article thumbnail
Understanding the GWT Compiler
[img_assist|nid=3421|title=|desc=|link=url|url=http://www.manning.com/affiliate/idevaffiliate.php?id|align=left|width=208|height=388]The GWT compiler is the fulcrum of GWT. The entire approach GWT takes, encapsulating browser differences and compiling JavaScript from Java, is made possible by the design and architecture of the compiler. The GWT compiler compiles Java into JavaScript, but it’s important to understand that the compiler doesn’t compile Java the same way javac does. The GWT compiler is really a Java source to JavaScript source translator. The GWT compiler needs hints about the work that it must perform partly because it operates from source. These hints come in the form of the module descriptor, the marker interfaces that denote serializable types, the JavaDoc style annotations used in serializable types for collections, and more. Although these hints may sometimes seem like overkill, they’re needed because the GWT compiler will optimize your application at compile time. This doesn’t just mean compressing the JavaScript naming to the shortest possible form; it also includes pruning unused classes, and even methods and attributes, from your code. The core engineering goal of the GWT compiler is summarized succinctly: you pay for what you use. This optimization offers big advantages over other Ajax/JavaScript libraries, where a large initial download of a library may be needed even if just a few elements are used. In Java, serialization marked by the java.io.Serializable interface is handled at the bytecode level. GWT examines your code and only provides serialization for the classes where you explicitly need it. Like GWTShell, GWTCompiler supports a set of useful command-line options. They’re described in table 1: GWTCompiler [-logLevel level] [-gen dir] [-out dir] [-treeLogger] [-style style] module Table 1 GWTCompiler parameters Option Description -logLevel The logging level: ERROR, WARN, INFO, TRACE, DEBUG, SPAM, or ALL -gen The directory into which generated files will be written for review -out The directory to which output files will be written (defaults to the current directory) -treeLogger Logs output in a graphical tree view -style The script output style: OBF[uscated], PRETTY, or DETAILED(defaults to OBF) module The name of the module to compile The -gen and -out command-line options specify where generated files and the final output directory are to be, respectively. And -logLevel, as in the case of GWTShell, is used to indicate the level of logging performed during the compilation. You can even use the -treeLogger option to bring up a window to view the hierarchical logging information you would see in the shell’s console display. The GWT compiler supports several styles of output, each of use in looking at how your code is executing in the browser. JavaScript output style When working with the GWT compiler, you can use several values with the -style command-line option to control what the generated JavaScript looks like. These options are as follows: OBF - Obfuscated mode. This is a non-human-readable, compressed version suitable for production use. PRETTY - Pretty-printed JavaScript with meaningful names. DETAILED - Pretty-printed JavaScript with fully qualified names. To give you an idea of what these options mean, let’s look at examples of java.lang.StringBuffer compiled in the three different modes. First, in listing 1, is the obfuscated mode. Listing 1 StringBuffer in obfuscated compilation function A0(){this.B0();return this.js[0];}function C0(){if(this.js.length > 1){this.js = [this.js.join('')];this.length = this.js[0].length;}function D0(E0){this.js = [E0];this.length = E0.length;}function Ez(F0,a1){return F0.yx(yZ(a1));}function yB(b1){c1(b1);return b1;}function c1(d1){d1.e1('');}function zB(){}_ = zB.prototype = new f();_.yx = w0;_.vB = A0;_.B0 = C0;_.e1 = D0;_.i = 'java.lang.StringBuffer';_.j = 75;function f1(){f1 = a;g1 = new iX();h1 = new iX();return window;} Obfuscated mode is just that. This is intended to be the final compiled version of your application, which has names compressed and whitespace cleaned. Next is the pretty mode, shown in listing 2. Listing 2 StringBuffer in pretty compilation function _append2(_toAppend){ var _last = this.js.length - 1; var _lastLength = this.js[_last].length; if (this.length > _lastLength * _lastLength) { this.js[_last] = this.js[_last] + _toAppend; } else { this.js.push(_toAppend); } this.length += _toAppend.length; return this;}function _toString0(){ this._normalize(); return this.js[0];}// Some stuff omitted.function _$StringBuffer(_this$static){ _$assign(_this$static); return _this$static;}function _$assign(_this$static){ _this$static._assign0('');}function _StringBuffer(){}_ = _StringBuffer.prototype = new _Object();_._append = _append2;_._toString = _toString0;_._normalize = _normalize0;_._assign0 = _assign;_._typeName = 'java.lang.StringBuffer'; _._typeId = 75; append() becomes _append2() to avoid collision> toString() becomes _toString0()> _typeName holds name of original Java class> Pretty mode is useful for debugging as method names are somewhat preserved. However, collisions are resolved with suffixes, as the _toString0() method name shows . Last, we have the detailed mode, as displayed in listing 3. Listing 3 StringBuffer in detailed compilation function java_lang_StringBuffer_append__Ljava_lang _String_2(toAppend){ var last = this.js.length - 1; var lastLength = this.js[last].length; if (this.length > lastLength * lastLength) { this.js[last] = this.js[last] + toAppend; } else { this.js.push(toAppend); } this.length += toAppend.length; return this;}function java_lang_StringBuffer_toString__(){ this.normalize__(); return this.js[0];}function java_lang_StringBuffer_normalize__(){ if (this.js.length > 1) { this.js = [this.js.join('')]; this.length = this.js[0].length; }// . . . some stuff omittedfunction java_lang_StringBuffer(){}_ = java_lang_StringBuffer.prototype = new java_lang_Object();_.append__Ljava_lang_String_2 = java_lang_StringBuffer_append__Ljava_lang_String_2;_.toString__ = java_lang_StringBuffer_toString__;_.normalize__ = java_lang_StringBuffer_normalize__;_.assign__Ljava_lang_String_2 = java_lang_StringBuffer_assign__Ljava_lang_String_2;_.java_lang_Object_typeName = 'java.lang.StringBuffer';_.java_lang_Object_typeId = 75; Detailed mode preserves the full class name, as well as the method name #2. For overloaded methods, the signature of the method is encoded into the name, as in the case of the append() method #1. There are some important concepts to grasp about this compilation structure, especially given the way GWT interacts with native JavaScript, through the JavaScript Native Interface (JSNI), which will be discussed in the section on the compiler lifecycle. The names of your classes and methods in their JavaScript form aren’t guaranteed, even for different compilations of the same application. Use of the special syntax provided with JSNI will let you invoke known JavaScript objects from your Java code and invoke your compiled Java classes from within JavaScript; but you can’t freely invoke your JavaScript when using obfuscated style, predictably. This imposes certain limitations on your development: If you intend to expose your JavaScript API for external use, you need to create the references for calls into GWT code using JSNI registrations. You can’t rely on JavaScript naming in an object hash to give you java.lang.reflect.* type functionality, since the naming of methods isn’t reliable. Although they’re rare, you should consider potential conflicts with other JavaScript libraries you’re including in your page, especially if you’re publishing using the PRETTY setting. In addition to being aware of the available compiler output options and how they affect your application, you should also be familiar with a few other compiler nuances. Additional compiler nuances Currently, the compiler is limited to J2SE 1.4 syntactical structures. This means that exposing generics or annotations in your GWT projects can cause problems. Other options are available for many of the purposes for which you might wish to use annotations. For example, you can often use JavaDoc-style annotations, to which GWT provides its own extensions. Along with the J2SE 1.4 limitations, you also need to keep in mind the limited subset of Java classes that are supported in the GWT Java Runtime Environment (JRE) emulation library. This library is growing, and there are third-party extensions, but you need to be aware of the constructs you can use in client-side GWT code—the complete JRE you’re accustomed to isn’t available. Of course, one of the great advantages of GWT’s approach to compiling JavaScript from plain Java is that you get to leverage your existing toolbox while building your Ajax application. When you execute the hosted mode browser, you’re running regularly compiled Java classes. This, again, means you can use all the standard Java tooling—static analysis tools, debuggers, IDEs, and the like. These tools are useful for writing any code, but they become even more important in the GWT world because cleaning up your code means less transfer time to high-latency clients. Also, because JavaScript is a fairly slow execution environment, such cleanup can have a large impact on ultimate performance. The GWT compiler helps by optimizing the JavaScript it emits to include only classes and methods that are on the execution stack of your module and by using native browser functions where possible, but you should always keep the nature of JavaScript in mind. To that end, we’ll now take a closer look at the lifecycle of a GWT compilation and at how this JavaScript is generated. The compiler lifecycle When the GWT compiler runs, it goes through several stages for building the final compiled project. In these stages, the need for the GWT module definition file becomes clear. First, the compiler identifies which combinations of files need to be built. Then, it generates any client-side code using the generator metaprogramming model. Last, it produces the final output. We’ll look at each of these steps in the compiler lifecycle in more detail. dentifying build combinations One of the great strengths of GWT is that it builds specific versions of the application, each exactly targeted to what the client needs (user agent, locale, so on). This keeps the final download, and the operational overhead at the client level, very lean. A particular build combination is defined in the GWT module definition using a tag. This establishes a base set of values that are used for the build. The first and very obvious property is the user agent that the JavaScript will be built for. Listing 4 shows the core GWT UserAgent module definition. Listing 4 The GWT UserAgent definition = 6000) { return "ie6"; } } } else if (ua.indexOf("gecko") != -1) { var result = /rv:([0-9]+)\.([0-9]+)/.exec(ua); if (result && result.length == 3) { if (makeVersion(result) >= 1008) return "gecko1_8"; } return "gecko"; } return "unknown";]]> Here the tag establishes a number of different builds that the final compiler will output #1: in this case, ie6, gecko, gecko1_8, safari, and opera. This means each of these will be processed as a build of the final JavaScript that GWT emits. GWT can then switch implementations of classes based on properties using the tag in the module definition. As the GWT application starts up, the JavaScript snippet contained within the tag determines which of these implementations is used #2. This snippet is built into the startup script that determines which compiled artifact is loaded by the client. At the core of the GWT UI classes is the DOM class. This gets replaced based on the user.agent property. Listing 5 shows this definition. Listing 5 Changing the DOM implementation by UserAgent Now you can see the usefulness of this system. The basic DOM class is implemented with the same interface for each of the browsers, providing a core set of operations on which cross-platform code can easily be written. Classes replaced in this method can’t be instantiated with simple constructors but must be created using the GWT.create() method. In practice, the DOM object is a singleton exposing static methods that are called by applications, so this GWT.create() invocation is still invisible. This is an important point to remember if you want to provide alternative implementations based on compile-time settings in your application. You can also define your own properties and property providers for switching implementations. We have found that doing this for different runtime settings can be useful. For example, we have defined debug, test, and production settings, and replacing some functionality in the application based on this property can help smooth development in certain cases. This technique of identifying build combinations and then spinning off into specific implementations during the compile process is known in GWT terms as deferred binding. The GWT documentation sums this approach up as “the Google Web Toolkit answer to Java reflection.” Dynamic loading of classes (dynamic binding) isn’t truly available in a JavaScript environment, so GWT provides another way. For example, obj.getClass().getName() isn’t available, but GWT.getTypeName(obj) is. The same is true for Class.forName("MyClass"), which has GWT.create(MyClass) as a counterpart. By using deferred binding, the GWT compiler can figure out every possible variation, or axis, for every type and feature needed at compile time. Then, at runtime, the correct permutation for the context in use can be downloaded and run. Remember, though, that each axis you add becomes a combinatory compile. If you use 4 languages and 4 browser versions, you must compile 16 final versions of the application; and if you use several runtime settings, you end up with many more combinations in the mix. This concept is depicted in figure 1. Figure 1 Multiple versions of a GWT application are created by the compiler for each axis or variant application property, such as user agent and locale. Compiling a new monolithic version of your application for each axis doesn’t affect your end users negatively. Rather, this technique allows each user to download only the exact application version they need, without taking any unused portion along for the ride. This is beneficial for users, but it slows compile time considerably, and it can be a painful point for developers. Reducing the compile variants to speed up compile time Even though GWT compile time can be long, keep in mind that the end result for users is well optimized. Also, the GWT module system allows you to tweak the compile time variants for the situation. During day-to-day development, you may want to use the tag in your module definition to confine the compile to a single language, or single browser version, to speed up the compile step. Another important use for module properties is in code generation, which is the next step of the compilation process. Generating code GWT’s compiler includes a code generation or metaprogramming facility that allows you to generate code based on module properties at compile time. Perhaps the best example is the internationalization support. The i18n module defines several no-method interfaces that you extend to define Constants, Messages (which include in-text replacement), or Dictionary classes (which extract values from the HTML host page). The implementations of each of these classes are built at compile time using the code generation facility, producing a lean, custom version of your application in each language. The i18n module does this through the tag, which lets you add additional iterative values to a property in a module. Listing 6 demonstrates the use of this concept to add French and Italian support to a GWT application. Listing 6 Defining French and Italian using extend-property When an application inherits the i18n module, the GWT compiler searches for interfaces that extend one of the i18n classes and generates an implementation for the class based on a resource bundle matching the language code. This is accomplished via the tag in the i18n module definition. Listing 7 shows this along with the tag, which is used for establishing which language will be needed at runtime. Listing 7 The i18n module’s locale property declarations = 0) { var language = args.substring(startLang); var begin = language.indexOf("=") + 1; var end = language.indexOf("&"); if (end == -1) { end = language.length; } locale = language.substring(begin, end); } } if (locale == null) { // Look for the locale on the web page locale = __gwt_getMetaProperty("locale") } if (locale == null) { return "default"; } while (!__gwt_isKnownPropertyValue("locale", locale)) { var lastIndex = locale.lastIndexOf("_"); if (lastIndex == -1) { locale = "default"; break; } else { locale = locale.substring(0,lastIndex); } } return locale; } catch(e) { alert("Unexpected exception in locale "+ "detection, using default: " + e); return "default"; } ]]> The module first establishes the locale property. This is the property we extended in listing 6 to include it and fr. Next, the property provider is defined . The value is checked first as a parameter on the request URL in the format locale=xx and then as a tag on the host page in the format ; finally, it defaults to default. The last step is to define a generator class. Here it tells the compiler to generate implementations of all classes that extend or implement Localizable with the LocalizableGenerator . This class writes out Java files that implement the appropriate interfaces for each of the user’s defined Constants, Messages, or Dictionary classes. Notice that, to this point, nowhere have we dealt specifically with JavaScript outside of small snippets in the modules. GWT will produce the JavaScript in the final step. Producing output You can do a great deal from Java with the GWT module system, but you may need to get down to JavaScript-level implementations at some point if you wish to integrate existing JavaScript or extend or add lower-level components. For this, GWT includes JSNI. This is a special syntax that allows you to define method implementations on a Java class using JavaScript. Listing 8 shows a simple JSNI method. Listing 8 A simple JSNI method public class Alert { public Alert() { super(); } public native void alert(String message) /*-{ alert(message); }-*/; } In this listing, we first define the alert() method using the native keyword. Much like the Java Native Interface (JNI) in regular Java, this indicates that we are going to use a native implementation of this method. Unlike JNI, the implementation lives right in the Java file, surrounded by a special /*- -*/ comment syntax. To reference Java code from JavaScript implementations, syntax similar to JNI is provided. Figure 2 shows the structure of calls back into Java from JavaScript. Figure 2 The structure of JSNI call syntax GWT reuses the JNI typing system to reference Java types. The final compiled output will have synthetic names for methods and classes, so the use of this syntax is important to ensure that GWT knows how to direct a call from JavaScript. In the final step of the compilation process, GWT takes all the Java files, whether provided or generated, and the JSNI method implementations, and examines the call tree, pruning unused methods and attributes. Then it transforms all of this into a number of unique JavaScript files, targeted very specifically at each needed axis. This minimizes both code download time and execution time in the client. Although this complex compilation process can be time consuming for the developer, it ensures that the end user’s experience is the best it can possibly be for the application that is being built. This article is excerpted from Chapter 1 of GWT in Practice, by Robert Cooper and Charlie Collins, and published in May 2008 by Manning Publications.
June 17, 2008
by Schalk Neethling
· 126,147 Views
article thumbnail
Image Cross Fade Transition with jQuery
a frequent query and request i receive, and have had as a developer myself is: “how can i fade one image into another?”. in particular, nathan wrigley of pictureandword.com , needed a method which would fade one image into another on a mouse roll over event, and then slowly fade back once the mouse has moved of the image. image rollovers were the staple javascript nugget of the 90s, and for a lot of javascript developers i know, one of the starting points that led to their passion for the javascript language. today, rollovers are a no-brainer, whether with css or the simplest of javascript: $(function () { $('img.swap').hover(function () { this.src="images/sad.jpg"; }, function () { this.src="images/happy.jpg"; });}); today’s challenge is the rollover transition! watch the complete screencast ( alternative flash version ) (quicktime version is approx. 20mb, flash version is streaming) how to approach the problem there are a few different ways in which this problem can be solved (and i’d love to hear alternative methods via the ). here are the different approaches i’m going to go through: two image single image pure css the key to all of these techniques is how the rendered markup (i.e. what the browser finally sees) is arranged: all of which are very similar. essentially, the end image for the transition must sit absolute ly in the same position as the starting image. it’s also worth keeping in mind that the images we fade between should be the same size (height & width-wise). note: all three of these techniques have a caveat: styling the start or end image may cause the effect to break. i would recommend wrapping the image in a div or span and styling that element, as it will require less changes to the javascript. either way: it is always best to test in the targeted browsers. two image technique i should start by crediting karl swedberg who runs learning jquery . he solved nathan’s transition problem using the following technique. karl’s method starts with the two images in the markup: both the start and end images. they are contained in a div and the end image is contained in a further div with absolute positioning. it is important to note that this technique works best for absolutely position images. changing the div.fade to position: relative means the div element remains as a block element, and div will stretch the width of it’s container element (defaulting to 100%). view the working example and the source html css obviously if i had more than one fading image, i would use an id or alternative class to position the top and left css properties. .fade { position: absolute; top: 100px left: 100px } .fade div { position: absolute; top: 0; left: 0; display: none; } jquery // when the dom is ready: $(document).ready(function () { // find the div.fade elements and hook the hover event $('div.fade').hover(function() { // on hovering over, find the element we want to fade *up* var fade = $('> div', this); // if the element is currently being animated (to a fadeout)... if (fade.is(':animated')) { // ...take it's current opacity back up to 1 fade.stop().fadeto(250, 1); } else { // fade in quickly fade.fadein(250); } }, function () { // on hovering out, fade the element out var fade = $('> div', this); if (fade.is(':animated')) { fade.stop().fadeto(3000, 0); } else { // fade away slowly fade.fadeout(3000); } }); }); single image technique this takes the two image technique further. i like the idea that we should let javascript add the sugar to the markup - in that we should really only want an image tag, and using some method, know which image we want to fade to. this technique allows us to insert the image in the markup as we would if there were no transition effect, and the image can be inline, rather being positioned absolutely. we are going to use the background-image css property to specify the target image to fade to. view the working example and the source html css other than the inline background image - none is required. you can also apply the background-image using classes if you like. if we wanted to absolutely position the image, or float: right for instance, the best way to do this (if we want to keep the transition), would be to wrap it in a div and style that element. jquery using jquery, we execute the following tasks: wrap the image in a span insert a new image, whose source is the background-image of our start image position the new image so that sits directly behind the starting image bind the hover event to start the effect // create our transition as a plugin $.fn.crossfade = function () { return this.each(function () { // cache the copy of jquery(this) - the start image var $$ = $(this); // get the target from the backgroundimage + regexp var target = $$.css('backgroundimage').replace(/^url|[\(\)]/g, '')); // nice long chain: wrap img element in span $$.wrap('') // change selector to parent - i.e. newly created span .parent() // prepend a new image inside the span .prepend('') // change the selector to the newly created image .find(':first-child') // set the image to the target .attr('src', target); // position the original image $$.css({ 'position' : 'absolute', 'left' : 0, // this.offsettop aligns the image correctly inside the span 'top' : this.offsettop }); // note: the above css change requires different handling for opera and safari, // see the full plugin for this. // similar effect as single image technique, except using .animate // which will handle the fading up from the right opacity for us $$.hover(function () { $$.stop().animate({ opacity: 0 }, 250); }, function () { $$.stop().animate({ opacity: 1 }, 3000); }); }); }; // not only when the dom is ready, but when the images have finished loading, // important, but subtle difference to $(document).ready(); $(window).bind('load', function () { // run the cross fade plugin against selector $('img.fade').crossfade(); }); pure css technique if i’m honest, this final technique is a bit cheeky - but still valid. it uses css animations currently only available in safari 3 (and webkit). however, this is a great example of how to the leverage css using an iphone, in place javascript. the html is the same rendered html from the single image technique - but it requires zero javascript. html css although this is only supported in safari 3, the roll over still works in firefox (and could work in ie7 - though not ie6 because :hover only works on anchors) - because it’s changing the image’s opacity on :hover . img.fade { opacity: 1; -webkit-transition: opacity 1s linear; } img.fade:hover { opacity: 0; } taking it further i’ve taken the single image technique further in to a complete plugin. it’s designed to allows us to pass options to control the type of bind, delays, callbacks and tests before running the animation. download the full plugin you can see the plugin in action in this simple memory game i put together quickly. it pulls the latest photos from flickr , shuffles them, and then sets your memory skills to work. it’s obviously just a quick prototype - and i’m not sure what happens when you go beyond level 5! enjoy.
April 21, 2008
by $$anonymous$$
· 160,759 Views
article thumbnail
Understanding Loose Typing in JavaScript
for many front end developers, javascript was their first taste of a scripting and/or interpretive language. to these developers, the concept and implications of loosely typed variables may be second nature. however, the explosive growth in the demand for web 2.0-ish applications has resulted in a growing number of back end developers that have had to dip their feet into pool of client side technologies. many of these developers are coming from a background in strongly typed languages, such as c# and java, and are unfamiliar with both the freedom and the potential pitfalls involved in working with loosely typed variables. since the concept of loose typing is so fundamental to scripting in javascript, an understanding of it is essential. this article is a top level discussion of loose typing in javascript. since there may be subtle differences in loose typing from language to language, let me constrain this discussion to the context of javascript. ok, let's dig in... what is loose typing? well, this seems like a good place to start. it is important to understand both what loose typing is , and what loose typing is not . loose typing means that variables are declared without a type. this is in contrast to strongly typed languages that require typed declarations. consider the following examples: /* javascript example (loose typing) */ var a = 13; // number declaration var b = "thirteen"; // string declaration /* java example (strong typing) */ int a = 13; // int declaration string b = "thirteen"; // string declaration notice that in the javascript example, both a and b are declared as type var. please note, however, that this does not mean that they do not have a type, or even that they are of type "var". variables in javascript are typed, but that type is determined internally. in the above example, var a will be type number and var b will be type string. these are two out of the three primitives in javascript, the third being boolean. javascript also has other types beyond primitives. the type diagram for javascript is as follows (as per mozilla ): ya really - null and undefined too. note, however, that this distinction between primitives and objects will be dismissed in javascript 2.0. you can read more about that here . type coercion type coercion is a topic that is closely associated with loose typing. since data types are managed internally, types are often converted internally as well. understanding the rules of type coercion is extremely important. consider the following expressions, and make sure you understand them: 7 + 7 + 7; // = 21 7 + 7 + "7"; // = 147 "7" + 7 + 7; // = 777 in the examples above, arithmetic is carried out as normal (left to right) until a string is encountered. from that point forward, all entities are converted to a string and then concatenated. type coercion also occurs when doing comparisons. you can, however, forbid type coercion by using the === operator. consider these examples: 1 == true; // = true 1 === true; // = false 7 == "7"; // = true 7 === "7"; // = false; there are methods to explicitly convert a variable's type as well, such as parseint and parsefloat (both of which convert a string to a number). double negation (!!) can also be used to cast a number or string to a boolean. consider the following example: true == !"0"; // = false true == !!"0"; // = true conclusion this obviously is not a definitive reference to loose typing in javascript (or type coercion for that matter). i do hope, however, that this will be a useful resource to those who are not familiar with these topics, and a good refresher for those who already are. i have tried to insure that the above is accurate, but if you notice anything incorrect, please let me know! and as always, thanks for reading!
March 14, 2008
by Jeremy Martin
· 15,419 Views
article thumbnail
Easy Multi Select Transfer with jQuery
I'm sure that at some point or another you've encountered a form widget like the one below, allowing options to be traded from one multi select to another. Option 1 Option 2 Option 3 Option 4 add >> << remove I recently encountered a tutorial over at Quirks Mode on creating such a widget. While not a bad script, when all was said and done it was coming up on 40 lines of JS. I suppose that's not horrible, but we're talking about some simple functionality. This struck me as a perfect example to demonstrate the simple and compact nature of jQuery coding. The widget operating above is running off of the following code: $().ready(function() { $('#add').click(function() { return !$('#select1 option:selected').remove().appendTo('#select2'); }); $('#remove').click(function() { return !$('#select2 option:selected').remove().appendTo('#select1'); }); }); That's it... 8 lines. You can also try it out for yourself with the following test page: Option 1Option 2Option 3Option 4 add >> << remove Since the purpose of this widget is usually to collect all the elements in the second multi select, you can use the following snippet to automatically select all of the options before submitting. $('form').submit(function() { $('#select2 option').each(function(i) { $(this).attr("selected", "selected"); }); }); Just make sure you include that snippet inside the $().ready() handler. Thanks for viewing and I hope you found this helpful! Feel free to use and modify without constraint.
March 7, 2008
by Jeremy Martin
· 31,715 Views
article thumbnail
Let's Create a Tetris Game in Compiled JavaFX Script
i thought it would be fun to spend a few posts creating a tetris game in compiled javafx script together. today i made a rough start on it, and if you promise not to laugh, i'll show you the humble beginnings. here's a screenshot of its current status: in tetris, there are several types of tetrominoes , each having a letter that it resembles. the four buttons on the left represent four of these shapes. when you press one of these buttons, the corresponding tetromino appears at the top and begins moving down the screen. when you click the rotate button, the tetromino rotates to the right, and the left / right buttons move the tetromino left and right, respectively. the code is contained in four fx program files, and needs some refactoring already. :-) before showing you the code in its current state, i'd like to point out a couple of helpful things: as explained in the spinning wheel post, the key frame animation syntax that you see here will become much less verbose as the javafx script compiler team continues to address animation. javafx script programs should always be designed with the ui binding to a model. in this program, the model is represented in one class named tetrismodel , located in the fx file of the same name. you may find it helpful to take a look the creating a compiled javafx script program with multiple fx source files post to see a hello world style program that has more than one fx file. please notice the package statments in this tetris program, as that influences where you need to put the source files and how you build them. you can obtain the javafx compiler by following the instructions in the obtaining the openjfx script compiler post. the source code (so far) here's the main program, named tetrismain.fx , that declaratively expresses the ui, and starts things up: /* * tetrismain.fx - the main program for a compiled javafx script tetris game * * developed 2008 by james l. weaver (jim.weaver at lat-inc.com) * to serve as a compiled javafx script example. */ package tetris_ui; import javafx.ui.*; import javafx.ui.canvas.*; import java.lang.system; import tetris_model.*; frame { var model = tetrismodel { } var canvas:canvas width: 480 height: 500 title: "tetrisjfx" background: color.white content: borderpanel { center: canvas = canvas {} bottom: flowpanel { content: [ button { text: "i" action: function() { canvas.content = []; insert tetrisshape { model: model shapetype: tetrisshapetype.i } into canvas.content; model.t.start(); } }, button { text: "t" action: function() { canvas.content = []; insert tetrisshape { model: model shapetype: tetrisshapetype.t } into canvas.content; model.t.start(); } }, button { text: "l" action: function() { canvas.content = []; insert tetrisshape { model: model shapetype: tetrisshapetype.l } into canvas.content; model.t.start(); } }, button { text: "s" action: function() { canvas.content = []; insert tetrisshape { model: model shapetype: tetrisshapetype.s } into canvas.content; model.t.start(); } }, button { text: "rotate" action: function() { model.rotate90(); } }, button { text: "left" action: function() { model.moveleft(); } }, button { text: "right" action: function() { model.moveright(); } } ] } } visible: true onclose: function():void { system.exit(0); } } i made the tetrisshape class a custom graphical component. therefore, it is a subclass of the compositenode class, and overrides the composenode function. note: there is a typo in line 62 of the tetrisshape.fx listing. "returnreturn" should read "return". it will be corrected asap. /* * tetrisshape.fx - a tetris piece, configurable to the * different shape types. they are: * i, j, l, o, s, t, and z * * developed 2008 by james l. weaver (jim.weaver at lat-inc.com) * to serve as a compiled javafx script example. * */ package tetris_ui; import javafx.ui.*; import javafx.ui.canvas.*; import java.awt.point; import java.lang.system; import tetris_model.*; class tetrisshape extends compositenode { private static attribute squareoutlinecolor = color.black; private static attribute squareoutlinewidth = 2; private attribute squarecolor; public attribute model:tetrismodel; public attribute shapetype:tetrisshapetype on replace { if (shapetype == tetrisshapetype.i) { squarelocs = []; insert new point(0, model.square_size * 1) into squarelocs; insert new point(0, 0) into squarelocs; insert new point(0, model.square_size * 2) into squarelocs; insert new point(0, model.square_size * 3) into squarelocs; squarecolor = color.red; } else if (shapetype == tetrisshapetype.t) { squarelocs = []; insert new point(model.square_size * 1, 0) into squarelocs; insert new point(0, 0) into squarelocs; insert new point(model.square_size * 2, 0) into squarelocs; insert new point(model.square_size * 1, model.square_size * 1) into squarelocs; squarecolor = color.green; } else if (shapetype == tetrisshapetype.l) { squarelocs = []; insert new point(0, model.square_size * 1) into squarelocs; insert new point(0, 0) into squarelocs; insert new point(0, model.square_size * 2) into squarelocs; insert new point(model.square_size * 1, model.square_size * 2) into squarelocs; squarecolor = color.magenta; } else if (shapetype == tetrisshapetype.s) { squarelocs = []; insert new point(model.square_size * 1, 0) into squarelocs; insert new point(model.square_size * 2, 0) into squarelocs; insert new point(0, model.square_size * 1) into squarelocs; insert new point(model.square_size * 1, model.square_size * 1) into squarelocs; squarecolor = color.cyan; } } private attribute squarelocs:point[]; public function composenode():node { return group { transform: bind [ translate.translate(model.square_size * model.tetrominohorzpos, (model.a / model.square_size).intvalue() * model.square_size), rotate.rotate(model.tetrominoangle, squarelocs[0].x + model.square_size / 2, squarelocs[0].y + model.square_size / 2) ] content: [ for (squareloc in squarelocs) { rect { x: bind squareloc.x y: bind squareloc.y width: bind model.square_size height: bind model.square_size fill: bind squarecolor stroke: squareoutlinecolor strokewidth: squareoutlinewidth } } ] }; } } the tetrisshapetype class defines the tetromino types: /* * tetrisshapetype.fx - a tetris shape type, which are * i, j, l, o, s, t, and z * * developed 2008 by james l. weaver (jim.weaver at lat-inc.com) * to serve as a compiled javafx script example. * */ package tetris_ui; import javafx.ui.*; class tetrisshapetype { public attribute id: integer; public attribute name: string; public static attribute o = tetrisshapetype {id: 0, name: "o"}; public static attribute i = tetrisshapetype {id: 1, name: "i"}; public static attribute t = tetrisshapetype {id: 2, name: "t"}; public static attribute l = tetrisshapetype {id: 3, name: "l"}; public static attribute s = tetrisshapetype {id: 4, name: "s"}; } and finally, here's a model class, named tetrismodel: /* * tetrismodel.fx - the model behind the tetris ui * * developed 2008 by james l. weaver (jim.weaver at lat-inc.com) * to serve as a compiled javafx script example. * */ package tetris_model; import javafx.ui.animation.*; import java.lang.system; import com.sun.javafx.runtime.pointerfactory; public class tetrismodel { public static attribute square_size = 20; public attribute a:integer; private attribute pf = pointerfactory {}; private attribute bpa = bind pf.make(a); private attribute pa = bpa.unwrap(); private attribute interpolate = numbervalue.linear; public attribute t = timeline { keyframes: [ keyframe { keytime: 0s; keyvalues: numbervalue { target: pa; value: 0; interpolate: bind interpolate } }, keyframe { keytime: 20s; keyvalues: numbervalue { target: pa; value: 370 interpolate: bind interpolate } } ] }; public attribute tetrominoangle:number; public attribute tetrominohorzpos:number = 10; public function rotate90():void { (tetrominoangle += 90) % 360; } public function moveleft():void { if (tetrominohorzpos > 0) { tetrominohorzpos--; } } public function moveright():void { if (tetrominohorzpos < 20) { //todo:replace 10 with a calculated number tetrominohorzpos++; } } } compile and execute this example, and examine the code. i'll get busy making it behave a little more like a tetris game, and show you some progress in the next post. please feel free to get ahead of me, and make your own version! regards, jim weaver javafx script: dynamic java scripting for rich internet/client-side applications immediate ebook (pdf) download available at the book's apress site
February 22, 2008
by James Weaver
· 17,546 Views
article thumbnail
Ruby On Rails: Change Class Name Into Human Readable String
This turns a class name (like LineItem) into a nice string (like "line item") line_item = LineItem.new puts line_item.class.name.underscore.humanize.lowcase #spits out "line item"
January 9, 2008
by Chris O'Sullivan
· 7,143 Views
article thumbnail
Javascript Sprintf
September 19, 2007
by Snippets Manager
· 468 Views
article thumbnail
How To Convert A String With A Date To A Calendar
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy"); Date date = sdf.parse(strDate); Calendar cal = Calendar.getInstance(); cal.setTime(date);
May 8, 2007
by Snippets Manager
· 160,466 Views · 3 Likes
article thumbnail
JavaScript: Programmatically Click The Form Submit Button
// Programmatically Click the Form Submit Button // by using the 'click()' method submitTags : function() { var btnSubmitTags = document.getElementById( TagsHelperConfig.FORM_TAGS_ENTRY_SUBMIT_BUTTON_ID ); // Programmatically click the submit button btnSubmitTags.click(); }
March 6, 2007
by Snippets Manager
· 12,371 Views · 3 Likes
  • Previous
  • ...
  • 111
  • 112
  • 113
  • 114
  • 115
  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook
×