Create Your First Component
New components are defined using the `component` keyword followed by component
name:
Creating a component named `heading`
-- component heading:
-- ftd.text: My Heading
color: red
-- end: heading
Here, we have defined a new component `heading`. This component is using
[`ftd.text`](ftd/text/), a kernel component, as a definition. We have created a
custom-component which shows given text in `red` color.
Kernel Components
So, how is `ftd.text` implemented? `ftd` comes with some "kernel components",
these are the lowest level components that every other component uses directly
or indirectly.
Read about our [kernel components guide](ftd/kernel/) to learn about
these components.
Rendering a Component
Till now, we have created a component called `heading`. Now to invoke or render
this component in your `ftd` application, we can do the following:
Currently, `My Heading` is fixed text displayed by `heading` component. We can
make it customisable by using attributes.
Rendering a Component Conditionally
We can render a component conditionally. This can be achieved using an `if`
keyword.
Input
-- integer num: 10
-- heading:
if: { num <= 10 }
Output
Input
-- integer num: 10
-- heading:
if: { num > 10 }
-- ftd.text: Heading not shown!!
Output
Component Arguments
Component "arguments" refer to the inputs that are passed into a component when
it is called or invoked. These arguments provide the component with the
necessary data that is helpful to configure or customize the component's
behavior or appearance.
Component arguments are like function arguments, and you send them into the
component as attributes.
The arguments in `ftd` components creates variables for each component
invocation. These variables have local scope. This means that these variables
can be accessed only inside the component definition in which they are declared.
Defining Component Arguments
Component arguments starts with argument type follow by argument name, in our
component we can define one such argument, `title`.
The type of our `title` argument is [`string`](ftd/built-in-types/#string).
Using the `title` argument in the component
-- component heading:
string title:
-- ftd.text: $heading.title
color: red
-- end: heading
Adding Attributes to Component
When the component is used in another component or in the main application, the
`title` can be passed in as an attribute of the component:
Input
-- heading:
title: I love `ftd`!
Output
Using Specially Typed Arguments
We can use special types like [`caption`](ftd/built-in-types/#caption),
[`body`](ftd/built-in-types/#body) or [`caption or
body`](ftd/built-in-types/#caption-or-body) that give us flexibility to pass
attributes in different location of [`ftd::p1`
"section"](ftd/p1-grammar/#section-caption).
Let's use `caption or body` type using which we can pass attributes in either
`caption` or `body` area.
`caption or body` type for `title` argument
-- component heading:
caption or body title:
-- ftd.text: $heading.title
color: red
-- end: heading
Passing Attribute in Caption
Passing attribute in caption area makes the component more concise and readable.
It also make it clear what the component represents and what its purpose is.
Input
-- heading: I am in caption area.
Output
Passing Attribute in Body
Passing attributes in the body area can help make it more readable by providing
a way to break up complex or lengthy inputs into more manageable chunks of text.
Input
-- heading:
I am in body area.
Since I am long description, it's better to pass it here. Isn't it?
Output
I am in body area.
Since I am long description, it's better to pass it here. Isn't it?
By default `caption` or `body` is alias for `string` but if you want to pass
types other than `string` you can do the following:
Input
-- component show-number:
caption integer number:
-- ftd.integer: $show-number.number
-- end: show-number
-- show-number: 45
Output
Arguments with Default Values
An argument can be defined with a default value:
-- component heading:
caption or body title:
ftd.color text-color: red
-- ftd.text: $heading.title
color: $heading.text-color
-- end: heading
If no argument is provided, the component instance adopts the default value of
`text-color` defined by the `heading`. On the other hand, if an argument is
provided, it supersedes the default value.
`heading` with default `text-color`
Input
Output
`heading` with `text-color` value as `green`
Input
-- heading: this is nice
text-color: green
Output
Global Variable Reference As Default Value
We can pass global variable reference as a default value:
Passing global variable reference `ftd-title`
-- string ftd-title: I love `ftd`!
-- component heading:
caption or body title: $ftd-title
ftd.color text-color: red
-- ftd.text: $heading.title
color: $heading.text-color
-- end: heading
Other Argument Reference As Default Value
We can pass other argument reference as a default value:
-- component heading-with-detail:
caption title:
body detail: $heading-with-detail.title
-- ftd.column:
spacing.fixed.px: 20
color: $inherited.colors.text
-- ftd.text: $heading-with-detail.title
role: $inherited.types.heading-small
-- ftd.text: $heading-with-detail.detail
role: $inherited.types.label-small
-- end: ftd.column
-- end: heading-with-detail
Input
-- heading-with-detail: Title same as detail
Output
Title same as detail
Title same as detail
Conditional Attributes
Sometimes we want to set an attribute based on a condition.
True Condition Expression
Input
-- integer num: 10
-- heading:
title if { num <= 10 }: `num` is less than equal to 10
title: Default Title
Output
`num` is less than equal to 10
False Condition Expression
Input
-- heading:
title if { num > 10 }: `num` is less than equal to 10
title: Default Title
Output
Creating Container Component
`ftd` provides some container type kernel component like
[`ftd.row`](ftd/row/) and [`ftd.column`](ftd/column/). The container component
accepts the components as an attribute.
Using `ftd.ui list` type
We can define such arguments using [`ftd.ui list`](ftd/built-in-types/#ftd-ui)
type.
-- component show-ui:
caption title:
ftd.ui list uis:
-- ftd.column:
spacing.fixed.px: 10
color: $inherited.colors.text
-- ftd.text: $show-ui.title
-- ftd.column:
children: $show-ui.uis
border-width.px: 1
padding.px: 10
border-color: $inherited.colors.border
-- end: ftd.column
-- end: ftd.column
-- end: show-ui
Here, we have defined an argument `uis` of type `ftd.ui list`. We have also pass
this to [`children`](ftd/container/#children) attribute of `ftd.column`.
Input
-- show-ui: My UIs
-- show-ui.uis:
-- ftd.text: My First UI
-- heading: Using Heading Too
-- end: show-ui.uis
Output
My UIs
My First UI
Using Heading Too
Using `children` type
The [`children`](ftd/built-in-types/#children) type allows us to pass components
in subsection location.
-- component show-ui:
caption title:
children uis:
-- ftd.column:
spacing.fixed.px: 10
color: $inherited.colors.text
-- ftd.text: $show-ui.title
-- ftd.column:
children: $show-ui.uis
border-width.px: 1
padding.px: 10
border-color: $inherited.colors.border
-- end: ftd.column
-- end: ftd.column
-- end: show-ui
Input
-- show-ui: My UIs
-- ftd.text: My First UI
-- heading: Using Heading Too
-- end: show-ui
Output
My UIs
My First UI
Using Heading Too
Mutable Component Arguments
In `ftd`, we can define a component argument as mutable by using the `$` prefix
before its name. A mutable argument can be modified within the component and can
take mutable variables as input, which can be modified outside the component's
scope too. Any changes made to a mutable argument will be reflected in the
component's output.
Consider the following code snippet:
-- component toggle-ui:
caption title:
body description:
boolean $open: true
-- ftd.column:
-- ftd.text: $toggle-ui.title
-- ftd.text: $toggle-ui.description
if: { toggle-ui.open }
-- end: ftd.column
-- end: toggle-ui
In the above example, the `$open` argument is mutable, which means it can be
modified both within and outside the `toggle-ui` component. Any changes made to
the `$open` argument will be immediately reflected in the component's output.
Input
-- toggle-ui: My Title
$open: false
My Description
Output
Input
-- toggle-ui: My Title
My Description
Output
Passing Mutable Variable to Mutable Argument
Consider the following code snippet:
-- boolean $global-open: true
-- ftd.text: I change global-open
$on-click$: $ftd.toggle($a = $global-open)
-- toggle-ui: My Title
$open: $global-open
My Description
We have added an `$on-click$` [event](ftd/event/) here and used `ftd` built-in
function `ftd.toggle`. The function toggles the boolean value whenever event
occurs.
We have passed mutable reference of `global-open` to `open` attribute. So the
change in `global-open` value changes the `open` attribute too.
Click on the `I change global-open` and see the effect.
Events in Component
We have created `toggle-ui` component, now lets add event to this.
-- component toggle-ui:
caption title:
body description:
boolean $open: true
-- ftd.column:
$on-click$: $ftd.toggle($a = $toggle-ui.open)
-- ftd.text: $toggle-ui.title
-- ftd.text: $toggle-ui.description
if: { toggle-ui.open }
-- end: ftd.column
-- end: toggle-ui
We have added an `$on-click$` event and `ftd.toggle` action in `ftd.column`
component and pass `toggle-ui.open` argument.
Click on the rendered component below and see the effect.
Input
-- toggle-ui: Click me!
My Description
Output