Intro
Hey! I'm Rudy Alula, and I work as Front-end Developer for Stanley Robotics, which is solding the first outdoor robot parking vallet in automobile industry.
with this article, you're about to learn more on Ogone but first you need to understand what's happening and to README
Those who are familiar with Front-End tools will recognize some of the next features.
Installation
First you need to download Deno. And then run: deno --version
at this moment Ogone is built under deno 1.1.3
so please deno upgrade
if your version is under.
for this tutorial we will follow this architecture:
- components
|__root-component.o3
|__greeting-component.o3
- deps.ts
- mod.ts
this is the moment to load Ogone. in your deps.ts
start by importing Ogone:
// ./deps.ts
export { default as o3 } from 'https://x.nest.land/Ogone@0.18.0-rc.0-4/mod.ts';
// ./mod.ts
import { o3 } from './deps.ts';
o3.run({
entrypoint: '/root-component.o3',
modules: '/modules',
port: 8080, // dev port
});
this file will configure the compilation of your application.
Getting Started
In the root-component
, import another component like this with the use ... as
statement.
// ./components/root-component.o3
use ./greeting-component.o3 as 'greeting-component';
<greeting-component />
let's see what we can do in the greeting-component.
// ./components/greeting-component.o3
<p>Hello World</p>
write the hello world inside an element
unwrapped textnodes throw an error
yup ok, not so cool but this is like every tools has to present a Hello world example.
Require data from the parent component
hmm, let's say Hello Deno instead but through the root-component. open your greeting-component.o3
and start using the require ... as
statement.
// ./components/greeting-component.o3
require name as String;
<p>Hello ${name}!</p>
As you can see the first part of the component is the configuration of the component. you can see the first statement require <property> as <constructors>
if empty, name will throw an error. so you need to provide the Deno to the greeting-component.
// ./components/root-component.o3
use ./greeting-component.o3 as 'greeting-component';
<greeting-component :name="'Deno'" />
List of name
now you have seen two statements use ... as
and require ... as
.
what if our component has to present a list of random names ?
First, define the names, by using protocol element and def
statement, follow this example:
// ./components/greeting-component.o3
require name as String;
<proto>
def:
people:
- Mary Grey
- Sam Lee
- Christopher Van Sperso
index: 0
</proto>
<p>Hello ${name}!</p>
here to understand the protocol element.
So we got now an index and a list, but nothing change. hmm let's use the mounted/onMounted feature of Ogone, we will use setInterval to see all the list.
inside the protocol element, use default
statement like following:
// ./components/greeting-component.o3
require name as String;
<proto>
def:
people:
- Mary Grey
- Sam Lee
- Christopher Van Sperso
index: 0
actual: null
default:
setInterval(() => {
this.actual = this.people[this.index];
this.index++;
if (this.index > this.people.length) this.index = 0;
}, 1000);
</proto>
<p>Hello ${actual || name}!</p>
default
comes from the switch statement. It's the initialization case used by Ogone. this default
statement is used when the component is mounted.
Now that the presentations are made, we have one problem. one loop is running until the user exit the window.
let's save this interval.
// ./components/greeting-component.o3
require name as String;
<proto>
def:
people:
- Mary Grey
- Sam Lee
- Christopher Van Sperso
index: 0
actual: null
interval: null
case 'destroy':
clearInterval(this.interval);
break;
default:
this.interval = setInterval(() => {
this.actual = this.people[this.index];
this.index++;
if (this.index > this.people.length) this.index = 0;
}, 1000);
</proto>
<p>Hello ${actual || name}!</p>
as you can see all properties has to be defined in
def
setting an property during the runtime will throw an error
ok that's better like this. you can spot the case case 'destroy'
. this case is selected when the component is removed.
what if I want to use user events instead of an interval ?
You can use the flag --click
. to test it, write a button element, set the flag attribute '--click:caseName', this will say to Ogone, select this case when there is a click.
example:
<proto>
case 'click:caseName':
// do things
break;
</proto>
<button --click:caseName />
apply this inside the greeting-component. btw you can also use before-each
statement:
// ./components/greeting-component.o3
require name as String;
<proto>
def:
people:
- Mary Grey
- Sam Lee
- Christopher Van Sperso
index: 0
actual: null
before-each:
const update = () => {
this.actual = this.people[this.index];
if (this.index > this.people.length) this.index = 0;
};
case 'click:prev':
this.index--;
update();
break;
case 'click:next':
this.index++;
update();
break;
</proto>
<p>Hello ${actual || name}!</p>
<button --click:prev>-</button>
<button --click:next>+</button>
before-each
is selected before all cases, including the default case. this allows you to define vars that you can use in every cases.
use Reflections
Ogone comes out with few new features. one of those is the reflection. this act like a computed: {...}
in Vue or the $:
in Svelte, for those who knows.
the syntax this.reflect => value;
or
this.reflect => {
// do things
return value;
}
how can I use it inside the greeting-component ?
good question. you can write it under the before-each
statement like this:
// ./components/greeting-component.o3
require name as String;
<proto>
def:
people:
- Mary Grey
- Sam Lee
- Christopher Van Sperso
index: 0
actual: null
before-each:
this.actual => {
if (this.index > this.people.length) this.index = 0;
return this.people[this.index];
};
case 'click:prev': this.index--; break;
case 'click:next': this.index++; break;
</proto>
<p>Hello ${actual || name}!</p>
<button --click:prev>-</button>
<button --click:next>+</button>
reflections outside the
before-each
statement are not parsed
For and If flags
what if I want to see all the names ?
there is the flag --for="array as (value, key)"
where array is a property or a new array.
erase the useless statements. and insert the paragraph element with the flag.
// ./components/greeting-component.o3
require name as String;
<proto>
def:
people:
- Mary Grey
- Sam Lee
- Christopher Van Sperso
</proto>
<p --for="people as (person)">Hello ${person || name}!</p>
I don't want to see Mary Grey in the list, what can I do ?
there is two solutions. using .filter()
inside the flag --for
// ./components/greeting-component.o3
require name as String;
<proto>
def:
people:
- Mary Grey
- Sam Lee
- Christopher Van Sperso
</proto>
<p --for="people.filter(n => n!== 'Mary Grey') as (person)">Hello ${person || name}!</p>
or using the --if
flag:
// ./components/greeting-component.o3
require name as String;
<proto>
def:
people:
- Mary Grey
- Sam Lee
- Christopher Van Sperso
</proto>
<p --for="people as (person)" --if="person !== 'Mary Grey'">Hello ${person || name}!</p>
sometimes you will have a long list of name to write, this is annoying inside a component that we want to keep small. Ogone lets you download ressources inside the protocol. assuming that we have a file names.yaml
inside a folder sources
.
// ./components/greeting-component.o3
require name as String;
<proto def="./sources/names.yaml" />
<p --for="people as (person)" --if="person !== 'Mary Grey'">Hello ${person || name}!</p>
inside ./sources/names.yaml
people:
- Mary Grey
- Sam Lee
- Christopher Van Sperso
Store
- components
|__stores
|__names.stores.o3
|__root-component.o3
|__greeting-component.o3
- deps.ts
- mod.ts
in Ogone, everything is a component. To create a store, start by creating a names.store.o3
file.
names is for our example and '.store' is not required.
this component will looks like this:
// ./components/stores/names.store.o3
<proto type="store" namespace="names">
def:
people:
- Mary Grey
- Sam Lee
- Christopher Van Sperso
</proto>
inject this store component inside your greeting-component
// ./components/greeting-component.o3
use ./stores/names.store.o3 as 'store-names';
require name as String;
<store-names namespace="names" />
<proto>
def:
people: []
</proto>
<p --for="people as (person)" --if="person !== 'Mary Grey'">Hello ${person || name}!</p>
the type of the component has to start the name of the component, if it's not starting you will get an error
how can I add one name to the store
you can use the object Store
, that you can only access if the component uses a store component.
First create an action inside your store component:
// ./components/stores/names.store.o3
<proto type="store" namespace="names">
def:
people:
- Mary Grey
- Sam Lee
- Christopher Van Sperso
case 'action:setName':
this.people.push(ctx.name);
break;
</proto>
all actions case have to start with 'action:'
now inside the component use the Store
object. like following:
// ./components/greeting-component.o3
use ./stores/names.store.o3 as 'store-names';
require name as String;
<store-names namespace="names" />
<proto>
def:
people: []
default:
Store.dispatch('names/setName', {name: 'Ogone is Cool'});
</proto>
<p --for="people as (person)" --if="person !== 'Mary Grey'">Hello ${person || name}!</p>
now if you want to update this name, start writing a mutation inside the store:
// ./components/stores/names.store.o3
<proto type="store" namespace="names">
def:
people:
- Mary Grey
- Sam Lee
- Christopher Van Sperso
case 'action:setName':
this.people.push(ctx.name);
break;
case 'mutation:UPDATE_NAME':
this.people[ctx.id] = ctx.name;
break;
</proto>
as the actions, the mutations have to start with 'mutation:'
Now, use it inside the component. For this, you need to use the flag --bind
on an input. like <input --bind="property" />
but in our greeting-component we are going to use the property name.
// ./components/greeting-component.o3
use ./stores/names.store.o3 as 'store-names';
require name as String;
<store-names namespace="names" />
<proto>
def:
people: []
name: Ogone
case 'update:name':
Store.commit('names/UPDATE_NAME', {
id: this.people.length -1,
name: this.name,
});
break;
default:
Store.dispatch('names/setName', {name: 'Ogone is Cool'});
</proto>
<p --for="people as (person)" --if="person !== 'Mary Grey'">Hello ${person || name}!</p>
<input --bind="name" />
Conclusion
with this article you learnt few features from Ogone:
use ... as 'component-name'
require <props> as <constructors>
<proto [type="store"[ >
def: <yaml>
default:
before-each:
case 'destroy'
case 'update:property'
--click:...
--for="array as (item, key)"
--if
--bind="property"
Store.dispatch('namespace/action')
Store.commit('namespace/mutation')
that's a good list for a start.
keep playing with it and mind that Ogone is still open for Pull requests or issues, and it still under development.
Top comments (0)