Validation and Editing in ProtoListPicker

One of the Newton 2.x OS Q&As
Copyright © 1997 Newton, Inc. All Rights Reserved. Newton, Newton Technology, Newton Works, the Newton, Inc. logo, the Newton Technology logo, the Light Bulb logo and MessagePad are trademarks of Newton, Inc. and may be registered in the U.S.A. and other countries. Windows is a registered trademark of Microsoft Corp. All other trademarks and company names are the intellectual property of their respective owners.


For the most recent version of the Q&As on the World Wide Web, check the URL: http://www.newton-inc.com/dev/techinfo/qa/qa.htm
If you've copied this file locally, click here to go to the main Newton Q&A page.
This document was exported on 7/23/97.


Validation and Editing in ProtoListPicker (4/1/96)

Q: I am trying to use the ValidationFrame to validate and edit entries in a protoListPicker. When I edit certains slots I get an error that a path failed. All the failures occur on items that are nested frames in my soup entry. What is going on?

A: The built-in validation mechanism is not designed to deal with nested soup information. In general, you gain better flexibility by not using a validationFrame in your pickerDef, even if you have no nested entries. Instead, you can provide your own validation mechanism and editors:

Define a Validate method in your picker definition
Define an OpenEditor method in your picker definition
Draw a layout for each editor you require

pickerDef.Validate(nameRef, pathArray)

nameRef - nameRef to validate
pathArray - array of paths to validate in the nameRef
returns an array of paths that failed, or an empty array

Validate each path in pathArray in the given nameRef. Accumulate a list of paths that are not valid and return them.

The following example assumes that pickerDef.ValidateName and pickerDef.ValidatePager have been implemented:

pickerDef.Validate := func(nameRef, pathArray)
begin
    // keep track of any paths that fail
    local failedPaths := [];
                                                        
    foreach index, path in pathArray do
    begin
        if path = 'name then
        begin
            // check if name validation fails
            if NOT :ValidateName(nameRef) then
                // if so, add it to array of failures
                AddArraySlot(failedPaths, path);
        end;
        else begin
            if NOT :ValidatePager(nameRef) then
                AddArraySlot(failedPaths, path);
        end;
    end;
    // return failed paths or empty array
    failedPaths;
end;


pickerDef.OpenEditor(tapInfo, context, why)

The arguments and return value are as per OpenDefaultEditor. However, you need to use this instead of DefaultOpenEditor.

pickerDef.OpenEditor := func(tapInfo, context, why)
begin
   local valid = :Validate(tapInfo.nameRef, tapInfo.editPaths) ;
    if (Length(valid) > 0) then
        // if not valid, open the editor
        // NOTE: returns the edit slip that is opened
        GetLayout("editor.t"):new(tapInfo.nameRef,
            tapInfo.editPaths, why, self, 'EditDone, context);
    else
    begin
        // the item is valid, so just toggle the selection
        context:Tapped('toggle);
        nil;                                        // Return <nil>.
    end;..
end;

The example above assumes that the layout "editor.t" has a New method that will open the editor and return the associated View.

The editor can be designed to fit your data. However, we suggest that you use a protoFloatNGo that is a child of the root view created with the BuildContext function. You are also likely to need a callback to the pickderDef so it can appropriately update the edited or new item. Finally, your editor will need to update your data soup uing an "Xmit" soup method so that the listPicker will update.

In the OpenEditor example above, the last three arguments are used by the editor to send a callback to the pickerDef from the viewQuitScript. The design of the callback function is up to you, here is an example:
pickerDef.EditDone := func(nameRef, context)
begin
   local valid = :Validate(tapInfo.nameRef, tapInfo.editPaths) ;
    if (Length(valid) > 0) then
    begin
    // Something failed. Try and revert back to original
        if NOT :ValidatePager(nameRef) AND
            self.('[pathExpr: savedPagerValue, nameRef]) = nameRef             then
            nameRef.pager := savedPagerValue.pager;
        
        context:Tapped(nil);        // Remove the checkmark
    end;
    else
        // The nameRef is valid, so select it.
        context:Tapped('select);
    
    // Clear the saved value for next time.
    savedPagerValue := nil;        
end;