Definition
As opposed to a Function , the property order for views doesn't matter, it always produces the same type signature. The only valid return types for a view
are the Primitives .
Copy view SimpleView(age: number, name: string) -> {
<div />
}
// is equivalent to
view AlternateView(name: string, age: number) -> {
<div />
}
// and
type SpreadType {
age: number;
name: string;
}
view SpreadView(...props: SpreadType) -> {
<div />
}
SimpleView.Props; // $Type(age: number, name: string)
AlternateView.Props; // $Type(age: number, name: string)
SpreadView.Props; // SpreadType -> $Type(age: number, name: string)
SimpleView;
// $View<SimpleView.Props, SimpleView()>
// -> $View<$Type(age: number, name: string), SimpleView()>
AlternateView;
// $View<AlternateView.Props, AlternateView()>
// -> $View<$Type(age: number, name: string), AlternateView()>
SpreadView;
// $View<SpreadView.Props, SpreadView()>
// -> $View<SpreadType, SpreadView()>
// -> $View<$Type(age: number, name: string), SpreadView()>
Keys
In order to differentiate elements for more efficient re-rendering, keys can be provided (this might not actually be necessary based on how we build the dependency graphs).
Copy view ListView(items: string[]) -> {
<ul class="list">
{items.map((item, index) => {
<li({index}) class="list__item">
<span>{item}</span>
</li>
}}
</ul>
}
Omit Parentheses
When your view does not accept properties, the parentheses can be omitted.
Copy view SimpleView() -> { /* ... */ } // not preferred
view SimpleView -> { /* ... */ } // preferred
View Composition
Building your view
's with composition in mind allows you to re-use them easily and combine them with other view
's to create complex experiences for your users with ease.
Slots
Composition is achieved by using special <slot>
tags to inject content placed within the containing view
. This allows view
's to be more easily re-used, such as a complex "button" that can have different textual content injected into it.
Copy view WrappingView -> {
<div class="wrapper">
<slot />
</div>
}
view ComposingView -> {
<h1>
<WrappingView>
<span>Inner Content</span>
</WrappingView>
</h1>
}
// generates the following HTML
// <h1>
// <div class="wrapper">
// <span>Inner Content</span>
// </div>
// </h1>
Named Slots
In order to inject multiple different pieces of content, slots can be named and matched to the keys provided on elements placed into the view
.
Copy view ModalView {
<div class="modal">
<h1 class="modal__header">
<slot(header) />
</h1>
<div class="modal__body">
<slot(body) />
<div>
<div class="modal__footer">
<slot(footer) />
<div>
</div>
}
view AlertModal {
<Modal>
<span(header)>This is a modal</span>
<div(body)>
<p>some content...</p>
<p>some more content...</p>
</div>
<div(footer)>
<button>Cancel</button>
<button>OK</button>
</div>
</Modal>
}
// generates the following HTML
// <div class="modal">
// <h1 class="modal__header">
// <span>This is a modal</span>
// </h1>
// <div class="modal__body">
// <div>
// <p>some content...</p>
// <p>some more content...</p>
// </div>
// <div>
// <div class="modal__footer">
// <div>
// <button>Cancel</button>
// <button>OK</button>
// </div>
// <div>
// <div>
// </div>
// </div>
Default Content
Slots can be provided with default content that is replaced if overridden by the containing view
.
Copy view NamedSlotView -> {
<div class="named-slot">
<slot(optional)>
<span>some default content</span>
</slot>
<div>
}
view NonComposingView -> {
<NamedSlotView />
}
view ComposingView -> {
<NamedSlotView>
<h1>overridden content</h1>
</NamedSlotView>
}
// generates the following HTML
// <div class="named-slot">
// <span>some default content</span>
// </div>
// generates the following HTML
// <div class="named-slot">
// <h1>overridden content</h1>
// </div>
Alternate Composition
The above solution might not be so good because it ties you more closely to only being able to use the children
in a very rigid way. This alternative pattern sticks closer to React
's children
.
Slots
The keyword children
is available within any view
to easily access the children placed into it.
Copy view WrappingView -> {
<div class="wrapper">
{children}
</div>
}
view ComposingView -> {
<h1>
<WrappingView>
<span>Inner Content</span>
</WrappingView>
</h1>
}
// generates the following HTML
// <h1>
// <div class="wrapper">
// <span>Inner Content</span>
// </div>
// </h1>
Named Slots
In order to inject multiple different pieces of content, children
can be selected based on their keys.
Copy view ModalView -> {
<div class="modal">
<h1 class="modal__header">
{children.header}
</h1>
<div class="modal__body">
{children.body}
<div>
<div class="modal__footer">
{children.footer}
<div>
</div>
}
view AlertModal -> {
<Modal>
<span(header)>This is a modal</span>
<div(body)>
<p>some content...</p>
<p>some more content...</p>
</div>
<div(footer)>
<button>Cancel</button>
<button>OK</button>
</div>
</Modal>
}
// generates the following HTML
// <div class="modal">
// <h1 class="modal__header">
// <span>This is a modal</span>
// </h1>
// <div class="modal__body">
// <div>
// <p>some content...</p>
// <p>some more content...</p>
// </div>
// <div>
// <div class="modal__footer">
// <div>
// <button>Cancel</button>
// <button>OK</button>
// </div>
// <div>
// </div>
Extend or Implement Other Views
Override or extend the signature and functionality of another view
.
Copy view BaseView(x: string, y: string) -> {
<div>You are the {x} to my {y}</div>
}
Property Type Inheritance
Properties by the same name as the super view
inherit the same types by default.
Copy view CopyView(x, y) : BaseView -> {
x; // string
y; // string
}
Simple Property Transformation
Easily transform properties before applying them to a base view.
Copy view SimpleExtendingView(x, y) : BaseView(x: `x: {x}`, y);
Partially Apply Properties
Properties can be applied during view
declaration. If no properties are passed to the extended view
, the parentheses can be omitted.
Copy view PartialApplicationExtendingView(x, y) : BaseView(x) -> {
super(y: `my value {y}`)
}
// or without partial application
view NoPartialApplicationExtendingView(x, y) : BaseView -> {
let formatted = `my value {y}`;
super(x, y: formatted);
}
Extend and Augment Signature
Override and the property signature of the extended view
.
Copy view AugmentedExtendingView(x, y: number, z: string) : BaseView -> {
let formatted = `my value {y} with {z}`;
super(x, y: formatted);
}
AugmentedExtendingView.Props; // $Type(x: string, y: number, z: string)
Extension as an Interface
Use extend another view
for its signature.
Copy view InterfacingView(x, y) : BaseView -> {
<span>Some other {x} of {y}</span>
}
Nest Super Content
Nest content from super view
inside of more tags.
Copy view NestedExtendingView(x, y) : BaseView(x, y) -> {
<h1 id="nested">
{super()}
</h1>
}
Full view declaration syntax
NOTE: consider an alternative to with
such as the &
character.
Copy view ComplexView(a, b, c) : BaseView(a, b) ~ SomeState, OtherState(), SomeProvider, OtherProvider(c) -> {
}
Create State explicitly (bypass dependency injection)
Ignore grabbing a state from the currect scope by partially or fully constructing one in the declaration of the view.
Copy state MyState(y: string, x: number) -> { }
// force a local state while receiving parameters from a provider / multiple partial application providers
view SomeView ~ MyState() -> { }
view PartialConstructionView ~ MyState#(x: 2) -> { }
view PartialConstructionView ~ MyState('abc') -> { }
Partial Context Application
Copy state ValueProvider($x: number, $y: number) -> { }
view TopLevel -> {
<ValueProvider x={123}>
<div>
<ChildComponent />
</div>
</ValueProvider>
}
view ChildComponent -> {
<ValueProvider y={657}>
<span>
<DependentComponent />
</span>
</ValueProvider>
}
view DependentComponent ~ ValueProvider {
<div>
<span>x: {$x}</span>
<span>y: {$y}</span>
</div>
}