I've been working on these and I've added some stuff. It's still needs a lot of work (I'm going to add callback and options as well as more widgets and fix some inconsitencies on how the widgets are created and inserted into the temapltes), I don't think this is ready for inclusion on the SVN trunk. The patch is provided so other programmers can check the code from problems or improvements.

You can test the functions added on (as long as the IP lasts):
http://200.117.30.210:8888/rpc
http://200.117.30.210:8888/remoteform

(try senao on the searchfield, tested on opera 8.5, IE 6 and FireFox 1.5)

The controllers for these tests are:
@turbogears.expose(template="pruebatg.templates.rpc")
   def rpc(self):
       mostrarArticulosSenao = widgets.LinkRemoteFunction(
               target = turbogears.url('/buscarArticulo'),
               data = dict(searchString = 'senao')
       )
       mostrarArticulosModem = widgets.LinkRemoteFunction(
               target = turbogears.url('/buscarArticulo'),
               data = dict(searchString = 'modem')
       )
return dict(mostrarArticulosSenao=mostrarArticulosSenao, mostrarArticulosModem=mostrarArticulosModem)

   @turbogears.expose(template="pruebatg.templates.remoteform")
   def remoteform(self):
       formBusquedaArticulo = widgets.RemoteForm(
           name = "BusquedaArticulo",
widgets = [widgets.TextField("searchString", default=None, validator=validators.String())],
     target_dom = 'articulos'
       )
       return dict(formBusquedaArticulo=formBusquedaArticulo)

And the templates:
   for rpc:
<span py:replace="mostrarArticulosSenao.insert('Find *senao*',update='articulos')"></span><br/> <span py:replace="mostrarArticulosModem.insert('Find *modem*',update='articulos')"></span>
           <div id="articulos"></div>

   for remoteform:
<span py:replace="formBusquedaArticulo.insert(action='/buscarArticulo')"></span>
           <div id="articulos"></div>


Before continuing on this I wanted an orientation about the MVC paradigm, I've been working for a long time on a framework of my own so there are many stuff that I still have to get used to. For example, I'm pretty confused on this particular point, why the action url for TableForms is specificed on the views and not in the controllers, you will see that RemoteForm behaves just like TableForm because it's inherited from it but in LinkRemoteFunction I've put the target url to be specified when the widget is created in the controller, what you specifiy on the view is the DOM target that will cointain the answer from the POST done. I'd like to know is this is correct or if I have to change it, also in my patch the dictionary containing the POST data for the LinkRemoteFunction widget has to be specfied on the controller. I'm not sure if this is ok or not either.

After getting those things staight I'll try to get both widgets to follow the MVC correctly

--
Claudio


Index: turbogears/widgets/static/ajax.js
===================================================================
--- turbogears/widgets/static/ajax.js   (revision 0)
+++ turbogears/widgets/static/ajax.js   (revision 0)
@@ -0,0 +1,58 @@
+function remoteFormRequest(form, target) {
+       var query = Array();
+       var target = getElement(target);
+       fields = form.getElementsByTagName('INPUT');
+       for( var i=0; i<fields.length; i++) {
+               element = getElement(fields[i].name);
+               if (element) {
+                       query[element.name] = element.value;
+               }
+       }
+       query["tg_random"] = new Date().getTime();
+       makePOSTRequest(form.action, target, queryString(query));
+       return true;
+}
+
+function remoteRequest(target_url, target_dom, data, options) {
+       makePOSTRequest(target_url, getElement(target_dom), queryString(data));
+       return true;
+}
+
+function makePOSTRequest(url, target, parameters) {
+  var http_request = false;
+  if (window.XMLHttpRequest) { // Mozilla, Safari,...
+     http_request = new XMLHttpRequest();
+     if (http_request.overrideMimeType) {
+        http_request.overrideMimeType('text/xml');
+     }
+  } else if (window.ActiveXObject) { // IE
+     try {
+        http_request = new ActiveXObject("Msxml2.XMLHTTP");
+     } catch (e) {
+        try {
+           http_request = new ActiveXObject("Microsoft.XMLHTTP");
+        } catch (e) {}
+     }
+  }
+  if (!http_request) {
+     alert('Cannot create XMLHTTP instance');
+     return false;
+  }
+
+  var insertContents = function () {
+         if (http_request.readyState == 4) {
+            if (http_request.status == 200) {
+               target.innerHTML = http_request.responseText;
+            } else {
+               alert('There was a problem with the request. 
Status('+http_request.status+')');
+            }
+         }
+       }
+  
+  http_request.onreadystatechange = insertContents;
+  http_request.open('POST', url, true);
+  http_request.setRequestHeader("Content-type", 
"application/x-www-form-urlencoded");
+       http_request.setRequestHeader("Content-length", parameters.length);
+  http_request.setRequestHeader("Connection", "close");
+  http_request.send(parameters);
+}
Index: turbogears/widgets/forms.py
===================================================================
--- turbogears/widgets/forms.py (revision 440)
+++ turbogears/widgets/forms.py (working copy)
@@ -1,8 +1,9 @@
-from turbogears.widgets import Widget, SubmitButton, TextField
+from turbogears.widgets import Widget, SubmitButton, TextField, JSLink, 
static, mochikit
 from turbogears import validators
 from turbogears.util import setlike
 import cherrypy
 from formencode import declarative
+from turbogears import jsonify
 
 class attrdictwrapper(object):
     def __init__(self, d):
@@ -132,7 +133,8 @@
     template = """
 <form xmlns:py="http://purl.org/kid/ns#"; name="${widget.name}" 
       action="${getattr(self, 'action', None)}" method="${getattr(self, 
'method', 'post')}"
-      enctype="${getattr(self, 'enctype', None)}">
+      enctype="${getattr(self, 'enctype', None)}"
+      py:attrs="getattr(widget, 'attrs', dict())">
     <div py:for="widget in widgets" py:if="widget.hidden" py:strip="True">
        ${widget.insert(getattr(self.widget_value, widget.name, None), 
input_values, widget_error.get(widget.name, None))}
     </div>
@@ -152,3 +154,71 @@
     </table>
 </form>
 """
+
+class RemoteForm(TableForm):           
+               def __init__(self, **kw):
+                       super(RemoteForm, self).__init__(**kw)
+                       self.attrs["onSubmit"] = "return 
!remoteFormRequest(this, '%s');" % self.target_dom
+
+               def retrieve_javascript(self, **kw):
+                               scripts = super(RemoteForm, 
self).retrieve_javascript(**kw)
+                               scripts.add(JSLink(static,'ajax.js'))
+                               scripts.add(mochikit)
+                               return scripts
+
+class RPC(object):
+               """ RPC base object """
+               
+               def __init__(self, target=None, data=None, **kw):
+                               assert target is not None and data is not None, 
"Missing keyword argument"
+                               
+                               # this will invoke the widget constructors 
(multi-inheritance)
+                               super(RPC, self).__init__(**kw)
+                               
+                               self.target_url = target
+                               #self.target_dom = target_dom
+                               self.data = data
+
+                               # TODO: callbacks
+                               """
+                               The callbacks that may be specified are (in 
order): 
+                               :loading:       Called when the remote document 
is being loaded with data by the browser.       
+                               :loaded:        Called when the browser has 
finished loading the remote document.       
+                               :interactive:   Called when the user can 
interact with the remote document, even though it has not finished loading.    
+                               :success:       Called when the XMLHttpRequest 
is completed, and the HTTP status code is in the 2XX range.      
+                               :failure:       Called when the XMLHttpRequest 
is completed, and the HTTP status code is not in the 2XX range.  
+                               :complete:      Called when the XMLHttpRequest 
is complete (fires after success/failure if they are present).,
+                               """
+                               
+                               # TODO: options
+                               """
+                               You can customize further browser side call 
logic by passing in JavaScript code snippets via some optional parameters. In 
their order of use these are: 
+                               :confirm:       Adds confirmation dialog.       
+                               :condition:     Perform remote request 
conditionally by this expression. Use this to describe browser-side conditions 
when request should not be initiated.     
+                               :before:        Called before request is 
initiated.     
+                               :after: Called immediately after request was 
initiated and before :loading.     
+                               :submit:        Specifies the DOM element ID 
that's used as the parent of the form elements. By default this is the current 
form, but it could just as well be the ID of a table row or any other DOM 
element.
+                               """
+       
+               def insertjs(self, target_dom):
+                               return "return ! remoteRequest('%s', '%s', %s, 
%s)" % (self.target_url, target_dom, jsonify.encode(self.data), dict())
+                               
+               def retrieve_javascript(self, **kw):
+                               scripts = super(RPC, 
self).retrieve_javascript(**kw)
+                               scripts.add(JSLink(static,'ajax.js'))
+                               scripts.add(mochikit)
+                               return scripts
+                               
+class LinkRemoteFunction(RPC, Widget):
+               """ Returns a link that executes a POST asynchronously 
+               and updates a DOM Object with the result of it """
+               
+               template = """
+<div xmlns:py="http://purl.org/kid/ns#"; py:strip="True">
+<a name="${widget.name}" py:attrs="widget.attrs" 
onclick="${widget.insertjs(target_dom=self.update)}" 
href="#">${widget_value}</a>
+</div>
+"""
+
+               def __init__(self, description=None, **kw):
+                               super(LinkRemoteFunction, self).__init__(**kw)
+                               self.description = description
\ No newline at end of file

Reply via email to