269 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Rendering
To render a Marko view, you need to `import` it.
_example.js_
```js
import FancyButton from "./components/fancy-button.marko";
```
> **Note:** If you are targeting node.js, you will need to enable the [require extension](./installing.md#require-marko-views) in order to require `.marko` files or you will need to precompile all of your templates using [Marko CLI](https://github.com/marko-js/cli). If you are targeting the browser, you will need to use a bundler like [`lasso`](./lasso.md), [`webpack`](./webpack.md) or [`rollup`](./rollup.md).
Once you have a view, you can pass input data and render it:
_example.js_
```js
import FancyButton from "./components/fancy-button.marko";
const html = FancyButton.renderToString({ label: "Click me!" });
console.log(html);
```
The data passed to `renderToString` becomes available as `input` in the component, so if `fancy-button.marko` looked like this:
_./components/fancy-button.marko_
```marko
<button>${input.label}</button>
```
The output HTML would be:
```html
<button>Click me!</button>
```
## Rendering methods
We used the `renderToString` method above to render the view, but there are a number of different method signatures that can be used to render.
Many of these methods return a [`RenderResult`](#renderresult) which is an object with helper methods for working with the rendered output.
### `renderSync(input)`
| params | type | description |
| ------------ | ------------------------------- | -------------------------------------- |
| `input` | `Object` | the input data used to render the view |
| return value | [`RenderResult`](#renderresult) | The result of the render |
Using `renderSync` forces the render to complete synchronously. If a tag attempts to run asynchronously, an error will be thrown.
```js
import View from "./view.marko";
var result = View.renderSync({});
result.appendTo(document.body);
```
### `render(input)`
| params | type | description |
| ------------ | -------------------------------- | -------------------------------------- |
| `input` | `Object` | the input data used to render the view |
| return value | `AsyncStream`/`AsyncVDOMBuilder` | the async `out` render target |
The `render` method returns an async `out` which is used to generate HTML on the server or a virtual DOM in the browser. In either case, the async `out` has a `then` method that follows the Promises/A+ spec, so it can be used as if it were a Promise. This promise resolves to a [`RenderResult`](#renderresult).
```js
import View from "./view.marko";
var resultPromise = View.render({});
resultPromise.then((result) => {
result.appendTo(document.body);
});
```
### `render(input, callback)`
| params | type | description |
| -------------- | -------------------------------- | ---------------------------------------------- |
| `input` | `Object` | the input data used to render the view |
| `callback` | `Function` | a function to call when the render is complete |
| callback value | [`RenderResult`](#renderresult) | The result of the render |
| return value | `AsyncStream`/`AsyncVDOMBuilder` | the async `out` render target |
```js
import View from "./view.marko";
View.render({}, (err, result) => {
result.appendTo(document.body);
});
```
### `render(input, stream)`
| params | type | description |
| ------------ | -------------------------------- | -------------------------------------- |
| `input` | `Object` | the input data used to render the view |
| `stream` | `WritableStream` | a writeable stream |
| return value | `AsyncStream`/`AsyncVDOMBuilder` | the async `out` render target |
The HTML output is written to the passed `stream`.
```js
import http from "http";
import View from "./view.marko";
http.createServer((req, res) => {
res.setHeader("content-type", "text/html");
View.render({}, res);
});
```
### `render(input, out)`
| params | type | description |
| ------------ | -------------------------------- | -------------------------------------- |
| `input` | `Object` | the input data used to render the view |
| `out` | `AsyncStream`/`AsyncVDOMBuilder` | The async `out` to render to |
| return value | `AsyncStream`/`AsyncVDOMBuilder` | The `out` that was passed |
The `render` method also allows passing an existing async `out`. If you do this, `render` will not automatically end the async `out` (this allows rendering a view in the middle of another view). If the async `out` won't be ended by other means, you are responsible for ending it.
```js
import View from "./view.marko";
var out = View.createOut();
View.render({}, out);
out.on("finish", () => {
console.log(out.getOutput());
});
out.end();
```
### `renderToString(input)`
| params | type | description |
| ------------ | -------- | -------------------------------------- |
| `input` | `Object` | the input data used to render the view |
| return value | `String` | The HTML string produced by the render |
Returns an HTML string and forces the render to complete synchronously. If a tag attempts to run asynchronously, an error will be thrown.
```js
import View from "./view.marko";
var html = View.renderToString({});
document.body.innerHTML = html;
```
### `renderToString(input, callback)`
| params | type | description |
| -------------- | ----------- | -------------------------------------- |
| `input` | `Object` | the input data used to render the view |
| callback value | `String` | The HTML string produced by the render |
| return value | `undefined` | N/A |
An HTML string is passed to the callback.
```js
import View from "./view.marko";
View.renderToString({}, (err, html) => {
document.body.innerHTML = html;
});
```
### `stream(input)`
The `stream` method returns a Node.js-style stream of the output HTML.
```js
import fs from "fs";
import View from "./view.marko";
const writeStream = fs.createWriteStream("output.html");
View.stream({}).pipe(writeStream);
```
This method is available on the server, but not available by default in the browser. If you need to use streams in the browser, you may `import 'marko/stream'` as part of your client-side bundle.
## RenderResult
### `getComponent()`
### `getComponents(selector)`
### `afterInsert(doc)`
### `getNode(doc)`
### `getOutput()`
### `appendTo(targetEl)`
### `insertAfter(targetEl)`
### `insertBefore(targetEl)`
### `prependTo(targetEl)`
### `replace(targetEl)`
### `replaceChildrenOf(targetEl)`
## Global data
If you need to make data available to all rendered views, use the `$global` property on the input data object. This property will be removed from `input` and provided to the template through a variable called `$global`. It is also made available on the `out.global` property.
Global values persist across renders.
```js
View.render({
$global: {
flags: ["mobile"],
},
});
```
Within the template you can access `$global` similar to accessing `input`.
```marko
<div>
You are on ${$global.flags.includes("mobile") ? "mobile" : "desktop"}
</div>
```
> **Note:** `$global` is not available within [`static`](./syntax.md#static-javascript) parts of the template. In order to reference `$global` within the component class you must use `out.global` from one of the lifecycle methods that provide it.
> **Warning:** Use `$global` with caution; it is visible in any component.
### Sending global data to browsers
⚠️ To prevent accidentally exposing sensitive data, by default **no keys** in `$global` are sent to browsers. To serialize data to the frontend, name the desired properties in `$global.serializedGlobals`.
Values must be serializable by [the `warp10` module](https://www.npmjs.com/package/warp10).
```js
import Page from "./index.marko";
app.get("/", (req, res) => {
const ua = req.get("User-Agent");
Page.render(
{
$global: {
isIos: /iPad|iPhone/.test(ua), // Serialized and available on the server and browser as `$global.isIos`
isAndroid: /Android/.test(ua), // Serialized and available on the server and browser as `$global.isAndroid`
req, // Only available server-side and not serialized, because its not in `serializedGlobals`
serializedGlobals: {
isIos: true, // Tell Marko to serialize `isIos`
isAndroid: true, // Tell Marko to serialize `isAndroid`
},
},
},
res,
);
});
```
> **Warning:** Ensure that you serialize only data which is necessary for the browser. Serialization is expensive for both server rendering performance and HTML output size.
For details, check [#672: “Serialize only input and state on top-level server-rendered UI components”](https://github.com/marko-js/marko/pull/672).