--- title: Client Side Business Logic in Nodes hide_title: true --- import CopyToClipboardButton from '/src/components/copytoclipboardbutton' import ImportButton from '../../../src/components/importbutton' # Client Side Business Logic Using Nodes ## What you will learn in this guide This guide will show some examples on how to use different logical nodes in Noodl to build simple business logic. The guide will use a conditional form (i.e. a form where different fields need to be filled out based on other fields are filled out) but the concepts are general. In this guide we will not use any Javascript but rely only on existing nodes in Noodl. We will use the nodes [Expression](/nodes/math/expression), [Condition](/nodes/utilities/logic/condition), [Switch](/nodes/logic/switch) and [States](/nodes/utilities/logic/condition) as well as the boolean nodes [And](/nodes/logic/and) and [Or](/nodes/logic/or) to implement the logic. ## Overview The guide will show the following ways to implement logic * Using a **Switch** and **States** node with **Mounted** attribute to control what's visible on the screen * Using **Expression** and **Condition** to validate the content of a text input * Using boolean operators to build logic Another way to build logic is to use the Javascript nodes [Function](/nodes/javascript/function) and [Script](/nodes/javascript/script) this is covered in [this guide](/docs/guides/business-logic/client-side-biz-logic-js). ## Building a conditional form Let's build one of those complicated forms that changes progressively depending on what you enter in the form. In our case, it will be a form for registering returns, for example clothes that your ordered online. ### The form logic In words, we want to build a form that works like this: * You start the form by _either_ entering a order number _or_ an email address. * If you entered a order number, the order number need to be exactly 8 digits, otherwise the form should warn the user. * You also have to give a reason for the return. The options are **The product did not fit**, **The product did not meet my expectations**, or **Other**. * If you selected **The product did not fit** you have to select whether the product was **too large** or **too small**. * If you selected **The product did not meet my expectations** you will ge a given an optional text field to fill out with more details. * If you selected **Other** you will also be given an optional text field to fill out with more details. There is a "Submit" button at the end of the form. It will only be available if all necessary information has been given, otherwise it will be disabled. All the data should be stored in an [Object](/nodes/data/object/object-node) with the following properties: ```json { "email":, "order_nbr":, "reason":, "bad_fit_reason":<"too_small" or "too_large">, "did_not_meet_exp_reason":, "other_reason": } ``` Naturally, you have to have either and email or an order_nbr. Also, if you have selected `bad_fit` as reason you also need a `bad_fit_reason` to be able to submit. Ok, lets go! ## The static content of the form We will start with the content of the form that are unconditional. These are * A [Radio Button Group](/nodes/ui-controls/radio-button-group) with the options `order_nbr` or `email`. * Another [Radio Button Group](/nodes/ui-controls/radio-button-group) with the label `Reason of return` and the options `The product did not fit`, `The product did not meet my expectations` and `Other` * A submit [Button](/nodes/ui-controls/button) * A few titles to seperate the different sections With the labels filled out and some padding and margins the form will look something like below. You can copy the nodes using the Copy/Paste button below and paste into a new Project if you don't want to create the nodes yourself.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/nodes-1.png) ![](/docs/guides/business-logic/client-side-biz-logic-nodes/form-1.png)
Note that the `value` of the [Radio Buttons](/nodes/ui-controls/radio-button) was also filled out (`email`/`order_nbr` and `bad_fit`/`did_not_meet_exp`/`other`). We will use these values to determine our logic later. We've also set the initial value of the first **Radio Button** group to `order_nbr` as we want this to be the default option.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/radio-button-group-panel-1.png)
## Conditional UI using a **Switch** The first conditional part of the form is whether the user fills out an order nr or an email. Let's create that. Often a good way to handle this type of conditional UI is to create both cases and add then into the node tree and then use the **Mounted** property on the root group for each case to pick which one that should be shown. We need a **Switch** that can encode the state, for example the **Switch** could be `true` if the order number is active, otherwise `false`. To do the actual check of the **Value** of the **Radio Button Group** we will use an **Expression** node. This is a simple way to write logical statements. It uses a Javascript format. We will check if the **Value** of the **Radio Button Group** equals the string `order_nbr` and in that case return `true`, otherwise `false`. We write this in the expression node as below: ```javascript order_or_email_selection === "order_nbr" ``` This will create a new input on the **Expression** node called `order_or_email_selection`. We connect the output **Value** of the **Radio Button Group** to that input. Our output `Result` on the **Expression** node will now be either `true` or `false`. This value will control the state of a **Switch**. So create a **Switch**, call it `Order Nbr selected` and connect it to the **State** input of the **Switch**.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/nodes-2.png)
Now the **Switch** will be either **On** or **Off** based on what the user picks in the first **Radio Button Group**. Now we want to use this to control the conditional UI. Add two [Text Input](/nodes/ui-controls/text-input) nodes below the first **Radio Button Group**. One is for the order number, make that one type `Number`, and one for email. That one shold be of type `Email`. Give them a good label. Also make the width 300 px. For the order number, we also add an addtional tip in the place holder text, that it should be 8 digits.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/nodes-3.png) ![](/docs/guides/business-logic/client-side-biz-logic-nodes/form-2.png)
We want these text field to be mutual exclusive, that is, only one to be visible at a time. For the order number one it's super straight forward. Just connect the **Current State** of the **Switch** to **Mounted** input of the **Text Input**. We need to invert the state (`true`=>`false` and `false`=>`true`) for the other **Text Input**. So add in an [Inverter](/nodes/logic/inverter) node and connect it in-between the **Switch** and the **Text Input** for the email.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/nodes-4.png)
As you can see, the two different **Text Inputs** are now visible depending on the selection in the first **Radio Button Group**. ## Conditional UI using a **States** node Now let's build the logic for the second part, the "Reason for returning the product" **Radio Button Group**. Here we actually have four different states we need to keep track of so a **Switch** won't do. The four states are: * No selection * `The product did not fit` * `The product did not meet my expectations` * `Other` We can easily encode these states in a **States** node. Add one to your App. Then create the four states. We will use the names so they match the value in our property `reason` in our data **Object** above, hence the states will be called `no_selection`, `bad_fit`, `did_not_meet_exp` and `other`.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/states-1.png)
We will not add any values to this **States** node. If you used the **States** node before, for example to animate different values, you might find this strange - why don't we need any values that changes when we change states? Well, we are only interested in the state properties of the **States** node, more specifically `At bad_fit`, `At did_not_meet_exp` and `At other`. Again we will use the trick where we add all UI states in the tree and use the **Mounted** signal to control which one is visible. Hence we add our different UI components to the tree. Another **Radio Button Group** to select whether the product was too small or too large, and two **Text Input** nodes that we set as type **Text Areas**. This is how the bottom of the tree looks, with some additional titles and small tweaks on margins. If you want you can copy the nodes to get the exact layuot.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/nodes-5.png)
![](/docs/guides/business-logic/client-side-biz-logic-nodes/form-3.png)
We now need to let the **Radio Button Group** for the reason to do the return control the state of the **States** node. Since we named the states the same as the **Value** coming out of the **Radio Button Group** we can connect the **Value** directly to the **State**.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/nodes-6.png)
We then use the `At ` outputs of the **States** node to control when the respective controls are mounted. Note that only on of the `At ` outputs can be `true` at any given moment, so we don't need to use an **Inverter** here.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/nodes-7.png)
If you click around now, you should see the different controls being mounted depending on the settings of the radio buttons. ## Validating the form contents using **Conditions** and boolean logic nodes Ok, next step is to do some simple validation that the input in the form is coherent, for example that if you said that the product didn't fit you, that you also filled out whether the goods was too small or too large. If you haven't filled out the form properly the "Submit" button should be greyed out. Of course the rules will be different for different scenarios. If you use your email, you won't be forced to fill out an 8 digit order number, etc. Let's first summarize the validation rules: For the order reference: * If you fill out a order number, it has to be exactly 8 digits. * If you fill out an email it cannot be empty. For the reason of return: * You have to pick a reason for the return. * If you picked "Product didn't fit" as a reason, you also have to state whether the product was too small or too large. We need to encode this logic in nodes and then let that control whether the **Button** is **Enabled** or not. Let's start with the two first ones regarding the order reference. We already know if order number was selected in our previously added **Switch**. We also need to check whether the order number is 8 digits. We do this by passing the content to a **String** node. We can then extract the length of the **String** and see if it's exactly 8.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/validation-1.png)
We also check the length of the email string in the same way. Of course, we could also check the email address if valid, but we will leave this for now. Actually, if you do many of these string validations it's probably a good idea to use a specialized module for it like the [Form Validation Module](/library/modules/validation). In this example we will not focus on the string validation but rather the logic to control the state of the button.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/validation-2.png)
Ok, now we are ready to build our logic namely _the order reference is valid IF (the email is selected AND the email has more than 0 characters) OR (the order number is selected AND the order number is exactly 8 digits)_. Note the parenthesises to clarify in what order the different expressions should be evaluated (it makes a difference!). We encode this using **And** and **Or** nodes.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/validation-3.png)
Note how the **Inverted** is used to encode "order number is selected", actually saying "email is not selected". Try it out by entering order numbers and emails, switching back and forth. It works! As you can see, for these boolean constructs, the node tree can be pretty big and messy. So let's clean it up by putting it all in a component. Create a new logic component.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/logic-component-1.png)
Call it "Valid Reference". As you can see you will already have a **Component Input** and **Component Output** node defined here. We need to define the inputs. To do the validation we need the following inputs: `Order Number Selected`. `Order Number` and `Email`. Remove the existing `Do` input and add those in.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/validation-4.png)
The output of the component should be one boolean value, let's call it `Reference Valid`. So remove the two existing ports from the **Component Output** and add that in. Now we cut the existing logic from our root component and add into this component.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/copy-paste-1.png)
The component should look like below when the inputs and outputs have been connected properly. You can also copy and paste the nodes below into the components.
0"},"ports":[],"children":[]},{"id":"87267823-be74-27a9-9191-e0662af19f50","type":"And","x":605.4401471748379,"y":202,"parameters":{},"ports":[],"children":[]},{"id":"45561d30-705c-50e5-adfd-6c6e5d6f6c69","type":"Inverter","x":218.44014717483788,"y":315,"parameters":{},"ports":[],"children":[]},{"id":"0340a6e9-471f-6b11-2a38-2905e669b6ba","type":"And","x":602.4401471748379,"y":383,"parameters":{},"ports":[],"children":[]},{"id":"2bfa14b7-c9bd-a85a-d89b-ddab05eadaa0","type":"Or","x":844.4401471748379,"y":318,"parameters":{},"ports":[],"children":[]},{"id":"fa0eb4b6-cf7a-7897-8dce-5b9edc5aa6f4","type":"Boolean","x":1077.2997086893442,"y":322.44948030643695,"parameters":{},"ports":[],"children":[]}],"connections":[{"fromId":"d2024637-8e72-a6c8-c42c-b4f2ab976e1a","fromProperty":"length","toId":"3306c3b3-d858-24d1-5bf9-8256d23a4c8d","toProperty":"order_number_length"},{"fromId":"45561d30-705c-50e5-adfd-6c6e5d6f6c69","fromProperty":"result","toId":"0340a6e9-471f-6b11-2a38-2905e669b6ba","toProperty":"input 0"},{"fromId":"87267823-be74-27a9-9191-e0662af19f50","fromProperty":"result","toId":"2bfa14b7-c9bd-a85a-d89b-ddab05eadaa0","toProperty":"input 0"},{"fromId":"0340a6e9-471f-6b11-2a38-2905e669b6ba","fromProperty":"result","toId":"2bfa14b7-c9bd-a85a-d89b-ddab05eadaa0","toProperty":"input 1"},{"fromId":"c4bd66d9-e267-86ca-7636-c80b7abc462f","fromProperty":"length","toId":"aa33b4df-00f8-c285-04c4-7d1e4a4aabb6","toProperty":"email_length"},{"fromId":"aa33b4df-00f8-c285-04c4-7d1e4a4aabb6","fromProperty":"result","toId":"0340a6e9-471f-6b11-2a38-2905e669b6ba","toProperty":"input 1"},{"fromId":"fa0eb4b6-cf7a-7897-8dce-5b9edc5aa6f4","fromProperty":"savedValue","toId":"d4d0f698-22cb-c43d-a5dd-290ebfbbab87","toProperty":"Reference Valid"},{"fromId":"637aac97-3421-6fec-4c05-b003a251960b","fromProperty":"Order Number","toId":"d2024637-8e72-a6c8-c42c-b4f2ab976e1a","toProperty":"value"},{"fromId":"637aac97-3421-6fec-4c05-b003a251960b","fromProperty":"Email","toId":"c4bd66d9-e267-86ca-7636-c80b7abc462f","toProperty":"value"},{"fromId":"637aac97-3421-6fec-4c05-b003a251960b","fromProperty":"Order Number Selected","toId":"45561d30-705c-50e5-adfd-6c6e5d6f6c69","toProperty":"value"},{"fromId":"637aac97-3421-6fec-4c05-b003a251960b","fromProperty":"Order Number Selected","toId":"87267823-be74-27a9-9191-e0662af19f50","toProperty":"input 0"},{"fromId":"3306c3b3-d858-24d1-5bf9-8256d23a4c8d","fromProperty":"result","toId":"87267823-be74-27a9-9191-e0662af19f50","toProperty":"input 1"},{"fromId":"2bfa14b7-c9bd-a85a-d89b-ddab05eadaa0","fromProperty":"result","toId":"fa0eb4b6-cf7a-7897-8dce-5b9edc5aa6f4","toProperty":"value"}]}} /> ![](/docs/guides/business-logic/client-side-biz-logic-nodes/validation-5.png)
With the old logic replaced with our new component it will look like this:
![](/docs/guides/business-logic/client-side-biz-logic-nodes/validation-6.png)
Now we have to do something similar for the reason part of the form. This time we will start with a separate component. So create another logic component, call it `Valid Reason`. We know that the inputs we need to look at are `Return Reason` and `Bad Fit Reason`. The logic would be _The reason is valid if (the reason is `did_not_meet_exp` OR `other`) OR (if the reason is `bad_fit` AND the bad fit reason is EITHER `too_small` OR `too_large`)_. Note that there are other ways to express this logic, this is just one way. Lets build this logic in out new component. We add the **Component Inputs** and a **Component Output** port called `Return Reason Valid`. The built out logic looks like below.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/validation-7.png)
As you can see, you can use the **Expression** node to build more complex tests. In this case we use the `||` (or) operator. In fact, you could have built the whole test in one large **Expression** ```javascript (return_reason === "did_not_meet_exp" || return_reason === "other") || (return_reason === "bad_fit" && (bad_fit_reason === "too_large" || bad_fit_reason === "too_small")) ``` however, the nice thing with splitting it up is that you can see how each sub expression is evaluated which makes it easier to debug if you need to. With the logic component added into the main App it now looks like below:
![](/docs/guides/business-logic/client-side-biz-logic-nodes/validation-8.png)
Ok, we are almost there. We have two components that will tell us if the reference is valid and if the reason is valid. Both of these have to be `true`, so we feed them both into an **And** node. If the result is `true` the "Submit" Button should be enabled, otherwise not. Easy peasy!
![](/docs/guides/business-logic/client-side-biz-logic-nodes/validation-9.png)
## Storing the content of the form in an **Object** Ok, the form is done. We now need to store the values in an **Object** so it can be used elsewhere in an App. While most of this logic is easy, just connect the values to the **Object** and use **Set Object Properties** to store them there is one complication. Let's look at the format again ```json { "email":, "order_nbr":, "reason":, "bad_fit_reason":<"too_small" or "too_large">, "did_not_meet_exp_reason":, "other_reason": } ``` We need to be careful when storing the `email` and `order_nbr`. If we store both we will not know which one to use. So we need to make sure that only one is stored and the other one is empty. The other values are simple, if `reason` is set to `bad_fit` we know that `bad_fit_reason` is valid, otherwise we will just ignore it. Same for `did_not_meet_exp` and `other` for their respective properties. But let's start with the basice. We want to create a new **Object** when the form is mounted. So we start by connecting the **Did Mount** signal from the root **Group** to a [Create New Object](/nodes/data/object/create-new-object) and its **Do** signal.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/save-1.png)
The **Id** of this new **Object** is what we will use going forward. Then lets tackle the other problem, either `email` or `order_nbr` should be stored, never both. So we create two **Set Object Properties** nodes. One with the properties `email`, `reason`, `bad_fit_reason`, `did_not_meet_exp_reason` and `other_reason`. The other have `order_nbr`, `reason`, `bad_fit_reason`, `did_not_meet_exp_reason` and `other_reason`. We connect the **Id** from our **Create New Object** node to the **Ids** of the **Set Object Properties**.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/set-obj-1.png) ![](/docs/guides/business-logic/client-side-biz-logic-nodes/set-obj-2.png)
![](/docs/guides/business-logic/client-side-biz-logic-nodes/set-obj-3.png)
Let's connect all the values from the form components to the **Set Object Properties**.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/save-2.png)
We also need to make sure that for the two cases, the value that's not stored (`email` when using an order number, and `order_nbr` when using an email) is set to an empty string. So we create those properties and connect an empty **String** node to the respective value. Not assigning a value to the **String** node will make it be an empty string.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/save-3.png)
## Using a **Condition** node to trigger different nodes What's left to do is to trigger one of the two **Set Object Properties** depending on the **Switch** we use to determine the two reference cases. We will use a **Condition** node. It's very good when you want to evaluate something at one particular moment - in our case when the "Submit" Button is clicked. You will get either of two signals **On True** or **On False** depending on if the value to evaluate is `true` or `false`. Let's build it! Create a **Condition** node. We connect the **Current State** output of our `Order Nbr Selected` **Switch** to the **Condition** input of the **Condition** node. This is _what_ we want to evaluate. We then connect the **Click** signal of the "Submit" Button to the **Evaluate** action on th **Condition** node. This is _when_ we want to evaluate it. Finally we connect the **On True** and **On False** signals from the **Condition** node to the respective **Set Object Properties** nodes. **On True** means "Order Number Selected" is `true` so naturally that signal should be connected to the **Set Object Properties** that has `email` set to empty. And vice versa for **On False**.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/save-4.png)
Let's connect an **Object** to the **Id** so we can see what's going on.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/result-1.png)
We are done, we implemented our conditional form with custom logic. You can download the full project below.
![](/docs/guides/business-logic/client-side-biz-logic-nodes/final-2.png)