Basic components
Components are the building blocks of avalanche apps.
They're represented as functions since they're fundamentally meant to be pure:
they take in inputs and describe a UI as their output. They're so fundamental that
this book is largely a component tutorial.
Creating components
Though components are written as functions, avalanche implements some quality-of-life and performance features
not possible with functions. This requires the use of specialized syntax.
If we call a component that takes no parameters called Comp, for example, we'll call
it with Comp(self). These component calls only work within the bodies of other components, and they return the
View type.
Passing parameters to components
Most components have parameters, and they're named. For example, in the components below, we pass href by name. However, the last
parameter in a function definition can be supplied without a name as the last parameter in a call.
For Text, that's the text parameter, which takes something convertible to a Cow<'_, str>,
meaning you can pass it either a String or a &str.
For every other avalanche_web component, the unnamed last parameter is children, which takes an Into<Vec<View>>.
Below are two versions of a link component. One passes text and children explicitly, while the other does it implicitly, as is idiomatic.
#![allow(unused)] fn main() { use avalanche::{component, View}; use avalanche_web::components::{A, Text}; const FIRST_SITE: &str = "http://info.cern.ch/hypertext/WWW/TheProject.html"; #[component] fn LinkExplicit() -> View { A( self, href = FIRST_SITE, children = [ Text(self, text = "The first website") ] ) } #[component] fn LinkImplicit() -> View { A( self, href = FIRST_SITE, [ Text(self, "The first website") ] ) } }
For avalanche_web components, it's generally recommended to use implicit last parameters, so we'll be using them from now on in this tutorial.
In third-party components, by convention, you should only use them if there is only one
parameter, like in Text, or if the last parameter is a child or children of the component.
Receiving parameters and tracking
So far, we've dealt only with components with hard-coded data. However, within most practical components, we want to allow other components to pass them data.
To enable that, you simply need to add parameters to your component function. Here, let's say we want a reusable Link with its own custom functionality,
and want to allow a custom destination and text to be specified. We can add to and text parameters:
#![allow(unused)] fn main() { use avalanche::{component, tracked, View}; use avalanche_web::components::{A, Text}; #[component] fn Link(to: &str, text: &str) -> View { A( self, href = tracked!(to), [ Text(self, tracked!(text)) ] ) } }
Notice that for the href and implicit text parameters, we pass tracked! parameters instead of just passing them by value.
That's because when we say a parameter is, for example, a &str, we actually receive a Tracked<&str>. A Tracked value
wraps an inner value with data on whether it's been updated since last render. avalanche uses this to allow for efficient re-rendering
of components. Calling tracked! on a Tracked value gives us its inner value. Since Text and A expect &str-like values, but
the to and text parameters are Tracked<&str>, we use tracked!() to get &str values.
We can then construct Link inside of other components:
#![allow(unused)] fn main() { use avalanche::{component, tracked, View}; use avalanche_web::components::{Text, Div}; use avalanche_web::components::A; #[component] fn Link(to: &str, text: &str) -> View { A( self, href = tracked!(to), [ Text(self, tracked!(text)) ] ) } #[component] fn Example() -> View { Div(self, [ Link( self, to = "https://example.com", text = "example.com" ), Text(self, " is a domain reserved for use in demos.") ]) } }
When rendered, the above is equivalent to the HTML below.
<div>
<a href="https://example.com">
example.com
</a>
is a domain reserved for use in demos.
</div>
Within component calls, parameter order does not matter (except for what the implict last parameter is),
so we could've also called Link with to and text swapped:
Link(
self,
text = "example.com".into(),
to = "https://example.com".into()
)
Parameter type restrictions
All parameters must implement Clone. Note that all non-mut references implement Clone, making this restriction
generally easy to deal with in practice.