- From: Leigh L Klotz Jr <leigh.klotz@xerox.com>
- Date: Tue, 10 May 2011 10:33:32 -0700
- To: public-webapps@w3.org, Rafael Weinstein <rafaelw@google.com>
- CC: public-forms@w3.org
- Message-ID: <4DC976EC.4050505@xerox.com>
> From: Rafael Weinstein<rafaelw@google.com <mailto:rafaelw@google.com?Subject=Re%3A%20Model-driven%20Views&In-Reply-To=%253CBANLkTimZcXiO8U8xHg2Y6eT4igixkCiF7w%40mail.gmail.com%253E&References=%253CBANLkTimZcXiO8U8xHg2Y6eT4igixkCiF7w%40mail.gmail.com%253E>> > It sounds like the group wants to proceed by looking first at missing > primitives. Maciej is right that one of them is the ability to declare > inert DOM structures, In XForms we use the /instance/ element to do this. > but my feeling is that it's probably best to > start with the central problem: > > -There's no way to observe mutations to JS objects. I'll give some comments on this second question, but first I'll go through how we do "inert DOM structures" using the instance element. Of course, in spec-land we do it by describing the /instance element /and its interactions with expressions referring to it, and with DOM events, but in the context of the current discussion about adding new webapps support, it's important to take a look at how it's achieved in today's client-side implementations of XForms in common desktop browsers. There are two main approaches: The AgenceXML implementation uses an XSLT PI at the top to parse out the XForms namespaced elements, and when xhtml:head/xforms:model/xforms:instance with element content is seen, it's converted into a DOM object. The Ubiquity XForms implementation uses a purely Javascript-based approach to this transformation, without using the XSLT PI, but it suffers the treatment non-HTML elements receive when appearing lexically in the host document DOM. As far as feature definition of the "inert DOM structure" goes, here's a brief overview of how we use instance in XForms, and then I'll segue into how separating data and presentation makes the mutation observation question solvable. Additionally, note that XForms allows for an @src attribute on instance which specifies a resource to be loaded; there's a DOM event which signals that it's time to do this, and the XForms processor responds to that event. Finally, XForms offers a /submission/ element which can submit data from an ID'd instance, and send data from responses back to instances or parts thereof. For example, <html> <head> <model> <instance> <quote> <color>red</color> <size>3</size> <quote> </instance> <submission id="price" resource="/rfq" method="post" replace="instance" /> </model> </head> <body> <input ref="color"> <label>Color: </color> </input> <input ref="size"> <label>Size: </label> </input> <output ref="price"> <label>Your price: </label> </output> <submit submission="buy"> <label>Buy</label> </submit> </body> </html> When the page is loaded, the instance XML will get initialized with the data. When the user interacts with the form controls, changes will be commited to the instance data. When the user presses the submit button labeled Buy, the submission will POST the instance data. Since the submission says replace instance, the submitted instance will be replaced. Note that the initial data contains no price, so the output bound to "price" will not display at all; it's considered /irrelevant/. When the response comes back, if it has a price, it will then display, along with its label. Let's say you wanted to split the request and response into two different pieces of XML and not share them in the page. Just add a second instance, and now put ID attributes on the two: <html> <head> <model> <instance id="rfq"> <quote> <color>red</color> <size>3</size> <quote> </instance> <instance id="quote"><empty /></instance> <submission id="price" resource="/rfq" method="post" ref="instance('rfq') replace="instance" target="quote" /> </model> </head> <body> </body> <input ref="color"> <label>Color: </color> </input> <input ref="size"> <label>Size: </label> </input> <output ref="instance('quote')/price"> <label>Your price: </label> </output> <submit submission="buy"> <label>Buy</label> </submit> </body> </html> Note the changes in the submission to show that the data from the "rfq" instance and the response goes to "quote." If some sub-part of the instance were to be submitted, it would be done inside that expressions. Now, let's assume that performing a request for a quote is idempotent and has no side effects. REST web architecture would have us use a GET instead of a POST. XForms uses sensible defaults, so the GET will serialize leaf-node data as application/x-www-url-formencoded, so all we need to do is change the method on the submission from POST to GET: <html> <head> <model> <instance id="rfq"> <quote> <color>red</color> <size>3</size> <quote> </instance> <instance id="quote"><empty /></instance> <submission id="price" resource="/rfq" method="get" ref="instance('rfq') replace="instance" target="quote" /> </model> </head> <body> </body> <input ref="color"> <label>Color: </color> </input> <input ref="size"> <label>Size: </label> </input> <output ref="instance('quote')/price"> <label>Your price: </label> </output> <submit submission="buy"> <label>Buy</label> </submit> </body> </html> Let's move the initial order out of the form and into a resource on the server by changing instance to have a src attribute: <html> <head> <model> <instance id="rfq" src="initial-quote.xml" /> <instance id="quote"><empty /></instance> <submission id="price" resource="/rfq" method="get" ref="instance('rfq') replace="instance" target="quote" /> </model> </head> <body> </body> <input ref="color"> <label>Color: </color> </input> <input ref="size"> <label>Size: </label> </input> <output ref="instance('quote')/price"> <label>Your price: </label> </output> <submit submission="buy"> <label>Buy</label> </submit> </body> </html> where initial-quote.xml has the obvious content, and is served up with an HTTP GET. Let's flesh out the data layer a bit with some datatypes and constraints. Let's say size is an integer and it's constrained to be 0 to 14. We do this declaratively by binding a datatype and a constraint to the size node. Furthermore, let's make sure that both color and size aren't empty with required="true()": <html> <head> <model> <instance id="rfq" src="initial-quote.xml" /> <bind ref="instance('rfq')"> <bind ref="size" type="integer" constraint=". >= 0 and . <= 14" required="true()" /> <bind ref="color"required="true()" /> </bind> <instance id="quote"><empty /></instance> <submission id="price" resource="/rfq" method="get" ref="instance('rfq') replace="instance" target="quote" /> </model> </head> <body> </body> <input ref="color"> <label>Color: </color> </input> <input ref="size"> <label>Size: </label> </input> <output ref="instance('quote')/price"> <label>Your price: </label> </output> <submit submission="buy"> <label>Buy</label> </submit> </body> </html> Now the form won't submit (and if you try will raise an xforms-submit-error DOM event with the context information), and any form controls bound to the invalid nodes will receive xforms-invalid events when the user interacts with them. Furthermore, those controls will also get CSS pseudo-property :invalid. (They're also similarly have :required and xforms-required events). So this interestingly answers Rafael's questions below about mutations. In XForms, the mutations happen to the data model, but the observation of the mutations happens at the presentation level, and is reflected in the HTML world as DOM events and CSS presentation style attributes. Using DOM Events (or the XML syntax isomorphism XML Events), you can place listeners on the form controls bound to the data nodes, and run actions when the events happen. > Current approaches resort to a number of hacks which create really terrible > artifacts in application code. The ECMA Script Proxy mechanism may be the > best starting point, but there's a related > problem with timing that isn't addressed. > Leigh.
Received on Tuesday, 10 May 2011 17:38:48 UTC