tutorial: building apps with Déjà Vu
This tutorial covers the fundamentals of Déjà Vu through a social news aggregation app SN, that is a simple clone of Hacker News. In SN, users can submit links, which can then be voted up by other members. Users can also comment on a post or comment and upvote comments. The code of the app can be found in samples/sn.
- including and configuring concepts
- linking components
including and configuring concepts
The process of building a Déjà Vu app begins by navigating the catalog of concepts to find the concepts that provide the functionality you need for your app. The documentation accompanying a concept includes information about the configuration options (e.g., their effect on behavior) and the exported components. Concept components control a patch of the screen, are interactive, and can read and write back-end data. They also have input and output properties.
SN uses the Authentication concept to handle user authentication, Comment to comment on posts and reply to comments, and Scoring twice: for keeping track of upvotes on both posts and on comments separately. It also uses Property to save a post’s author, title, and link—the Property concept gives you a data-model-defining facility for simple CRUD behavior.
The concepts used by the app are specified in the app’s JSON config file (dvconfig.json). The code for SN’s config file is shown below:
usedConcepts object has one key-value pair per concept instance. The key
post on line 6)
determines the name that is going to be used in the HTML to refer to that instance.
The value is another object with two
concept for providing the name of the concept to
property on line 7), and
config for specifying the configuring options
for the concept instance (e.g., the object in lines
If no concept name is provided, the concept instantiated is
the one with name equal to the
instance name. Thus, on line 4,
the concept to be instantiated for
configuration object is given, the default configuration
for that concept is used.
The format of the values of configuration options
is also JSON.
In SN, we only have to configure Property.
Property accepts a configuration variable (
schema) that expects a
JSON Schema to describe the objects it will be
schema to specify the type of properties we expect our
objects (the posts) to have (an author, a title, and a url). The effect of this is that when we
include a component from Property,
the component will allow
the user to input only those fields—author, title, and url.
Moreover, since we specified that
that the format of the
url field is
url (line 14) and that
all fields (author, title, and url) are
required (line 16),
will expect the user to provide a value for each
one and check that the value given for
the url field is
a valid URL. If the user
doesn’t provide a value for each field or
the value for url is invalid,
create-object will show an
In the app’s config file, we also define the
name (line 2) and
routes (lines 23-27) of our app.
maps a URL path to a component.
Any app component can be a page (i.e., accessible
via a URL).
is the component
home (line 24) because
path is empty.
If the user navigates to “/login”, the
will be shown (line 25) and if they navigate to “/item”, the
component will be shown (line 26).
Our template language looks, by design, similar to other template languages. To create an app component, users include components and synchronize them to implement the desired functionality.
App components can contain other components, which can be
concept components (i.e., components defined by concepts) or app components (i.e., components that
are defined as part of the app being developed).
Components are included as if they were HTML
elements, with the tag given by the
concept instance or app name,
followed by the component name.
submit-post includes one app component,
navbar (line 2); two concept components,
create-object of the
post instance of Property
(lines 5-8), and
create-score of the
scoreposts instance of
Scoring (lines 9-10);
and one built-in component,
dv.gen-id (line 4),
which generates a unique ID
can be regarded as free-standing concept components).
Inputs to a component are bound with the syntax
Template expressions can include
literal values, app component inputs,
outputs from other components
on the page, and
syntax of template expressions is
that produce side-effects are allowed.
Components can be fired repeatedly, and the output variables hold the values from the last execution. This is how a selector widget such as a dropdown would typically be connected to another component: the dropdown sets an output property every time it is activated containing the choice the user made, which is then bound to the input variable of components that use that choice.
Some inputs are for customizing
appearance and have no impact on the behavior of the component.
For example, as a result of setting
"Submit" on line 7 of
create-object’s button will carry the label “Submit” instead of the
default button label “Create Post”.
hidden parameter of
Hidden components are not shown to the user, but still run
as if they were visible. Thus
the object data itself is still loaded, emitted as
an output, and used in several parts of the view—the title
and the link are used and shown through lines 5-6, while
the author is displayed on line 11.
App component inputs are preceded with
\$. For example,
has an input named
id that it uses in lines 2, 4, 9, and 12.
Based on this input,
how-object will show the post whose ID matches the given one;
upvote will use the ID as the target of the score if one is created;
show-target will show the score with the given ID;
and clicking on the “comments” link will take
the user to
show-post-details with its
set to the given ID.
To bind entities in different concepts we use a common identifier.
submit-post, for example,
the same ID, generated by
gen-id (line 4), is passed
create-object (line 5) and
create-score (line 9). As a result,
create-score will create a score
with the same target ID as the object
create-object will create.
show-post, we feed
show-object (line 2) and
show-target (line 9). Each of these components loads and
displays its own
view of the post entity; the effect when put together is to
display a SN post object.
Concept components have two actions: an evaluation action (eval) and an execution action (exec). The concept author determines what triggers the evaluation or execution of the component. Typically, the loading of the component itself triggers the evaluation of a component, and some user interaction (e.g., a button click) triggers its execution. What happens on eval or exec is also up to the author of the concept—the only restriction is that an eval action cannot produce a side effect. App components don’t have actions. This is because app components have no back-end functionality of their own—all data and behavior is pushed to concepts.
Eval/exec actions support the conventional user interaction pattern of web apps: data is loaded and displayed, and then the user executes commands to mutate the data. But perhaps concept components could offer arbitrary action types. This would require more work from the user (who would now have to specify what action types are to be coordinated), but could allow more complicated applications to be built, without requiring the modification or creation of a concept.
There are two kinds of app component: a regular component and
a transaction (tx) component.
A regular component
allows any of its children
components to eval/exec without
A tx component, on the other
the eval/exec of the concept components it wraps.
As a result, the eval (or exec) of one concept
component triggers the eval (or exec) of all its
sibling concept components and they either
complete in their entirety (if all succeeded) or
have no effect whatsoever other than displaying an error
message to the user (if one or more aborts).
Instead of putting each component in separate HTML files,
you can wrap elements in another component with the
dv.tx tag to create an
anonymous tx component with content equal to the content of the tag.
the tx is triggered by
create-object (line 5)
when the user clicks on the button.
This is because
the button in the
create-object component of Property causes
the component to execute on click,
is wrapped in a
dv.tx, it will trigger the execution of all its sibling concept components. As a result,
a new post and a new score will be created, bound by
the shared id (the target id of the score is the post id).