<div ng-class="css('rowFluid')">
  <div class="visible-lg visible-desktop col-md-3 span3">
    </div>
    <div class="col-sm-9 span9 page-header">
        <h1>Schemas</h1>
        Controlling what your inputs look like
    </div>
</div>

<div class="container-fluid schema-page" ng-controller="DemoCtrl">

<div ng-class="css('rowFluid')">
<div class="visible-lg visible-desktop col-md-3 span3">
    <affix></affix>
</div>

<div class="affix-section col-sm-9 span9">
<section id="introduction" name="Introduction" affix-scroll>
    <h3>Introduction</h3>

    <p>The most visible part of forms-angular is the <strong>form-input</strong> directive which
        takes a <em>schema</em> in the scope and creates inputs. These schemas can be very simple as in the
        example below:</p>
    <pre>
&lt;div
  ng-init="name=[
    {name:'surname'},
    {name:'forename']" &gt;
  &lt;form-input schema="name" /&gt;
&lt;div&gt;</pre>
    <p>This simple bit of markup creates a small schema and stores it in the scope as <em>name</em> (you
        wouldn't normally it like this, but it illustrates things well) then invokes the form-input directive,
        telling it to use the schema just created to create a form with the appropriate input fields.</p>

    <div class="well well-sm well-small" ng-init="name=[{name:'surname'},{name:'forename'}]">
        <form-input schema="name" noautofocus="true" /> <!--Add a noautofocus here otherwise the resulting form stops the page scrolling to the internal links-->
    </div>
    <pre ng-show="record">Record content: {{record}}</pre>

    <p>There is more information about the forms-input directive on the <a ng-href="{{buildUrl('forms')}}">forms page</a>.</p>
</section>
<section id="formschema" name="Form Schemas" affix-scroll>
    <h3>Form Schemas</h3>

    <p>The form-input directive can get a good deal richer than the simple example above.  The following
        options are available in a form schema element:</p>
    <ul>
        <li><strong>type</strong> sets the type of input control generated.  In addition to the
            <a href="http://www.html5rocks.com/en/tutorials/forms/html5forms/">standard
                HTML5 types</a> there are some 'special' types:<ul>
                <li><strong>textarea</strong> a textarea control</li>
                <li><strong>radio</strong> a radio button control</li>
                <li><strong>select</strong> a select control</li>
            </ul></li>
        <li><strong>id</strong> specifies the id of the input field (which defaults to f_<em>name</em>)</li>
        <li><strong>label</strong> specifies the label of the input field (which defaults to <em>name</em> converted to title case)</li>
        <li><strong>required</strong> validates the field</li>
        <li><strong>placeHolder</strong> allows placeholder text to be specified</li>
        <li><strong>showWhen</strong> either:<ol><li>an object of the form {lhs:variable, comp:comparison, rhs:variable} where:<ul>
            <li><strong>variable</strong> can be a string, number or field in the model preceded by <strong>$</strong></li>
            <li><strong>comparison</strong> is one of 'eq', 'ne', 'gt', 'gte', 'lt', 'lte'</li></ul><p>or</p>
        </li><li>an angular expression</li></ol>
        </li>
    <li><strong>help</strong> is for specifying help text</li>
    <li><strong>helpInline</strong> is for specifying help text which is displayed inline</li>
    <li><strong>add</strong> allows anything else to be added to the input html.  For example
      <ul>
        <li><code>add: 'class="myClass"'</code> allows custom styling of a specific input</li>
        <li><a href="https://docs.angularjs.org/api/ng/directive/ngModelOptions" target="_blank">Angular model options</a> can be used - for example
          <code>add: 'ng-model-options="{updateOn: \'default blur\', debounce: { \'default\': 500, \'blur\': 0 }}" '</code></li>
        <li>custom validation directives, such as the <a href="/scripts/directives/timezone.js">timezone validation</a> in <a href="/models/b_enhanced_schema.js">this schema</a></li>
      </ul>
    </li>
    <li><strong>rows</strong> (in conjunction with a textarea type) allows the depth of the control to be
        specified.  The <strong>auto</strong> option creates a textarea that automatically expands</li>
    <li><strong>options</strong> (in conjunction with a select or radio type) allows the options for the select / radio button group to
        be specified.  You can either do this by passing the option values in an array and passing it directly,
        or by putting them in an array on the scope and passing the name of the array (which allows run-time
        modification</li>
</ul>

    <h5>Example</h5>
    <pre>&lt;div ng-init="example2=[
        {name:'surname',label:'Family Name',required:true},
        {name:'forename',id:'fnameinput'},
        {name:'dateOfBirth',type:'date'},
        {name:'sex',type:'radio',options:['male','female']},
        {name:'hairColour',type:'select',options:['brown','black','blonde','grey']}]"&gt;
    &lt;form-input schema="example2" /&gt;
&lt;div&gt;</pre>
    <p>which looks like:</p>
    <div ng-init="colours=['brown','black','blonde','grey']"></div>
    <div class="well well-sm well-small" ng-init="example2=[
        {name:'surname',label:'Family Name',required:true},
        {name:'forename',id:'fnameinput'},
        {name:'dateOfBirth',type:'date',help:'Here is some help text'},
        {name:'sex',type:'radio',options:['male','female']},
        {name:'hairColour',type:'select',options:['brown','black','blonde','grey']}]">
        <!--Add a noautofocus & forceform here and modify the model-->
        <form>
            <form-input schema="example2" model="exampledata" forceForm="true" noautofocus="true" />
        </form>
    </div>
    <pre ng-show="exampledata">Record content: {{exampledata}}</pre>

</section>

<section id="mongoose" name="Mongoose Schemas" affix-scroll>
    <h3>Mongoose Schemas</h3>

    <p>Mongoose is a popular package that simplifies using MongoDB with Node JS. Mongoose requires that
        a schema is created for each 'model'. <strong>forms-angular</strong> (specifically the BaseCtrl controller)
        can convert Mongoose schemas into the schemas that the form-input directive uses, which saves a lot of
        coding by hand and makes it very easy to keep your front and back ends in sync.  If you use BaseCtrl, then you
        should set the <strong>schema</strong> attribute of the form-input tag to "baseSchema()".</p>

    <p>As many models as you want can be registered with forms-angular by calling the <code>addResource</code>
        method of the forms-angular object. If you have more than a few models it is a good idea to put them in their own
        directory and loop through it.</p>

    <p>A trivially simple schema looks like this:</p>
            <pre>var ApplicantSchema = new Schema({
    surname: {type:String, required:true, index:true},
    forename: {type:String, index:true},
});</pre>
    <p>Each schema element (or field) has a type and optionally a number of other properties. Many of these properties
        are used by forms-angular to generate appropriate behaviour on the front end. The appropriate input types are
        used (for instance an <strong>enum</strong> string) attribute will generate a select control (you can specify radio button - see below)
        and client side validation keeps round trips down (we cannot say "to a minimum" as work remains to
        be done in this area).</p>

    <p>You can get an idea of what forms-angular does with a vanilla Mongoose JS schema by looking at <a href="/models/a_unadorned_schema.js">this schema</a> and <a ng-href="{{buildUrl('a_unadorned_schema/new')}}">the
        form generated from it</a>.</p>

    <p>For comprehensive information about Mongoose schemas visit the <a href="http://mongoosejs.com/docs/guide.html">Mongoose JS website</a>.</p>

    <p>Note that enumerated fields options can be specified in the form</p>
<pre>eyeColour: {type:String, enum:['Blue','Brown','Green','Hazel']}</pre>
  <p>or</p>
<pre>eyeColour: {type:String, enum:{values:['Blue','Brown','Green','Hazel']}}</pre>
  <p>as you can see from the <a href="http://mongoosejs.com/docs/api.html#schema_string_SchemaString-enum">Mongoose documentation</a>.  In the latter case
  a <strong>labels</strong> key in the enum object will be used for the labels in the generated input.</p>
  <p>For example</p>
  <pre>eyeColour: {
  type:String,
  enum: {
    values:['bl','br','gr','ha'],
    labels:['Blue','Brown','Green','Hazel']
  }
}</pre>
  <p>will use shorter strings for the stored value than for the displayed label.</p>
</section>
<section id="list-fields" name="List Fields" affix-scroll>
    <h3>List Fields</h3>

    <p>In a couple of places forms-angular uses the concept of 'list fields' - fields that generally allow
        the user to quickly see
        what they are dealing with - for example in the case of a person the forename and surname would be
        list fields. They don't
        have to be unique, they just have to be useful. List fields are used:</p>
    <ul>
        <li>When populating select options when a model is referenced by another model</li>
        <li>In list forms - where the contents of a collection are displayed</li>
        <li>In search results (though this can be overridden)</li>
        <li>In reports when a model is referenced by another model and a columnTranslation is used</li>
    </ul>

    <p>A field may be specified to be a list field by adding a truthy value for a <strong>list</strong> key
        in the schema element: <code>forename: {type:String, list:true}</code></p>.  The <strong>_id</strong> field can be made a list field
  by setting up an <strong>idIsList</strong> property on the model options (since it is implicitly added to mongoose schemas).
  This is useful to get the creation date of the object (for which
  you need to pass the timestamp parameter).  There is an example of this in <a href="/models/b_enhanced_schema.js">this</a> model
  and you can see result in the listing <a ng-href="{{buildUrl('b_enhanced_schema')}}">here</a>.</p>

    <p>List fields can be generated on both the front and back end, and it is done as follows:</p>
    <ol>
        <li>If there is at least one schema element with a truthy value for <strong>list</strong> then all
            such fields are list fields
        </li>
        <li>If no field is specified as a list field then:</li>
        <ul>
            <li>On the client the first non hidden string field is used or failing that the first field</li>
            <li>On the server the first two fields are used (one day there will hopefully be some
                consistency here!)
            </li>
        </ul>
    </ol>
</section>

<section id="form-object" name="The form Object" affix-scroll>
    <h3>The <em>form</em> Object</h3>

    <p>The mark-up of generated forms can be influenced by use of the <strong>form</strong> object in the
        schema type: <pre>
  <code>
surname: {
  type:String,
  form:{
    label:'Family Name',
    size:'large'
  }
}</code></pre></p>

    <p>The form object can have the following optional keys (many of which are duplicates of options in the Form Schemas section
      <a href="{{buildUrl('schemas#formschema')}}">above</a> (where they may have additional documentation and examples) :</p>
    <ul>
        <li><strong>type</strong> the input type to be generated - which must be compatible with the Mongoose type.
            Common examples are <em>email</em>, <em>url</em>.  Note that if the field type is String and the name (or label) contains the string 'password'
            then type="password" will be used <em>unless</em> type="text".  If the Mongoose schema has an <em>enum</em>
            array you can specify a radio button group (instead of a select) by using a type of <em>radio</em></li>.
        <li><strong>hidden</strong> inhibits this schema key from appearing on the generated form.</li>
        <li><strong>label</strong> overrides the default input label. label:null suppresses the label
            altogether.
        </li>
        <li><strong>placeHolder</strong> adds placeholder text to the input (depending on data type).</li>
        <li><strong>help</strong> adds help text under the input.</li>
        <li><strong>helpInline</strong> adds help to the right of the input.</li>
        <li><strong>popup</strong> adds popup help as specified.</li>
        <li><strong>order</strong> allows user to specify the order / tab order of this field in the form.  This
            overrides the position in the Mongoose schema.</li>
        <li><strong>size</strong> sets <a href="http://twitter.github.io/bootstrap/base-css.html#forms">control
            width</a>.
            Options are: mini, small, medium (default), large, xlarge, xxlarge and block-level.
        </li>
        <li><strong>readonly</strong> adds the readonly attribute to the generated input (currently doesn't work with
            date - and perhaps other types). </li>
        <li><strong>rows</strong> sets the number of rows in inputs (such as textarea) that support this.
            Setting rows to <strong>"auto"</strong> makes the textarea expand to fit the content, rather than create a
            scrollbar.</li>
        <li><strong>tab</strong> used to divide a large form up into a tabset with multiple tabs
        (see <a ng-href="{{buildUrl('i_tabbed_form/new')}}">example</a>).
        </li>
        <li><strong>showWhen</strong> allows conditional display of fields based on values elsewhere.  For example having
            prompted whether someone is a smoker you may want a field asking how many they smoke a day:
<pre><code>smoker: {type: Boolean},
howManyPerDay: {type: Number, form:{showWhen:{lhs:"$smoker", comp:"eq", rhs:true}}}</code></pre>
            <p>As you can see from the example there are three parts to the showIf object:</p>
            <ul>
                <li><strong>lhs</strong> (left hand side) a value to be compared.  To use the current value of another
                    field in the document preceed it with $.</li>
                <li><strong>comp</strong> supported comparators are 'eq' for equality, 'ne' for not equals,
                    'gt' (greater than), 'gte' (greater than or equal to), 'lt' (less than) and 'lte' (less than or equal to)</li>
                <li><strong>rhs</strong> (right hand side) the other value to be compared.  Details as for lhs.</li>
            </ul>
        </li>
        <li><strong>noAdd</strong> if set to <strong>true</strong>, inhibits an Add button being generated for arrays.  Alternatively can be set to an
            expression which hides the Add button if it yields a truthty result</li>
        <li><strong>unshift</strong> (for arrays of sub documents) puts an add button in the sub schema header which allows insertion of
          new sub documents at the beginning of the array.</li>
        <li><strong>sortable</strong> (for arrays of sub documents) allow drag and drop sorting - requires ui-sortable and elements of jquery-ui.  See
            <a href="https://github.com/angular-ui/ui-sortable" target="_blank">here</a> for details.</li>
        <li><strong>noRemove</strong> if set to <strong>true</strong>, inhibits a Remove button being generated for array elements.  Alternatively can be set to an
            expression which hides the Add button if it yields a truthy result.  (see an example in <a href="/models/f_nested_schema.js">this schema</a>)</li>
        <li><strong>remove</strong> remove button for array elements has Remove as label (default is 'x' since 0.12).</li>
        <li><strong>add</strong> allows arbitrary attributes to be added to the input tag.</li>
        <li><strong>class</strong> allows arbitrary classes to be added to the input tag.</li>
        <li><strong>inlineRadio</strong> (only valid when type is radio) should be set to true to present all radio button options in a single line</li>
        <li><strong>editor</strong> (only valid when type is textarea) should be set to <em>ckEditor</em> to use <a href="http://ckeditor.com">CKEditor</a></li>
        <li><strong>directive</strong> allows you to specify custom behaviour.  Gets passed attributes from form-input (with
            <em>schema</em> replaced with the current element - so <em>add</em> can be used to pass data into directives).</li>
        <li><strong>noLookup</strong>, which can only be used in confunction with <em>directive</em>, inhibits the forms-angular client from looking up the possible values for a
          'lookup' field (when the directive has a an alternative way of handling things)</li>
        <li><strong>customHeader</strong> (only valid on a sub document) allows you to specify custom HTML (which may include directives) for the header of a group of sub docs</li>
        <li><strong>customSubDoc</strong> (only valid on a sub document) allows you to specify custom HTML (which may include directives) for the sub doc</li>
        <li><strong>customFooter</strong> (only valid on a sub document) allows you to specify custom HTML (which may include directives) for the footer of a group of sub docs</li>
        <li><strong>formstyle</strong> (only valid on a sub schema) sets style of sub form. See <a ng-href="{{buildUrl('forms#form-styles')}}">form style section</a> for options.
        </li>
        <li><strong>link</strong> sets up hyperlinks for reference fields as follows:
            <ul>
                <li><em>linkOnly</em> if true (which at the time of writing is the only option supported)
                    then the input element is not generated.
                </li>
                <li><em>form</em> can be used to generate a link to a <a ng-href="{{buildUrl('schemas#custom')}}">custom schema</a></li>
                <li><em>text</em> the literal value used for the link.  If this property is omitted then text is generated from the field values of the
                  document referred to by the link - see examples in the collection <a ng-href="{{buildUrl('e_referencing_another_collection/links')}}">here</a>
                  (view source code in the form static of <a href="/models/e_referencing_another_collection.js">this</a> file).</li>
            </ul>
        </li>
    </ul>

    <p><a href="/models/b_enhanced_schema.js">This example schema</a> and <a ng-href="{{buildUrl('b_enhanced_schema/new')}}">this
        form</a> shows many of these options in use.</p>

</section>

<section id="containers" name="Containers" affix-scroll>
    <h3>Containers</h3>

    <p>Sometimes it is hard within a Mongoose schema to detail exactly how you want the associated form
        or forms to be laid out. One particular class of layout features that currently cannot be
        requested within a Mongoose schema are containers. Hopefully this will be addressed in a forthcoming
        release
        but if you need them now then you need to create a schema by hand (or dynamically in code).</p>

    <p>An example of a use of containers would be:</p>
    <pre>&lt;form-input ng-init="names=[
  {containerType:'fieldset',title:'Name',content:
    [
      {id:'f_surname',label:'Surname',name:'surname',type:'text'},
      {id:'f_forename',label:'forename',name:'forename',type:'text'}
    ]
  }
]" schema="names"&gt;&lt;/form-input&gt;</pre>
    <p>Other containers are <strong>tab</strong>,
        <strong>well</strong>, <strong>well-large</strong> and <strong>well-small</strong>.</p>

    <p>If you want to "roll your own" you can do something like:</p>
    <pre>&lt;style&gt;
  .redBorder {border:solid 2px red;}
  p.bigRed {color: red; font-size: large}
&lt;/style&gt;
&lt;form-input ng-init="names=[
  {containerType:'redBorder',title:'Name',titleTagOrClass:'bigRed',content:
    [
      {id:'f_surname',label:'Surname',name:'surname',type:'text'},
      {id:'f_forename',label:'forename',name:'forename',type:'text'}
    ]
  }
]" schema="names"&gt;&lt;/form-input&gt;
</pre>
    <p>Where container type is seen as a class for a div and a titleTagOrClass of h1..h6 is interpreted as a
        heading tag and anything else as a class to be applied to a paragraph (as in the example).  If a title is
        specified without a corresponding titleTagOrClass then &lt;h4&gt; is used as a default.</p>

    <p>If you need even more flexibility then you can create a function that returns an object of the form
        {before: <em>markup before container contents</em>, after:<em>markup after container contents</em>}.</p>
</section>

<section id="custom" name="Custom Schemas" affix-scroll>
    <h3>Custom Schemas</h3>

    <p>It is easy to create custom form schemas which are a subset of the whole schema by specifying the
        fields to include and any options. See the static in <a href="/models/b_enhanced_schema.js">this example</a>.
        These schemas are automatically used to generate custom views and routes to them (see <a href="/#/forms#routes">here</a>).
    </p>
    <p>It is also possible to show a subset of fields in a sub-schema (which can be seen <a ng-href="{{buildUrl('f_nested_schema/ResultsOnly/new')}}">here</a>
      if you add a new exam).</p>

    <h4>Subkeys</h4>
    <p>Custom schemas are a good way to allow access to only part of a document, for instance if a customer record contained operational
        and accounts information an accounts user would require a different view of the data (that would include account balance)
        than a user from operations (who would not have access to the account balance).  Consider the following:</p>
    <pre><code>var AddressSchema = new Schema({
        type: { type: String, default: 'Home', enum: ['Delivery', 'Invoice', 'Historic'] },
        street: String,
        town: String,
        postalCode: String
        }, {_id: false});</code></pre>
    <p>and in the customer model</p>
    <pre><code>addressList: {type: [AddressSchema], mergeKey: 'type', form: {pane: 'Address', labels: 'Addresses'}}</code></pre>
    <p>We need a way of having the delivery address appear on one form (for the operation user) and the invoice address on the
        finance form, even though they are in the same field.</p>
    <p>forms-angular has the concept of a <strong>subkey</strong> for this scenario.  The custom schema for the operations user would contain:</p>
    <pre><code>addressList: {subkey:
  {keyList: {type: 'Delivery'}, containerType: 'well-small', title: 'Address', titleTagOrClass: 'h5'}
}</code></pre>
    <p>while that for finance might include</p>
    <pre><code>addressList: {subkey:
  [
    {keyList: {type: 'Invoice'}, containerType: 'well-small', title: 'Invoice To:'},
    {keyList: {type: 'Delivery'}, containerType: 'well-small', title: 'Deliver To:'}
  ]
}</code></pre>
    <p>Note that subkey can be an object or an array of objects.  Where there are multiple sub docs that match the subkey the first will be used.  If there are no sub
      docs then one will be created, unless the subkey object has a <strong>onNotFound</strong> property of <strong>'error'</strong>.</p>
  <p>An alternative way of selecting the sub doc is to specify a function, using the <strong>selectFunc</strong> property.  In this case the function
  (which) must be on the base scope - usually added during the onAllReady event handler.  No automatic sub doc creation happens in this case.</p>
</section>
</div>
</div>
</div>
