Skip to main content
Kinetic Community

Pass values between a form and its subforms

Overview

In this example we are going to create a form and embed a subform into the main form. I will refer to the form that has the subform embedded into it as the main form for the rest of this article. There are many techniques that can be use to pass data between a subform and a main form. We are going to use the K function to retrieve field objects and update the field values. We are also going to put a watch on fields to detect change and execute the write function when a change event is triggered. This article assumes an intermediate level understanding of javascript and that you have used the Kinetic Request CE product. This exercise uses the request-ce-triangle bundle, if you are using another bundle you many need to create an unstyled jsp page for displaying your embedded form (see attachments).

Create Main Form

  1. From the Management Console navigate to the desired kapp.
  2. Choose the Form option for the kapp.
  3. Click the Create Form button in the upper right.
  4. Give the form a Name.
    • I chose to give my form the name "Parent Form"
  5. Click the Create button.

Populate the Main Form with fields and content

  1. After the form is created you should be directed to the builder.  If not navigate to the form builder for the newly created form.
  2. Add a Content type Page element and give it a Name.
    • I named mine "Page"
  3. Add a Content type HTML element and give it a Name.
    • I named mine "Sub Form Container"
  4. Add a Field type Text element and give it a Name.
    • I named mine "Parent Text Field"

  5. Select the HTML element.
  6. Copy the code and paste it in the HTML Content area. (the image above shows where to paste the code)
<div id="sub-container"></div>
<script>
  K.load({
    path: bundle.kappLocation()+'/child-form',
    container: '#sub-container',
    loaded: function(form){
      var subform = K.as(form)
      subform('form').$watch('Child Text Field', function(newVal,oldVal){
        K('field[Parent Text Field]').value(newVal);
      })
      K('form').$watch('Parent Text Field',function(newVal,oldVal){
        subform('field[Child Text Field]').value(newVal);
      })
    },
  })
</script>

Adding the code in script tag in an HTML element is not following best practices, it is done here for clarity and demonstration purposes only. All Javascript should be placed in it's own file (or in many files) in the bundle and a declaration to that file should be added to the script pack. Adding Javascript to an HTML element requires that the code be wrapped in <script> tags.

Create and Populate the Subform

  1. Follow the steps in the Create Main Form section to create a subform.
    • I named my subform "Child Form"
    • It is important that the slug of the subform be "child-form" or that the path property in the above code be edited to match the slug you choose
  2. Add a Content type Page element and give it a Name.
    • I named mine "Page"
  3. Set the Display Page field of the Page element to formUnstyled.jsp
    • This requires your bundle to have a jsp named formUnstyled (I have include the jsp used in this example in the attachment section below)
  4. Add a Field type Text element and give it a Name.
    • I named mine "Child Text Field"

Usage

Once your are done creating and populating the two forms make sure to Save each of them and Preview the main form. If you used the same names as this example it should look like one form with two fields. One with a label "Child Text Field" belonging to the subform and one with a label "Parent Text Field" belonging to the main form. Type into the field with the Child Text Field label and then leave the field's focus (click outside of the field). You will see that the Parent Text Field will be updated with the value from the Child Text Field.

Now type in the field labeled Parent Text Field and then leave the field's focus and you will see the Child Text Field will be updated with the value from the Parent Text Field.

This is useful in many situations. You could be pass an ID defined by you from a subform to a main form or visa versa. You could also use the watch to write to a hidden field and have the hidden field control a display conditional in ether the main form or the subform. This allows each form to control the behavior of the other form.

JavaScript code analysis

In this section I give a brief explanation of what the code is doing.

  K.load({
    path: bundle.kappLocation()+'/child-form',
    container: '#sub-container',
    loaded: function(form){
      var subform = K.as(form)
      subform('form').$watch('Child Text Field', function(newVal,oldVal){
        K('field[Parent Text Field]').value(newVal);
      })
      K('form').$watch('Parent Text Field',function(newVal,oldVal){
        subform('field[Child Text Field]').value(newVal);
      })
    },
  })

First K.load({}) will attempt to retrieve a form using a rest call.  K load looks to find the form using the path property.  

The first part of the path property is bundle.kappLocation(). The bundle kapp location is a JavaScript function available inside every form.  It returns a context variable meaning that it will return a value base on where it is called from. In my example bundle.kappLocation() returns "/kinetic/internal/catalog" where internal is my space and catalog is my kapp.

The second part of the path property is +'/child-form'. The + is going to concatenate the return from the bundle kapp location function call to whatever string follows the plus sign. The single quotes define a string. The / defines a subdirectory path to our form which in this example is child-form. child-form is the slug of the form we are attempting to retrieve. So put all together when the path is evaluated it would be "/kinetic/internal/catalog/child-form" which should return our subform.

The second property defined is the container. When the subform is returned it will be in HTML. In order for the subform to display we need a container to append the HTML to. If you look at the code in the Populate the Main Form with fields and content section you will notice that there is an HTML div element with an id attribute that is equal to sub-container. The container property needs to be provided a Jquery selector to know where to append the returned HTML to; in the example we used #sub-container. # is used to identify an HTML element with and Id attribute.

The Third property is the loaded callback. Once the form has been retrieved and loaded you can define your own behavior that will be executed at that time. The loaded callback function can have the form object of the subform that was retrieved passed in as a parameter.

The first line inside of the loaded call back is where the magic happens. K is a function used to gain access to a number of runtime object like the current kapp, form and fields. K has a reference to the main form which means that it is not possible to get access to a field on the subform using the K object directly. As an example K('field[Child Text Field]') returns null because the Child Text Field belongs to the subform which the K doesn't "know" about. This presents a problem if you want to move values between fields on the main form and subforms and is the reason for this article. Luckily the genius developers at Kinetic Data left a way to get access to the fields on the subform. K.as() allows for a form object to be passed in, then the K function executes from the reference of the passed in form.

If you remember the loaded call back allows for the form object of the subform to be passed in. So if we use K.as(form) inside of the loaded callback we can execute commands like K.as(form)('field[Child Text Field]') to return the field object. For clarity I pass the K.as(form) function into a variable called subform so that the commands look like subform('field[Child Text Field]') instead.

subform('form').$watch('Child Text Field', function(newVal,oldVal){})

Next we are applying a watch. Watches monitor changes in input field values. The first parameter in the watch can be a function or a string that matches a field on the form. This is tricky because you have to remember which form you are adding the watch to. In the first watch I am using the subform so I apply the watch to the field on the subform with the name "Child Text Field". The second parameter for the watch is a function that takes two parameters. The first parameter is the new value or the value currently in the field that is being watched. The second parameter is the old value or the previous value that was in the field being watched.  Remember that the watch fires when the field loses focus; so the new and old values are referenced to before and after the focus event being triggered.

We are not using the old value inside our watch at all. The objective of this exercise was to pass the current value from a subform to a main form and to pass a value from a main form to its subform. To pass the value of the Child Text Field to the Parent Text Field inside of the watch of the Child Text Field I use K('field[Parent Text Field]').value(newVal). K('field[Parent Text Field]') is the selector to get the field object, and one of its properties is a function to get/set the value in the field. I pass the variable newVal, which is the current value of the Child Text Field, into the value function of the Parent Text Field. This sets the value of the Parent Text Field to match the value of the Child Text Field.

The second watch is the same as the first watch but in reverse. We are watching the Parent Text Field and having that update the value of the Child Text Field after the Parent Text Field loses focus.

Attached Files

formUnstyled.jsp - Place in the bundle on the same level as the kapp.jsp file