Try our conversational search powered by Generative AI!

Trouble getting started with XForms

Vote:
 

I'm taking my first look at XForms and trying to figure out how we can use it to meet our requirements. But I'm falling over at the first hurdle and I'm struggling to find clear documentation on how XForms can be customised.

I'm using EPiServer 7.5 WebForms and the things I need to look at are adding in more validation types, improving the mark-up or at least getting some control over it, and customising submit behavior to integrate to a third party API as well as save locally.

What I have at the moment is pretty much directly taken from the Allow project:

public class FormBlock : BaseBlockData
{
    [Display(
        GroupName = SystemTabNames.Content,
        Order = 100)]
    [CultureSpecific]
    public virtual string Heading { getset; }
 
    [Display(
        GroupName = SystemTabNames.Content,
        Order = 200)]
    [CultureSpecific]
    public virtual XForm Form { getset; }
}

<EPiServer:Property runat="server" PropertyName="Heading" CustomTagName="h2" />
<EPiServer:Property runat="server" PropertyName="Form" CustomTagName="div" />

 

And I've started by trying to add in my own data type (ignore regex it is just for testing and validates integers not telephone numbers at this stage):

public class Global : EPiServer.Global
{
    protected void Application_Start()
    {
        EPiServer.XForms.DataTypes.Types.Add("telephone""^\\d+$");
    }
}

However although that prevents submission I can't see how I'd actually supply a validation message. Even with a valid submission there doesn't seem to be any visual indication the form has submitted (not using a new page), even the on the Alloy demo you seem to get a thank you message and part of the requirement for me is to try do in place submission.

Unfortunately all the documentation I've found so far seem to have isolated fragments which I struggle to even tell whether they are appropriate to 7.5 let alone something that gives me a more complete picture of how a developer should work with XForms.

#83191
Mar 27, 2014 12:26
Vote:
 

Okay so progress made on getting the validation message in there, although I had to decompile EPiServer.XForms to figure it out.

Added the following:

  <localization fallbackBehavior="Echo, MissingMessage, FallbackCulture" fallbackCulture="en-GB">
    <providers>
      <add virtualPath="~/Resources/LanguageFiles" name="languageFiles" type="EPiServer.Framework.Localization.XmlResources.FileXmlLocalizationProvider, EPiServer.Framework" />
    </providers>
  </localization>

Then dropped in this XML language file in ~/Resources/LanguageFiles/:

<?xml version="1.0" encoding="utf-8" ?>
<languages>
  <language name="English" id="en-GB">
    <xform>
      <datatypes>
        <telephone>
          <inlineerrormessage>
            Not a valid telephone number.
          </inlineerrormessage>
        </telephone>
      </datatypes>
    </xform>
  </language>
</languages>

Onto the next item. :-)

#83198
Mar 27, 2014 12:59
Vote:
 

Just for completeness I've udpates the language file based on what I see for other datatypes (using LocalizationService.GetAllStrings("/xform/datatypes");)

<?xml version="1.0" encoding="utf-8" ?>
<languages>
  <language name="English" id="en">
    <xform>
      <datatypes>
        <telephone>
          <caption>Telephone number</caption>
          <inlineerrormessage>
            Not a valid telephone number.
          </inlineerrormessage>
          <summaryerrormessage>
            * is not a valid telephone number.
          </summaryerrormessage>
        </telephone>
      </datatypes>
    </xform>
  </language>
</languages>

Don't know if it is a bug or caching thing but the 'Validate as' field on the form editor doesn't seem to be picking up the caption for my new datatype.

#83200
Mar 27, 2014 13:17
Vote:
 

Hi Mark!

I peeked into the code and it seems the UI takes the label from the following path:

/edit/editxform/datatypes/" + typeKey + "/caption"

I have filed the following bug to address this so we use the structure you have used:

Bug #113490: Caption for datatypes in XForms editor should fetch translated values from "public" xml structure
#83254
Mar 28, 2014 9:37
Vote:
 

Okay, came back to this one today and made more progress with styling. I'm actually replacing the table layout by performing an XSLT transform on the FormDefinition. It doesn't change the mark-up of the fields although I do clear the labels and set my own with handy require field indicator.

I now have:

XFormControl.ControlSetup += XFormControl_ControlSetup;
 
void XFormControl_ControlSetup(object senderEventArgs e)
{
    var control = (sender as XFormControl);
    control.ClearEventHandlers();
    control.BeforeLoadingForm += control_BeforeLoadingForm;
}
 
void control_BeforeLoadingForm(object senderLoadFormEventArgs e)
{
    if (!e.EditMode)
    {
        XDocument s = XDocument.Parse(e.FormDefinition);
        XDocument d = new XDocument();
 
        XslCompiledTransform xslt = new XslCompiledTransform(true);
        using (XmlReader xsltReader = XmlReader.Create(GetXsltStream()))
        {
            xslt.Load(xsltReader);
            using (XmlReader reader = s.CreateReader())
            using (XmlWriter writer = d.CreateWriter())
            {
                xslt.Transform(readerwriter);
            }
        }
 
        e.FormDefinition = d.ToString();
    }
}
 
Stream GetXsltStream()
{
    var assembly = Assembly.GetExecutingAssembly();
    var resource = "MyNamespace.XForm.xslt";
 
    return assembly.GetManifestResourceStream(resource);
}

I have this in an initializer but I've just pulled out the relevant parts, you could just as well set it in Global.asax if need.

And my XSLT, although tailored to our solution, looks like:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
    xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <xsl:output method="xml" indent="yes"/>
 
  <xsl:template match="/root">
    <root xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <xsl:copy-of select="./model"/>
      <fieldset id="id_matrix">
        <xsl:apply-templates select=".//td/*" />
      </fieldset>
    </root>
  </xsl:template>
 
  <xsl:template match="td/span">
    <h2>
      <xsl:value-of select="./text()"/>
    </h2>
  </xsl:template>
 
  <xsl:template match="td/xforms:*">
    <div class="form-group">
      <label class="col-md-4 control-label">
        <xsl:attribute name="for">
          <xsl:value-of select="./@ref"/>
        </xsl:attribute>
        <xsl:if test="./@required">
          <em>*</em>
        </xsl:if>
        <xsl:value-of select="./xforms:label/text()"/>
      </label>
      <div class="col-md-8">
        <xsl:apply-templates select="." mode="field" />
      </div>
    </div>
  </xsl:template>
 
  <xsl:template match="td/xforms:submit">
    <xsl:copy>
      <xsl:attribute name="class">btn</xsl:attribute>
      <xsl:apply-templates select="@*|node()" mode="field-copy" />
    </xsl:copy>
  </xsl:template>
 
  <xsl:template match="td/xforms:*" mode="field">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" mode="field-copy" />
    </xsl:copy>
  </xsl:template>
 
  <xsl:template match="td/xforms:input|td/xforms:select1|td/xforms:textarea" mode="field">
    <xsl:copy>
      <xsl:choose>
        <xsl:when test="./@appearance = 'full'"></xsl:when>
        <xsl:otherwise>
          <xsl:attribute name="class">form-control</xsl:attribute>
        </xsl:otherwise>
      </xsl:choose>
      <xsl:apply-templates select="@*|node()" mode="field-copy" />
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="@*|node()" mode="field-copy">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" mode="field-copy" />
    </xsl:copy>
  </xsl:template>
 
  <xsl:template match="td/xforms:*/xforms:label" mode="field-copy" />
 
  <xsl:template match="td/xforms:submit/xforms:label" mode="field-copy">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" mode="field-copy" />
    </xsl:copy>
  </xsl:template>
 
</xsl:stylesheet>

It's not perfect but it's a lot closer to what we wanted. Hope others find this useful.

#87705
Jun 19, 2014 17:07
Vote:
 

Actually one minor tweak to the code:

...
XslCompiledTransform
 xslt = new XslCompiledTransform(true); using (Stream stream = GetXsltStream()) using (XmlReader xsltReader = XmlReader.Create(stream)) {     xslt.Load(xsltReader);
...

Just to make sure we dispose of the stream.

#87706
Jun 19, 2014 17:12
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.