Skip to content
Azarattum edited this page Nov 23, 2020 · 11 revisions

View component is used to create and style layouts. This component in exclusive for the browser branch.

Views are stored in:

src
└───components
    └───app
        └───views
            └───<name> #Name of a view
                ├───<name>.pug
                ├───<name>.scss
                └───<name>.view.ts

Typically you have at least 3 files for a single view. One for layout, style and component definition. You can omit the first or the second if you do not need them. Feel free to split your layouts and styles in multiple files, just keep in mind that you might want to abstract some logic to a different view instead.

Example

Example view included in the source code:

example.pug: empty

In .pug file we write our layout. Checkout pug documentation to learn more.

example.scss:

view-example {
	///Styles go here
}

Each view creates a custom element called view-<name>. We want our styles to be view-specific, so it is achieved with SASS nesting by using an element selector as a parent.

example.view.ts:

import View from "../../../common/view.abstract";
import Template from "./example.pug";
import "./example.scss";

/**Example view */
export default class Example extends View(Template) {}

This file helps us define the view's name and where to look for .pug and .scss files. Besides, it creates a wrapper around the layout, for it to be used as a component.

Features

Data attributes

Attributes defined as data-<something> are passed to the initial view render as pug locales. So they can be interpolated as variables and even used with data-binding in a controller.

index.pug:

view-article(data-title="Hello" data-content="Loading...")
view-article(data-title="World" data-content="Loading...")

article.pug:

h2=title
p=content

As a result we will have something like this (without all data-binding stuff):

<view-article data-title="Hello" data-content="Loading...">
    <h2>Hello</h2>
    <p>Loading...</p>
</view-article>
<view-article data-title="World" data-content="Loading...">
    <h2>World</h2>
    <p>Loading...</p>
</view-article>

The real content could be fetched later by a controller.

Nested Views

Views can be easily nested inside each other. Views that will have been rendered later, will use a refresh callback on the application. It means that all relations for components (including controllers) will be updated accounting for a newly rendered content.

Do not put a view inside itself! The app will try to create infinitely many views and that is not something you want...

index.pug:

view-article(data-text="Something")

article.pug:

h2 The Article:
view-content(data-content=text)

content.pug:

div Text: #{content}

The approximate result would be:

<view-article data-text="Something">
    <h2>The Article:</h2>
    <view-content data-content="Something">
        <div>Text: Something</div>
    </view-content>
</view-article>

View Databinding Loader

Actually, the real compiled code for the previous example would look like this:

<view-article data-text="Something">
    <h2>The Article:</h2>
    <view-content bind-text="Something" data-content="Something" bind="{&quot;data-content&quot;:&quot;text&quot;}">
        <div>Text: 
            <data-text bind-content="Something" bind="content">Something</data-text>
        </div>
    </view-content>
</view-article>

It happens because view loader creates these placeholders at the compile time. So any data-binding enabled controller can understand which parts of the DOM have to be changed and how. This is much faster that diffing the entire tree on every update. Moreover, binding looks for the elements only once they added then caches them. So finding the parts that have to be changed boils down to a simple look up problem.

- array = [1, 2] //Define the deafult value

each item, key in array
    p=`${item}: ${key}`

A simple pug each loop from above would compile down to a template. Later it will be used by a binding controller to create multiple elements. The default array state is also saved as a loop attribute to be rendered by a controller as soon as possible.

Note that data-dependent loop would not render without any binding-enabled controller. In that case use constant loops instead (more is on controller's page).

<template each bind="array" key="key" value="item" bind-array="[1,2]">
    <p>
        <data-text bind-key bind-item bind="`${item}: ${key}`">: </data-text>
    </p>
</template>