# `application!` and `component!`
Similar to other frameworks, `Simi` core thing is a virtual DOM. Everything that may change in the future will be tracked in the virtual DOM. On every update, the previously rendered values are check against the current value, if it has been changed, both virtual and real DOM will be updated. Everything else that will never change, `Simi` does not track them, instead, `Simi` render them directly to real DOM when the app started. `application!` and `component!` are responsible for building and updating DOMs. Internally, these two macros have the same implementation, working in two different modes (application mode or component mode). They generate code for building/update DOMs from a simi-style HTML template. We have a closer look at these two macros in this section.
## Syntax
Supported HTML element tags are `keyword`s in the content of `application!` and `component!`. It means that you are not allowed to use a local variable name `div`, `span`... to store some value to be use inside the macros.
```rust
let local_value
application! {
// HTML tags
hr // `
`
br // `
`
div (...) // With attribute block
span {...} // With content block
p (...) {...} // With both attribute block and content block
// Anything that is not a supported HTML element tag will be considered a value-expression.
// Its value will be converted to a string and render as a Text node.
local_value
self.some_value
}
```
### Important:
`simi` only works with a set of supported html tags. If `simi` found out that
an identifier is not an html tag, it will automatically treat it as an expression.
If you believe there are valid html tags that must be supported by `simi` please
open an issue (or better: open a pull/merge request).
## Text and child content
```rust
application! {
p { "This is the first paragraph." }
"Some text at the root level"
p {
"This paragraph contains"
code {
"some special text"
}
"in its content. It also contains"
self.get_some_string() // represent as a text node
self.field.some_funtion().count() // also represent as a text node
". Every expression output must have implemented trait ToString"
}
}
```
## Attributes
Attributes must be provided in the form of `name=value` pair.
Rules for `name`:
* An event name: `onclick`, `oninput`...
* An attribute name: `type`, `checked`, `id`, `class`...
* A hyphen-separated-series of identifiers such as `data-my-custom-attribute`.
* A special simi-attribute: a `str` literal like `"class1"`, this is only use as a class name, see more below - in the *Conditional classes* section.
Rules for `value`:
* A literal: `"class-name"`, `"element-id"`...
* A bool literal: `true`, `false`
* A Rust expression: `self.get_name()`
* A `Message` variant: `Msg::SendMessage`, `Msg::SetSomething(some_value)`, `Msg::ButtonClick(?)` (for events only)
```rust
application! {
p (id="first-para" class="class1 class2") {
"..."
}
input (type="checkbox" checked=self.pull_request.is_approved())
```
## Conditional classes
A `str` literal that is used in place of `attribute name` will be treated as a `class`. Its value will be the condition to add/remove itself to/from the real element.
```rust
application! {
div (
// These classes will never change
class="class1 class2"
// These classes will be on/off depend on the result value (true/false) of the expression
"blue-text"=self.is_something_true()
"border"=self.is_something_false()
// Remember: it (attribute name) must be a str literal
"this-must-be-a-str-literal"=other_bool_expression()
)
{
"IMPORTANT: expressions must evaluate to `bool`"
}
}
```
## Events
Messages that are sent back to the `Application::update` method:
```rust
application! {
// This will receive the event argument sended by JS in the place of `?`.
button (onclick=Msg::Clicked(?)) {
"Click me"
}
// This will ignore the event argument.
button (onclick=Msg::SendMessage) {
"Send"
}
// This will also ignore the event argument.
button (onclick=Msg::SetValue(value)) {
"Send"
}
}
```
## `if ... else ...` and `match`
`application!` and `component!` support `if else` and `match`:
```rust
application! {
// if else
if some_expr {
div {...}
p {...}
} else if another_expr {
section {...}
} else {
span {...}
}
// match
match some_value {
value1 => {
p {...}
}
value2 => {
div {...}
}
}
}
```
When rendering a new `if else`/`match` arm (different from the current rendered-arm in DOMs), all nodes (both virtual and real DOM) of the previous rendered-arm will be removed from their DOM, then the new arm will be created. So, you should minimize things in `if else`/`match` construct.
A special case is `if` or `if else if`... (without the final `else`). Simi will create comment node as the content of the omitted `else`. If you want everything in your control, you must create the phantom-element yourself.
## Non-keyed `for` loop
`application!` and `component!` supports `for` loop. Unlike `if else` or `match`, `for` only supports a single item at the root of its body:
```rust
application! {
for value in some_list.iter() {
li { "use the value of " value }
}
}
```
## Keyed `for` loop
Simi does not support keyed `for` loop yet.
## Tell `Simi` not to update things with `#`
`application!` and `component!` try to avoid building a new virtual DOM on update. It will try to update current virtual DOM, then - if it is required, updates the real DOM.
When updating, `Simi` always ignore `literal`s. `Simi` can also ignore updating an attribute, a content or a whole sub DOM if you want.
```rust
application! {
p { "The initial value of count: " #self.count }
// ^
// Prefix the value with `#`, Simi will only create it with the value when the app start. It will not be updated when the value change
// Similarly, for an attribute: `alt` will not be updated
img (src=self.image_url alt=#self.alt_string)
div {
p { ... }
#div { // This `div` and its whole sub will be ignore when updating
p { ... }
span { ... }
}
}
}
```
## View only
Simi does not like to have complex code in its macro. If you need complex code, do it before calling the macros.
```rust
// Complex code here, outside of `application!` or ``component!`
let check_on = match something {
value1 => true,
value2 if some_expr => true,
_ => false,
};
application! {
input (type="checkbox" checked=check_on)
// ^^^^^^^^ Use the result value here
}
```
Or may be better, create a dedicated method for it.
```rust
application! {
input (type="checkbox" checked=self.is_check_on())
// ^^^^^^^^^^^^^^^^^^ Use a dedicated method
}
```