marjoram v0.0.6
Marjoram đż
A lightweight JavaScript library to create and manipulate DOM elements and to manage their state
⨠0 dependencies â¨
⨠Safe from XSS attacks â¨
⨠TypeScript Support â¨
Table of Contents
Getting Started
Installation
npm i marjoram
Implementation
Views can be stateless or stateful when used in conjunction with useViewModel
.
Example:
import { html, useViewModel } from 'marjoram'
const Greeting = () => {
// Create the view's state
const viewModel = useViewModel({ name: 'world đ '})
// Create the view
const view = html`
<div>
<h1>Hello, ${viewModel.$name}</h1>
</div>
`
// Expose the viewModel
view.viewModel = viewModel
return view
}
const greeting = Greeting()
// Appends h1 that reads "Hello, world đ" to DOM
document.body.appendChild(greeting)
// Changes h1 text to "Hello, hotdog đ"
greeting.viewModel.name = "hotdog đ"
API
View
How to create a View
Use the html
tagged template literal to create a View
.
Returns: A View, which is an extention of the DocumentFragment object.
Example:
import { html } from 'marjoram'
const view = html`<h1>Hello, world!</h1>`
How to use refs
Create a ref adding a ref
attribute to any element in your view.
Collect refs using View.prototype.collect()
The collect()
method can be called from any View
instance and enables us to collect elements within our view with a ref
attribute. This is useful for a variety of reasons including to attach event listeners.
Returns
An object containing every HTML Element with a unique ref
attribute.
Example:
const view = html`
<div>
<h1 ref="heading">Welcome Back!</h1>
<h2 ref="subheading">You have 3 notifications</h2>
</div>
`;
const elements = view.collect();
/*
elements = {
heading: h1 Node,
subheading: h2 Node
}
*/
ViewModel
A ViewModel is a stateful representation of the model provided when creating it.
Getting and setting view model properties works the same way it would for any other object in JavaScript.
When a ViewModel
is created, a SchemaProp
will be made for each property and can be accessed by prefixing the property name with $
. Avoid reassigning this value.
How to create a ViewModel
The useViewModel({})
function creates a stateful "view model" object, given an object as an argument.
It is tightly coupled to a html
, such that when a view model property is modified, your view is also modified.
Example:
import { useViewModel } from 'marjoram'
const viewModel = useViewModel({ name: 'Patrick Stewart' })
How to use a ViewModel
âď¸ IMPORTANT
Prefix your view model properties with $
when we are accessing them within a view. This convention allows us to use a view model property more than once within a view.
Learn more about SchemaProps the $
prefix here
Example
import { html, useViewModel } from 'marjoram'
const viewModel = useViewModel({ firstName: 'Patrick', lastName: 'Stewart' })
const view = html`<h1>Hello, ${viewModel.$firstName} ${viewModel.$lastName}!</h1>`
// h1 text: Hello, Patrick Stewart!
viewModel.lastName = "Star"
// h1 text: Hello, Patrick Star!
SchemaProp
When a ViewModel
is created, a SchemaProp
will be made for each property and can be accessed by prefixing the property name with $
Excluding the $
prefix will return the property value as expected rather than the schemaProp.
It is necessary to add the $
prefix to your property name if that property is being used accessed by a View
.
âď¸ IMPORTANT
Avoid reassigning this value if we want to avoid breaking things.
Computed Values
When we call the SchemaProp.prototype.compute(callback)
method on a schemaProp, we can perform some logic on the value that we want to render every time the schemaProp value changes.
Example
const viewModel = useViewModel({ number: 4 });
const { $number } = viewModel
// Create a computed property
const doubledNumber = $number.compute((val) => val * 2)
const view = html`
<p>
${$number} x 2 = ${doubledNumber}
</p>
`;
/* 4 x 2 = 8 */
// Updating the value also updates all related computed properties
viewModel.number = 99
/* 99 x 2 = 198 */
Observe Values
TODO
Examples
Stateless View
const view = html`
<div>
<h1 ref="heading">Welcome Back!</h1>
<h2 ref="subheading">You have 3 notifications</h2>
</div>
`;
const elements = view.collect();
elements.subheading.addEventListener('click', () => {
// Do something
})
Stateful View
const viewModel = useViewModel({text:'Confirm'})
const view = html`
<button ref="btn">${viewModel.$text}</button>
`;
const elements = view.collect();
elements.btn.addEventListener('click', () => {
// Do something
viewModel.text = 'Cancel'
})
Nested Views
const numbers = [1, 2, 3, 4];
const viewModel = useViewModel({numbers});
const listItems = viewModel.numbers.map(
(num, i) =>
html`
<li ref="listItem${i}">
List Item: ${num} +
<span>${num.compute((val) => val + 1)}</span> =
<span>${num.compute((val) => val * 2 + 1)}</span>
</li>
`
)
const view = html`
<ul ref="listEl">
${listItems}
</ul>
`;
/*
List Item: 1 + 2 = 3
List Item: 2 + 3 = 5
List Item: 3 + 4 = 7
List Item: 4 + 5 = 9
*/
Arrays
TODO
More
Glossary
View
A DOM node, specifically a DocumentFragment, that is returned by the html
tagged template literal
Example
const view = html`<h1>Hello âď¸</h1>`
Model
An object with arbitrary values used to populate a ViewModel
Example
const model = { firstName: 'Patrick', lastName: 'Stewart' }
ViewModel
A stateful representation of the model provided and whose properties and are bound to the View
they populate.
Example
const model = { firstName: 'Patrick', lastName: 'Stewart' }
const viewModel = useViewModel(model)
SchemaProp
An individual property within a ViewModel
whose accessor is prefixed with a $
and is bound to the View
it populates.
Example
const model = { firstName: 'Patrick', lastName: 'Stewart' }
const viewModel = useViewModel(model)
// Bind schemaProps to the view with the `$` syntax
const view = html`<h1>Hello, ${viewModel.$firstName} ${viewModel.$lastName}!</h1>`
// Rendered text: Hello, Patrick Stewart!
viewModel.lastName = "Star"
// Rendered text: Hello, Patrick Star!