1 | # Rendering
|
2 |
|
3 | To render a Marko view, you need to `require` it.
|
4 |
|
5 | _example.js_
|
6 |
|
7 | ```js
|
8 | var fancyButton = require("./components/fancy-button");
|
9 | ```
|
10 |
|
11 | > **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), [`browserify`](../browserify.md) or [`rollup`](../rollup.md).
|
12 |
|
13 | Once you have a view, you can pass input data and render it:
|
14 |
|
15 | _example.js_
|
16 |
|
17 | ```js
|
18 | var button = require("./components/fancy-button");
|
19 | var html = button.renderToString({ label: "Click me!" });
|
20 |
|
21 | console.log(html);
|
22 | ```
|
23 |
|
24 | The input data becomes available as `input` within a view, so if `fancy-button.marko` looked like this:
|
25 |
|
26 | _./components/fancy-button.marko_
|
27 |
|
28 | ```marko
|
29 | <button>${input.label}</button>
|
30 | ```
|
31 |
|
32 | The output HTML would be:
|
33 |
|
34 | ```html
|
35 | <button>Click me!</button>
|
36 | ```
|
37 |
|
38 | ## Rendering methods
|
39 |
|
40 | 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.
|
41 |
|
42 | Many of these methods return a [`RenderResult`](#renderresult) which is an object with helper methods for working with the rendered output.
|
43 |
|
44 | ### `renderSync(input)`
|
45 |
|
46 | | params | type | description |
|
47 | | ------------ | ------------------------------- | -------------------------------------- |
|
48 | | `input` | `Object` | the input data used to render the view |
|
49 | | return value | [`RenderResult`](#renderresult) | The result of the render |
|
50 |
|
51 | Using `renderSync` forces the render to complete synchronously. If a tag attempts to run asynchronously, an error will be thrown.
|
52 |
|
53 | ```js
|
54 | var view = require("./view"); // Import `./view.marko`
|
55 | var result = view.renderSync({});
|
56 |
|
57 | result.appendTo(document.body);
|
58 | ```
|
59 |
|
60 | ### `render(input)`
|
61 |
|
62 | | params | type | description |
|
63 | | ------------ | -------------------------------- | -------------------------------------- |
|
64 | | `input` | `Object` | the input data used to render the view |
|
65 | | return value | `AsyncStream`/`AsyncVDOMBuilder` | the async `out` render target |
|
66 |
|
67 | 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).
|
68 |
|
69 | ```js
|
70 | var view = require("./view"); // Import `./view.marko`
|
71 | var resultPromise = view.render({});
|
72 |
|
73 | resultPromise.then(result => {
|
74 | result.appendTo(document.body);
|
75 | });
|
76 | ```
|
77 |
|
78 | ### `render(input, callback)`
|
79 |
|
80 | | params | type | description |
|
81 | | -------------- | -------------------------------- | ---------------------------------------------- |
|
82 | | `input` | `Object` | the input data used to render the view |
|
83 | | `callback` | `Function` | a function to call when the render is complete |
|
84 | | callback value | [`RenderResult`](#renderresult) | The result of the render |
|
85 | | return value | `AsyncStream`/`AsyncVDOMBuilder` | the async `out` render target |
|
86 |
|
87 | ```js
|
88 | var view = require("./view"); // Import `./view.marko`
|
89 |
|
90 | view.render({}, (err, result) => {
|
91 | result.appendTo(document.body);
|
92 | });
|
93 | ```
|
94 |
|
95 | ### `render(input, stream)`
|
96 |
|
97 | | params | type | description |
|
98 | | ------------ | -------------------------------- | -------------------------------------- |
|
99 | | `input` | `Object` | the input data used to render the view |
|
100 | | `stream` | `WritableStream` | a writeable stream |
|
101 | | return value | `AsyncStream`/`AsyncVDOMBuilder` | the async `out` render target |
|
102 |
|
103 | The HTML output is written to the passed `stream`.
|
104 |
|
105 | ```js
|
106 | var http = require("http");
|
107 | var view = require("./view"); // Import `./view.marko`
|
108 |
|
109 | http.createServer((req, res) => {
|
110 | res.setHeader("content-type", "text/html");
|
111 | view.render({}, res);
|
112 | });
|
113 | ```
|
114 |
|
115 | ### `render(input, out)`
|
116 |
|
117 | | params | type | description |
|
118 | | ------------ | -------------------------------- | -------------------------------------- |
|
119 | | `input` | `Object` | the input data used to render the view |
|
120 | | `out` | `AsyncStream`/`AsyncVDOMBuilder` | The async `out` to render to |
|
121 | | return value | `AsyncStream`/`AsyncVDOMBuilder` | The `out` that was passed |
|
122 |
|
123 | 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.
|
124 |
|
125 | ```js
|
126 | var view = require("./view"); // Import `./view.marko`
|
127 | var out = view.createOut();
|
128 |
|
129 | view.render({}, out);
|
130 |
|
131 | out.on("finish", () => {
|
132 | console.log(out.getOutput());
|
133 | });
|
134 |
|
135 | out.end();
|
136 | ```
|
137 |
|
138 | ### `renderToString(input)`
|
139 |
|
140 | | params | type | description |
|
141 | | ------------ | -------- | -------------------------------------- |
|
142 | | `input` | `Object` | the input data used to render the view |
|
143 | | return value | `String` | The HTML string produced by the render |
|
144 |
|
145 | Returns an HTML string and forces the render to complete synchronously. If a tag attempts to run asynchronously, an error will be thrown.
|
146 |
|
147 | ```js
|
148 | var view = require("./view"); // Import `./view.marko`
|
149 | var html = view.renderToString({});
|
150 |
|
151 | document.body.innerHTML = html;
|
152 | ```
|
153 |
|
154 | ### `renderToString(input, callback)`
|
155 |
|
156 | | params | type | description |
|
157 | | -------------- | ----------- | -------------------------------------- |
|
158 | | `input` | `Object` | the input data used to render the view |
|
159 | | callback value | `String` | The HTML string produced by the render |
|
160 | | return value | `undefined` | N/A |
|
161 |
|
162 | An HTML string is passed to the callback.
|
163 |
|
164 | ```js
|
165 | var view = require("./view"); // Import `./view.marko`
|
166 |
|
167 | view.renderToString({}, (err, html) => {
|
168 | document.body.innerHTML = html;
|
169 | });
|
170 | ```
|
171 |
|
172 | ### `stream(input)`
|
173 |
|
174 | The `stream` method returns a node.js style stream of the output HTML. This method is available on the server, but is not available by default in the browser. If you need to use streams in the browser, you may `require('marko/stream')` as part of your client-side bundle.
|
175 |
|
176 | ```js
|
177 | var fs = require("fs");
|
178 | var view = require("./view"); // Import `./view.marko`
|
179 | var writeStream = fs.createWriteStream("output.html");
|
180 |
|
181 | view.stream({}).pipe(writeStream);
|
182 | ```
|
183 |
|
184 | ## RenderResult
|
185 |
|
186 | ### `getComponent()`
|
187 |
|
188 | ### `getComponents(selector)`
|
189 |
|
190 | ### `afterInsert(doc)`
|
191 |
|
192 | ### `getNode(doc)`
|
193 |
|
194 | ### `getOutput()`
|
195 |
|
196 | ### `appendTo(targetEl)`
|
197 |
|
198 | ### `insertAfter(targetEl)`
|
199 |
|
200 | ### `insertBefore(targetEl)`
|
201 |
|
202 | ### `prependTo(targetEl)`
|
203 |
|
204 | ### `replace(targetEl)`
|
205 |
|
206 | ### `replaceChildrenOf(targetEl)`
|
207 |
|
208 | ## Global data
|
209 |
|
210 | If you need to make data available globally to all views that are rendered as the result of a call to one of the above render methods, you can pass the data as a `$global` property on the input data object. This object will be removed from `input` and merged into the `out.global` property.
|
211 |
|
212 | ```js
|
213 | view.render({
|
214 | $global: {
|
215 | flags: ["mobile"]
|
216 | }
|
217 | });
|
218 | ```
|
219 |
|
220 | To prevent sensitive data to be accidentally shipped to the browser, by default **none of the keys** in `out.global` is going to be sent to the browser. If you want the data to be serialized and ship to the frontend you need to specify it in `serializedGlobals` inside the `$global` object and they persist across re-renderings.
|
221 | The values need to be serializable.
|
222 |
|
223 | ```js
|
224 | app.get("/", (req, res) => {
|
225 | const ua = req.get("User-Agent");
|
226 | const isIos = !!ua.match(/iPad|iPhone/);
|
227 | const isAndroid = !!ua.match(/Android/);
|
228 |
|
229 | require("./index.marko").render(
|
230 | {
|
231 | $global: {
|
232 | isIos, // isPad is serialized and available on the server and the browser in out.global.isPad
|
233 | isAndroid, // isAndroid is serialized and available on the server and the browser in out.global.isAndroid
|
234 | req, // req is going to be available only server side and will not be serialized because in not present in serializedGlobals below
|
235 |
|
236 | serializedGlobals: {
|
237 | isIos: true, // Tell marko to serialize isIos above
|
238 | isAndroid: true // Tell marko to serialize isAndroid above
|
239 | }
|
240 | }
|
241 | },
|
242 | res
|
243 | );
|
244 | });
|
245 | ```
|
246 |
|
247 | Use `$global` with judgement. It is global and visible in any component.
|
248 |
|
249 | Check [this PR](https://github.com/marko-js/marko/pull/672) for more details.
|