Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 55 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
55
Dung lượng
0,97 MB
Nội dung
THE RAILS WAY 376 XMLHttpRequest vs. <iframe> So, you ask, what’s all the hype about? I did this with <iframe> for years! While it’s true you can do something along the lines of what XMLHttpRe- quest does, iframes are not nearly as flexible nor as clean as AJAX to use. Unlike the <iframe> approach, with AJAX • it’s easy to do GET, POST, and other HTTP request types, •theDOMisnotalteredinanyway, • you have powerful callback hooks, • there’s a clean API, and • you can customize HTTP headers. Considering all this, it’s obvious that XMLHttpRequest provides a far cleaner and more powerful programming model than that of iframes. 18.2 The Rails Way Rails has built-in support for AJAX calls, which makes it very easy to put your application on track with the Web, version 2.0. First of all, it has the prototype , 4 effects, dragdrop,andcontrols JavaScript prototype libraries built-in. These library neatly wrap all sorts of useful AJAX and DOM manipulation stuff in a nice, object-oriented way. The second thing is JavascriptHelper, a module that defines the methods JavascriptHelper we’ll be looking at in the rest of this chapter. It wraps JavaScript access in pristine Ruby code, so you won’t have to switch to another language when using AJAX. Talk about total integration. To use any of the functions defined by JavascriptHelper, you first have to include the prototype.js file in your application. Do this by making this call in the < head > section of your .rhtml page. <%= javascript_include_tag "prototype" %> For the code in this chapter, we’ve added the call to javascript_include_tag to our overall application.rhtml layout file, making the library available to all of our examples. 4 http://prototype.conio.net Report erratum THE RAILS WAY 377 You also need the prototype.js file in your application’s public/javascripts directory. It’s included by default if you generate your application’s struc- ture by running the rails command. link_to_remote The syntax for making a basic AJAX call from an .rhtml template can be as simple as File 196 <%= link_to_remote("Do the Ajax thing", :update => 'mydiv', :url => { :action => :say_hello }) %> <div id="mydiv">This text will be changed</div> This basic form of the link_to_remote( ) method takes three parameters. • The text for the link •The id= attribute of the element on your page to update • The URL of an action to call, in url_for()format When the user clicks on the link, the action ( say_hello in this case) will be invoked in the server. Anything rendered by that action will be used to replace the contents of the mydiv element on the current page. The view that generates the response should not use any Rails layout wrappers (because you’re updating only part of an HTML page). You can disable the use of layouts by making a call to render( ) with the :layout option set to false or by specifying that your action shouldn’t use a layout in the first place (see Section 17.9, Locating Layout Files,onpage357,formore on this). So, let’s define an action. File 186 def say_hello render(:layout => false) end And then define the corresponding say_hello.rhtml view. File 200 <em>Hello from Ajax!</em> (Session ID is <%= session.session_id %>) Try it. The text “This text will be changed” in the < div > element with id="mydiv" will be changed (see Figure 18.2, on the following page) to some- thing like Hello from Ajax! (Session ID is <some string>) It’s that easy. The session id is included to show off one more thing— cookie handling. Session information is handled transparently by the Report erratum THE RAILS WAY 378 Figure 18.2: Before and After Calling the Action via AJAX underlying XMLHttpRequest. You’ll always get the correct user’s session, regardless of whether an action is called by AJAX or not. Behind the Scenes Let’s have a look at what happened during our link_to_remote example. First, let’s take a quick look at the HTML code generated by link_to_remote(). <a href="#" onclick="new Ajax.Updater('mydiv', '/example/say_hello', {asynchronous:true}); return false;"> Do the AJAX thing </a> link_to_remote() generates an HTML < a > tag that, when clicked, generates a new instance of Ajax.Updater (which is defined in the Prototype library). This instance calls XMLHttpRequest internally, which in turn generates an HTTP POST request to the URL given as second parameter. 5 This process isshowninFigure18.3, on the next page. Let’s see what happens on the server. 127.0.0.1 - - [21/Apr/2005:19:55:26] "POST /example/say_hello HTTP/1.1" 200 51 5 For security reasons you can safely call URLs only on the same server/port as the page that includes the call to XMLHttpRequest. Report erratum THE RAILS WAY 379 HTML new Ajax.Request() XMLHttpRequest send() Rails action XMLHttpRequest readyState == complete Do something with the returned HTML Browser Server asynchronous (non-blocking) call raises event Figure 18.3: XMLHttpRequest Connects Browser and Server The web server (WEBrick, in this case) got an HTTP POST request to call /example/say_hello. From the server’s perspective this looks just like a normal, run-of-the-mill HTTP POST. That’s not surprising, because that’s what it is. The server then returns the output of the action being called (in this case say_hello()) to the XMLHttpRequest object that got created behind the scenes by link_to_remote(). The Ajax.Updater instance takes over and replaces the contents of the element given as first parameter (in this case mydiv)with the data returned from the XMLHttpRequest object. The browser updates the page to reflect the new content. As far as the user is concerned, the page simply changes. form_remote_tag() You can easily change any Rails form to use AJAX by replacing the call to form_tag()with form_remote_tag(). This method automatically serializes all form elements and sends them to the server, again using XMLHttpRequest. No change to your action is required—it simply receives its data as it normally would. 6 Let’s build a game. The object is to complete a simple phrase: the game says “Ruby on ” and the user has to supply the missing word. Here’s the controller. File 187 class GuesswhatController < ApplicationController def index end def guess @guess = params[:guess] || '' if @guess.strip.match /^rails$/i 6 There is one exception: you can’t use file upload fields with form_remote_tag( ), because JavaScript can’t get at file contents. This is a security (and performance) constraint imposed by the JavaScript model. Report erratum THE RAILS WAY 380 render(:text => "You're right!") else render(:partial => 'form') end end end The index.rhtml template file looks like this. File 204 <h3>Guess what!</h3> <div id="update_div" style="background-color:#eee;"> <%= render(:partial => 'form')%> </div> Finally, the main part of this hip new game that will make you rich and famous is the _form.rhtml partial. File 203 <% if @guess %> <p>It seems '<%=h @guess %>' is hardly the correct answer</p> <% end %> <%= form_remote_tag(:update => "update_div", :url => { :action => :guess } ) %> <label for="guess">Ruby on ?</label> <%= text_field_tag :guess %> <%= submit_tag "Post form with AJAX" %> <%= end_form_tag %> Try it out—it’s not too hard to find the answer, as shown in Figure 18.4, on the following page. form_remote_tag( ) is a great way to add on-the-fly inline forms for things such as votes or chats to your application, all without having to change anything about the page it’s embedded in. Partial templates help you honor the DRY principle—use the partial when initially displaying the form, and use it from your AJAX’d action. No change necessary. Observers Observers let you call AJAX actions when the user changes data on a form or in a specific field of a form. You can put this to good use to build a real-time search box. File 198 <label for="search">Search term:</label> <%= text_field_tag :search %> <%= observe_field(:search, :frequency => 0.5, :update => :results, :url => { :action => :search }) %> <div id="results"></div> Report erratum THE RAILS WAY 381 Figure 18.4: AJAX-Style Forms Update Inside Existing Window Report erratum THE RAILS WAY 382 Figure 18.5: Build Real-Time Searches with Observers The observer waits for changes to the given form field, checking every :fre- quency seconds. By default, observe_field uses the current value of the text field as the raw POST data for the action. You can access this data in your controller using request.raw_post. Having set up the observer, let’s implement the search action. We want to implement a search over a list of words in an array, with nice highlighting of the search term in the result. File 186 WORDLIST = %w(Rails is a full-stack, open-source web framework in Ruby for writing real-world applications with joy and less code than most frameworks spend doing XML sit-ups) File 186 def search @phrase = request.raw_post || request.query_string matcher = Regexp.new(@phrase) @results = WORDLIST.find_all { |word| word =~ matcher } render(:layout => false) end The view, in search.rhtml, looks like this. File 201 <% if @results.empty? %> '<%=h @phrase %>' not found! <% else %> '<%=h @phrase %>' found in <%= highlight(@results.join( ', '), @phrase) %> <% end %> Point your browser at the observer action, and you’ll get a nice text field with real-time search capability (see Figure 18.5 ). Note that in this exam- ple, the search supports regular expressions. Report erratum THE RAILS WAY 383 The @phrase = request.raw_post || request.query_string line allows you to test your search by entering a URL such as /controller/search?ruby directly in the browser—the raw POST data won’t be present, so the action will use the query string instead. The action invoked by an observer shouldn’t be overly complex. It might get called very often, depending on the frequency you set and how quickly your user types. In other words, avoid heavy database lifting or other expensive operations. Your user will thank you for it too, as he or she will experience a snappier interface. Periodic Updates The third helper function, periodically_call_remote(), helps if you want to keep part of your page refreshed by periodically calling the server via AJAX. As an example, we’ll show a process list from the server, updating it every couple of seconds. This example uses the ps command, so it’s fairly Unix- specific. Putting the command in backquotes returns its output as a string. Here’s the controller. File 186 def periodic # No action end # Return a process listing (Unix specific code) def ps render(:text => "<pre>" + CGI::escapeHTML(‘ps -a‘) + "</pre>") end And here’s the periodic.rhtml template. This contains the call to periodi- cally_call_remote (). File 199 <h3>Server processes:</h3> <div id="process-list" style="background-color:#eee;"> </div> <%= periodically_call_remote(:update => 'process-list', :url => { :action => :ps }, :frequency => 2 )%> If you’ve paid extra for the embedded web server version of this book, you’ll see Figure 18.6, on the following page update the list every two seconds (you should see the TIME column for the “ruby script/server” process go up with each iteration!). If you just bought the paper or PDF copies, you’ll have to take our word for it. Report erratum THE USER INTERFACE,REVISITED 384 Figure 18.6: Keeping Current Using periodically_call_remote 18.3 The User Interface, Revisited Web applications traditionally offer a less interactive user interfaces than traditional desktop applications. They didn’t really need more—until now. With the emergence of Web 2.0 this has to change, as we’ve been given boatloads of control over what happens on a web page with AJAX. The Prototype library overcomes this problem, helping your application communicate with the user in an intuitive way. And it’s fun, too! Besides the support for making AJAX calls, the Prototype library offers a wealth of useful objects to make your life easier and your users’ experience better at the same time. The functionality offered by the Prototype library falls into the following groups. • AJAX calls (which we’ve already discussed) • Document Object Model(DOM)manipulation • Visual effects Document Object Model Manipulation The standard support for DOM manipulation in JavaScript is cumbersome and clunky, so Prototype delivers handy shortcuts for a number of often- Report erratum THE USER INTERFACE,REVISITED 385 used operations. These functions are all JavaScript and are intended to be invoked from within the pages delivered to the browser. $(id) Pass the $( ) method a string, and it returns the DOM element with the given id. Otherwise it returns its argument. (This behavior means you can pass in either an element’s id= attribute or the element itself and get an element returned.) $('mydiv').style.border = "1px solid red"; /* sets border on mydiv */ Element.toggle(element, ) Element.toggle ( ) toggles whether the given element (or elements) are shown. Internally, it switches the value of the CSS display attribute between ’inline’ and ’none’. Element.toggle('mydiv'); /* toggles mydiv */ Element.toggle( 'div1', 'div2', 'div3'); /* toggles div1-div3 */ Element.show(element, ) Element.show ( ) ensures all elements it receives as parameters will be shown. Element.show('warning'); /* shows the element with id 'warning' */ Element.hide(element, ) Opposite of Element.show(). Element.remove(element) Element.remove ( ) completely removes an element from the DOM. Element.remove('mydiv'); /* completely erase mydiv */ Insertion methods The various insertion methods make it easy to add HTML fragments to existing elements. They are discussed in Section 18.4, Replace- ment Techniques,onpage389. Visual Effects Because AJAX works in the background, it’s transparent to the user. The server may receive an AJAX request, but the user doesn’t necessarily get any feedback about what’s going on. The browser doesn’t even indicate that a page is loading. The user might click a button to delete an entry from a to-do list, and that button might send off a request to the server, but without feedback, how is the user to know what’s happening? And, typically,iftheydon’t see something happening, the average user will just click the button, over and over. Report erratum [...]... this chapter and the Action Web Service code, is an analyst/developer originally from the city of Cape Town, South Africa Chapter 20 Web Services on Rails With the Depot application up and running, we may want to let other developers write their own applications that can talk to it using standard web service protocols To do that, we’ll need to get acquainted with Action Web Service (which we’ll call... http://www.mozilla.org/projects/venkman/ Report erratum 396 A DVANCED T ECHNIQUES Backward Compatibility Rails has several features that can help make your AJAX’d web application work with non-AJAX browsers or browsers with no JavaScript support You should decide early in the development process if such support is necessary or not; it may have profound implications on your development plans Called by AJAX? Use the request.xml_http_request?... Ruby development Wiki at http://wiki.rubyonrails.com /rails/ show/HowToReceiveEmailsWithActionMailer If you don’t have this kind of system-level access but you are on a Unix system, you could intercept e-mail at the user level by adding a rule to your procmailrc file We’ll see an example of this shortly The objective of intercepting incoming e-mail is to pass it to our application To do this, we use the Rails. .. between groups and use AJAX functions only within a group For example, you might want to use a normal link when you jump from a weblog’s start page to an article (so the Back button jumps back to the start page) and use AJAX for commenting on the article.13 Web V2.1 The AJAX field is changing rapidly, and Rails is at the forefront This makes it hard to produce definitive docmentation in a book—the libraries... updates (and to play with some cool effects) is Thomas Fuch’s site http://script.aculo.us/ 13 In fact, that’s what the popular Rails- based weblog software Typo does Have a look at http://typo.leetsoft.com/ Report erratum 3 98 Chapter 19 Action Mailer Action Mailer is a simple Rails component that allows your applications to send and receive e-mail Using Action Mailer, your online store could send out... 37signals; see http://www.37signals.com/svn/archives/0005 58. php Report erratum 387 T HE U SER I NTERFACE , R EVISITED Figure 18. 7: Up and Away units If you scale an image, width and height are not required to be set Let’s do some scaling on an image "new Effect.Scale(this, 100)") %> You can also do this with text, if you use em units for your font sizes . id="results"></div> Report erratum THE RAILS WAY 381 Figure 18. 4: AJAX-Style Forms Update Inside Existing Window Report erratum THE RAILS WAY 382 Figure 18. 5: Build Real-Time Searches with Observers The observer. programming model than that of iframes. 18. 2 The Rails Way Rails has built-in support for AJAX calls, which makes it very easy to put your application on track with the Web, version 2.0. First of all,. an array, with nice highlighting of the search term in the result. File 186 WORDLIST = %w (Rails is a full-stack, open-source web framework in Ruby for writing real-world applications with joy