OLD | NEW |
---|---|
(Empty) | |
1 --- | |
2 layout: default | |
3 title: "Dart Web Components" | |
4 rel: | |
5 author: siggi-cherem | |
6 description: "A brief introduction to Dart web components." | |
7 has-permalinks: true | |
8 --- | |
9 | |
10 # {{ page.title }} | |
11 _Written by Siggi Cherem<br /> | |
12 October 2012 <br /> | |
13 Updated: October 2012_ | |
14 | |
15 {% comment %} | |
16 | |
17 README --- General notes about editing this file: | |
18 | |
19 * This file is processed by liquid, which evaluates {{ var }} and removes the | |
20 \{\{ and \}\} symbols from the code. Because we use \{\{\}\} in our template | |
21 expressions, we need to escape them in a special way. Unfortunately using | |
22 \{\{\}\} within code makes the backslash show up. There are 2 ways to work | |
23 around this: | |
24 * Doing {{'{{'}}: lyquid evaluates it as a string expression returning \{\{. | |
25 * Using \{\% raw \%\} blocks. | |
26 | |
27 * the codesample blocks are an extension added to jekyll written as 'liquid' | |
28 blocks. The meaning of a block like this: | |
29 \{\% codesample 80 \%\} | |
30 | |
31 text | |
32 \{\% sample 10 20 url \%\} | |
33 \{\% endcodesample \%\} | |
34 is that we'll create a table, the first colum will have sample source code (in | |
35 this case "text"). This code can have a \{\% highlight \%\} section, but other | |
36 than that, no markdown is supported here :(. The column width is specified as | |
37 the first argument of the codesample tag above it (in this example 80 means | |
38 80%). The \{\% sample \%\} tag defines the width, height, and url for an iframe | |
39 that will contain the runnining version of the code sample. | |
40 | |
41 {% endcomment %} | |
42 | |
43 Dart web components provide a templating, data binding, and encapsulation, to | |
44 help you write web applications at scale. An early version is available in the | |
45 [dart-web-components project][dwc]. | |
46 | |
47 Many UI frameworks exist for writing web apps in | |
48 JavaScript -- for example [backbone], [ember.js][ember], and [angularjs][angular ]. | |
49 Recently, the web community has been pushing to include some of the common ideas | |
50 of these frameworks directly into the browser platform. A result has | |
51 been added features like shadow DOM and [web components][wc], and other | |
52 experimental and cool ideas like [model-driven views][mdv] (MDV). | |
53 | |
54 Dart web components combine the ideas of web components and MDV, adapting them | |
55 to work well with Dart. Dart web components take advantage of advanced browser | |
56 features when possible, emulating missing features when necessary. | |
57 | |
58 In this article, we'll walk through some examples that illustrate how to write | |
59 and use Dart web components. Soon we'll be creating a document with a detailed | |
60 specification (link TBR), and another document with instructions on how to use | |
61 the Dart web component tools (link TBR). | |
62 | |
63 ## MDV templates in Dart | |
64 | |
65 Most UI frameworks provide some form of templating to declare views with a | |
66 succinct declarative syntax. Dart web components closely follow the MDV syntax, | |
67 which is basically HTML with small extensions to enable the following features: | |
68 | |
69 * **One-way data binding**: Embed data into your UI | |
70 * **Two-way data binding**: Keep data in sync with UI changes | |
71 * **Events listeners**: Easily attach code that reacts to UI events | |
72 * **Conditionals**: Selectively show parts of the UI | |
73 * **Loops**: Construct lists and tables by iterating over collections | |
74 | |
75 We made some changes to make sure that using Dart with MDV feels natural, but | |
76 otherwise all the concepts from MDV are available here. Let's take a look at | |
77 examples for each of these features. | |
78 | |
79 ### One-way data bindings | |
80 | |
81 You can inject data in your template using `{{'{{'}}expression}}`. The | |
82 example below shows a simple hello world program where the value of the dart | |
Jennifer Messerly
2012/10/10 01:18:29
uppercase Dart?
Siggi Cherem (dart-lang)
2012/10/10 01:50:41
Done.
| |
83 top-level variable `dataValue` is injected in the page automatically. The right | |
84 column shows the app generated from this code running in an iframe. | |
85 | |
86 {% codesample 90 %} | |
87 {% highlight html %} | |
88 {% raw %} | |
89 <html><body> | |
90 <div>Hello {{dataValue}}!</div> | |
91 <script type="application/dart"> | |
92 String dataValue; | |
93 main() { | |
94 var today = new Date.now(); | |
95 dataValue = 'world ${today.year}-${today.month}-${today.day}'; | |
96 } | |
97 </script> | |
98 </body></html> | |
99 {% endraw %} | |
100 {% endhighlight %} | |
101 {% sample 300px 200px http://dart-lang.github.com/dart-web-components/example/ex plainer/helloworld.html.html %} | |
Jennifer Messerly
2012/10/10 01:18:29
meta: include links to the sample code in source c
Siggi Cherem (dart-lang)
2012/10/10 01:50:41
cool. done.
| |
102 {% endcodesample %} | |
103 | |
104 The template expression we just saw above is what we call a **one-way data | |
105 binding**. Like in many other UI libraries and frameworks, we automatically | |
106 watch for changes in your data and ensure the UI is up-to-date whenever the data | |
107 changes. | |
108 | |
109 Dart web components do this by using the `watchers.dart` library (included with | |
Jennifer Messerly
2012/10/10 01:18:29
Should this link to the apidoc for watchers?
(I re
Siggi Cherem (dart-lang)
2012/10/10 01:50:41
good point. I made it a link to the source code fo
| |
110 the dart web components package). You can listen for changes and notify everyone | |
111 when changes might occur. The following example illustrates how you would | |
112 manually use this library: | |
113 | |
114 {% codesample 90 %} | |
115 {% highlight html %} | |
116 {% raw %} | |
117 <html><body> | |
118 <div>Hello counter: {{count}}</div> | |
119 <script type="application/dart"> | |
120 import 'dart:html'; | |
121 import 'package:web_components/watcher.dart' as watchers; | |
122 int count; | |
123 main() { | |
124 count = 0; | |
125 window.setInterval(() { | |
126 count++; | |
127 watchers.dispatch(); | |
128 }, 1000); | |
129 } | |
130 </script> | |
131 </body></html> | |
132 {% endraw %} | |
133 {% endhighlight %} | |
134 {% sample 300px 200px http://dart-lang.github.com/dart-web-components/example/ex plainer/counter.html.html %} | |
135 {% endcodesample %} | |
136 | |
137 The code manually invokes `watchers.dispatch()` to make a change visible to the | |
Jennifer Messerly
2012/10/10 01:18:29
likewise would be cool to link to API docs
Siggi Cherem (dart-lang)
2012/10/10 01:50:41
cool idea.
| |
138 UI. Every now and then it is useful to access watchers directly, but we have | |
139 found that we often don't have to manually write these calls. In particular, | |
140 Dart web components will automatically make the dispatch call when using | |
141 common features like two-way binding and attaching event listeners. | |
142 | |
143 ### Two-way data bindings | |
144 | |
145 Two-way data bindings lets us define that we want a DOM element's value | |
146 (typically an input box or a check box) to be kept in sync with the value of a | |
147 Dart variable. The following example creates a two way binding between `str` and | |
148 `input.value`. We declare this binding by writing the attribute | |
149 `data-bind="value:str"`. | |
150 | |
151 {% codesample 90 %} | |
152 {% highlight html %} | |
153 {% raw %} | |
154 <html><body> | |
155 <div> | |
156 Input: | |
157 <input type="text" data-bind="value:str" placeholder="type something here"> | |
158 <div> Value: {{str}}</div> | |
159 <div> Length: {{str.length}}</div> | |
160 </div> | |
161 <script type="application/dart"> | |
162 String str = ''; | |
163 main() {} | |
164 </script> | |
165 </body></html> | |
166 {% endraw %} | |
167 {% endhighlight %} | |
168 {% sample 300px 200px http://dart-lang.github.com/dart-web-components/example/ex plainer/twoway.html.html %} | |
169 {% endcodesample %} | |
170 | |
171 This is a simple example where Dart web components automatically dispatch | |
172 events for data-model changes. In particular, whenever you update the value of | |
173 the text box, the string and it's length will be updated in other parts of the | |
174 UI. | |
175 | |
176 ### Data-binding expressions | |
177 | |
178 We want to make Dart web components have, to a great extend, feature and design | |
Jennifer Messerly
2012/10/10 01:18:29
typo: "extent" instead of "extend"
Siggi Cherem (dart-lang)
2012/10/10 01:50:41
:), thx
| |
179 parity with MDV. For this reason, we try to match the same syntax of their | |
180 template language where it is appropriate. MDV restricts data-binding | |
Jennifer Messerly
2012/10/10 01:18:29
remove "where it is appropriate"
Siggi Cherem (dart-lang)
2012/10/10 01:50:41
Done.
| |
181 expressions to an expression language containing variables, field dereferences, | |
182 and array accesses. This means that expressions in MDV templates would likely be | |
183 valid Dart and valid JavaScript as well. That also means that advanced Dart expr essions | |
Jennifer Messerly
2012/10/10 01:18:29
not sure we are trying to stay under 80 col? this
Siggi Cherem (dart-lang)
2012/10/10 01:50:41
thx, done.
| |
184 are not valid between `{{'{{'}}expressions}}`, however you can always hide | |
185 complex expressions under a getter in Dart and use the getter property within | |
186 the template. | |
187 | |
188 A full specification of the expression syntax will be available soon in our | |
189 [specification document (link TBR)][]. | |
Jennifer Messerly
2012/10/10 01:18:29
This renders showing the link. Did you mean to do
Siggi Cherem (dart-lang)
2012/10/10 01:50:41
Done.
| |
190 | |
191 ### Conditionals | |
192 | |
193 Template conditionals allow you to selectively activate parts of the UI. We | |
194 write a conditional by wrapping a portion of the UI in a `<template>` tag and | |
195 writing the condition in an attribute of the form `instantiate="if expr"`. For | |
196 instance, the following example only shows `They match!` when both input boxes | |
197 have the same text: | |
198 | |
199 {% codesample 90 %} | |
200 {% highlight html %} | |
201 {% raw %} | |
202 <html><body> | |
203 <div> | |
204 <div> Input1: <input type="text" data-bind="value:str1"></div> | |
205 <div> Input2: <input type="text" data-bind="value:str2"></div> | |
206 <template instantiate="if str1 == str2"> | |
207 <div>They match!</div> | |
208 </template> | |
209 </div> | |
210 <script type="application/dart"> | |
211 String str1 = ''; | |
212 String str2 = ''; | |
213 main() {} | |
214 </script> | |
215 </body></html> | |
216 {% endraw %} | |
217 {% endhighlight %} | |
218 {% sample 300px 200px http://dart-lang.github.com/dart-web-components/example/ex plainer/matchstrings.html.html %} | |
219 {% endcodesample %} | |
220 | |
221 ### Loops | |
222 | |
223 Template loops allow us to iterate over iterable Dart collections. The following | |
224 example shows a fun little app that has search as you type. We use the two-way | |
225 data binding to store the `query` string, then we compute a filtered set of | |
226 results and display it using a looping construct. | |
227 | |
228 {% codesample 90 %} | |
229 {% highlight html %} | |
230 {% raw %} | |
231 <html><body> | |
232 <div> | |
233 <span>Search for something:</span> | |
234 <input type="text" data-bind="value:query"> | |
235 <div> | |
236 <template instantiate='if noMatches'><span>No matches</span></template> | |
237 <template instantiate='if !noMatches'><span>Top results:</span></template> | |
238 </div> | |
239 <div><ul> | |
240 <template iterate='fruit in results'> | |
241 <li>{{fruit}}</li> | |
242 </template> | |
243 </ul></div> | |
244 </div> | |
245 <script type="application/dart"> | |
246 String query = ''; | |
247 List<String> values = const [ 'Apple', 'Apricot', 'Avocado', 'Banana', | |
248 'Blackberry', 'Blackcurrant', 'Blueberry', 'Currant', 'Cherry', | |
249 'Clementine', 'Date', 'Durian', 'Fig', 'Gooseberry', 'Grape', | |
250 'Grapefruit', 'Guava', 'Huckleberry', 'Kiwi', 'Lemon', 'Lime', | |
251 'Lychee', 'Mandarine', 'Mango', 'Cantaloupe', 'Honeydew melon', | |
252 'Nectarine', 'Orange', 'Peach', 'Pear', 'Plum', 'Pineapple', | |
253 'Pomegranate', 'Raspberry', 'Redcurrant', 'Star fruit', 'Strawberry', | |
254 'Tangerine', 'Tomato', 'Watermelon']; | |
255 | |
256 List<String> get results { | |
257 var res = values.filter( | |
258 (v) => v.toLowerCase().contains(query.toLowerCase())); | |
259 if (res.length > 20) { | |
260 res.length = 20; | |
261 res.add('... and many more'); | |
262 } | |
263 return res; | |
264 } | |
265 | |
266 bool get noMatches => results.isEmpty(); | |
267 | |
268 main() {} | |
269 </script> | |
270 </body></html> | |
271 {% endraw %} | |
272 {% endhighlight %} | |
273 {% sample 300px 600px http://dart-lang.github.com/dart-web-components/example/ex plainer/fruitsearch.html.html %} | |
274 {% endcodesample %} | |
275 | |
276 | |
277 ### Listen to UI events | |
278 | |
279 Templates also gives us a succinct way to listen for arbitrary UI events and | |
280 associate those events with Dart code. We do this by using `data-action` | |
281 attributes. Here is an example that listens for click events: | |
282 | |
283 {% codesample 90 %} | |
284 {% highlight html %} | |
285 {% raw %} | |
286 <html><body> | |
287 <div> | |
288 <button data-action="click:increment">Click me</button> | |
289 <span>(click count: {{count}})</span> | |
290 </div> | |
291 <script type="application/dart"> | |
292 int count = 0; | |
293 void increment(e) { count++; } | |
294 main() {} | |
295 </script> | |
296 </body></html> | |
297 {% endraw %} | |
298 {% endhighlight %} | |
299 {% sample 300px 200px http://dart-lang.github.com/dart-web-components/example/ex plainer/clickcount.html.html %} | |
300 {% endcodesample %} | |
301 | |
302 | |
303 ### Templating summary | |
304 | |
305 Templates solve one part of the problem in building web applications: reducing | |
306 the amount of boilerplate code that is used to set up a typical | |
307 model-view-viewmodel architecture. One-way databindings, two-way databindings, | |
Jennifer Messerly
2012/10/10 01:18:29
"databinding" with a space?
"data binding"
Siggi Cherem (dart-lang)
2012/10/10 01:50:41
Done.
| |
308 and data-action listeners, help reduce the need for manually creating controller | |
309 objects that do these bindings by hand. If we combine bindings, conditionals, | |
310 and loops, we have a formula to create simple and sophisticated views in a | |
311 declarative fashion. | |
312 | |
313 However, templating alone is not enough. We need mechanisms to encapsulate and | |
314 abstract views so we can scale to build larger web apps. This is were we need | |
315 web components. | |
316 | |
317 ## Web components in Dart | |
318 | |
319 Web components provide a way to create encapsulated reusable views, which will | |
320 be useful to build medium and large applications. A web component basically | |
321 includes a view, some data, and behavior. In our case, views are described in | |
322 terms of templates, while data and behavior are written directly in Dart. | |
323 | |
324 ### Declaring a component | |
325 | |
326 Web components are declared using a special `<element>` tag. For example, we can | |
327 take the click-count example above and make it a component as follows: | |
328 | |
329 {% highlight html %} | |
330 {% raw %} | |
331 <html><body> | |
332 <element name="x-counter" constructor="CounterComponent" extends="div"> | |
333 <template> | |
334 <div> | |
335 <button data-action="click:increment">Click me</button> | |
336 <span>(click count: {{count}})</span> | |
337 </div> | |
338 </template> | |
339 <script type="application/dart"> | |
340 import 'package:web_components/web_component.dart'; | |
341 | |
342 class CounterComponent extends WebComponent { | |
343 int count = 0; | |
344 void increment(e) { count++; } | |
345 } | |
346 </script> | |
347 </element> | |
348 <!-- more below... --> | |
349 </body></html> | |
350 {% endraw %} | |
351 {% endhighlight %} | |
352 | |
353 The `<element>` tag basically declares a component whose view is defined | |
Jennifer Messerly
2012/10/10 01:18:29
perhaps remove "basically"?
"visual appearance" mi
Siggi Cherem (dart-lang)
2012/10/10 01:50:41
Done.
| |
354 under the child `<template>` tag, and whose behavior code is embedded or sourced | |
355 by the child `<script>` tag. | |
356 | |
357 Components can be thought as if they are extending DOM nodes, their declaration | |
Jennifer Messerly
2012/10/10 01:18:29
"HTML elements" instead of "DOM nodes"?
Siggi Cherem (dart-lang)
2012/10/10 01:50:41
Done.
| |
358 indicates which DOM node they extend from using the `extends` attribute. The | |
359 attribute `constructor="CounterComponent"` indicates that this component's | |
360 behavior is defined in the `CounterComponent` class. Today, this class must | |
361 extend from `WebComponent`, but in the future it will be possible to extend | |
362 directly from the DOM Node that we declared in the `extends` attribute. For | |
363 instance, `class CounterComponent extends DivElement ...`. | |
364 | |
365 The `<element>` tag also declares the component's name with the `name` | |
366 attribute. This name will be used to instantiate this component later on. | |
367 | |
368 ### Instantiating a component | |
369 | |
370 Instantiating a component can be done in two ways: using its name as a tag, e.g. | |
371 `<x-counter></x-counter>` or using setting the `is` attribute on the | |
372 tag that the component extends from. For example, | |
373 | |
374 {% codesample 90 %} | |
375 {% highlight html %} | |
376 {% raw %} | |
377 <html><body> | |
378 <!-- ... element declared as above --> | |
379 <div is="x-counter"></div> | |
380 <script type="application/dart"> | |
381 main(){} | |
382 </script> | |
383 </body></html> | |
384 {% endraw %} | |
385 {% endhighlight %} | |
386 {% sample 300px 200px http://dart-lang.github.com/dart-web-components/example/ex plainer/countcomponent.html.html %} | |
387 {% endcodesample %} | |
388 | |
389 | |
390 ### Passing data to a component | |
391 | |
392 All of the public fields in a component declaration can be initialized directly | |
393 when we instantiate a component. We can do this by writing a special | |
394 `data-value` attribute in the HTML. For instance, the following example | |
395 instantiates two click-counter components (declared just like above), but | |
396 initializes the `count` field of the component to a different value each time. | |
397 | |
398 {% codesample 90 %} | |
399 {% highlight html %} | |
400 {% raw %} | |
401 <html><body> | |
402 <!-- ... element declared as above --> | |
403 <div is="x-counter" data-value="count: myNumber"></div> | |
404 <div is="x-counter" data-value="count: 5"></div> | |
405 <script type="application/dart"> | |
406 int myNumber = 12; | |
407 main(){} | |
408 </script> | |
409 </body></html> | |
410 {% endraw %} | |
411 {% endhighlight %} | |
412 {% sample 300px 200px http://dart-lang.github.com/dart-web-components/example/ex plainer/countcomponent5.html.html %} | |
413 {% endcodesample %} | |
414 | |
415 ### Working at scale | |
416 | |
417 When developing larger web applications, it is really useful to be able to split | |
418 the work and divide pieces of your application in multiple files. It is also | |
419 useful to create reusable components that you embed in several places of your | |
420 application or even across several applications. | |
421 | |
422 Web components allows us to do import other components using `<link | |
Jennifer Messerly
2012/10/10 01:18:29
typo: "to import" instead of "to do import"
Siggi Cherem (dart-lang)
2012/10/10 01:50:41
Done.
| |
423 rel="components">` tags. For example, suppose we created a file | |
424 `clickcounter.html` that contains the declaration of the click-counter component | |
Jennifer Messerly
2012/10/10 01:18:29
wondering if we should name the component "x-click
Siggi Cherem (dart-lang)
2012/10/10 01:50:41
Done.
| |
425 as we had above. Then, our last example could be rewritten as follows: | |
426 | |
427 {% highlight html %} | |
428 {% raw %} | |
429 <html> | |
430 <head> | |
431 <link rel="components" href="clickcounter.html"> | |
432 </head> | |
433 <body> | |
434 <div is="x-counter" data-value="count: myNumber"></div> | |
435 <div is="x-counter" data-value="count: 5"></div> | |
436 <script type="application/dart"> | |
437 int myNumber = 12; | |
438 main(){} | |
439 </script> | |
440 </body> | |
441 </html> | |
442 {% endraw %} | |
443 {% endhighlight %} | |
444 | |
445 ### Upcoming features | |
446 | |
447 We are still adding features and making changes to Dart web components. Here are | |
448 some of the features we are working on: | |
449 | |
450 * Support for sub-components. Using a special `<content>` tag, a component | |
451 can embed nodes that will be provided when instantiating it. For instance, | |
452 you could write a component that puts a frame around an image as follows: | |
453 | |
454 {% highlight html %} | |
455 {% raw %} | |
456 <html> | |
457 <body> | |
458 <element name="x-myframe"> | |
459 <template> | |
460 <!-- some complex wrapping happens here ... --> | |
461 <div class="framestyle">...<content></content>...</div> | |
462 </template> | |
463 </element> | |
464 | |
465 <div is="x-myframe"> | |
466 <!-- img will be included where we wrote <content> above --> | |
467 <img src="..."> | |
468 </div> | |
469 </body> | |
470 </html> | |
471 {% endraw %} | |
472 {% endhighlight %} | |
473 | |
474 * Alternative MDV conditional and listing constructs. Instead of using a | |
475 `<template>` tags, you will be able to use `instantiate` and | |
476 `iterate` attributes directly on any element. This is important because in | |
477 some parts of the HTML is not valid to write `<template>` tags. For example, | |
478 this feature will make it possible to iterate on table rows and cells. | |
479 | |
480 ## Developing and using Dart web components | |
481 | |
482 We are making several tools available to make it easy to create and deploy | |
483 projects that use dart web components. We have a compiler tool that can be run | |
484 offline to generate efficient code for your components. The generated code will | |
485 try to use the native features available in browsers, but otherwise it will | |
486 resort to other techniques to ensure that the semantics of dart web components | |
487 are the same everywhere. | |
488 | |
489 We have added integration with the Dart editor, so that changes to source files | |
490 can be compiled automatically in the background. We are also developing an | |
491 extension for dartium that will be able to compile components on the fly as | |
492 you load them. Both the editor and dartium integration will provide you with a | |
493 smooth edit/refresh cycle. | |
494 | |
495 More details about our tools will be available soon [(link TBR)][]. | |
496 | |
497 ## Exploring more | |
498 | |
499 There are many resources to explore and learn more about Dart web components. | |
500 Check out our Dartisans episode on this topic: | |
501 | |
502 <iframe width="560" height="315" src="http://www.youtube.com/embed/zUdQkSwslzc" frameborder="0" allowfullscreen></iframe> | |
503 | |
504 You can also follow the project in [github][dwc], track and file bugs in our | |
Jennifer Messerly
2012/10/10 01:18:29
think this should be "GitHub" to match how they wr
Siggi Cherem (dart-lang)
2012/10/10 01:50:41
Done.
| |
505 [issue tracker][bugs], and play with other examples in our repo, like our port | |
506 of [todomvc][todomvcindwc]. | |
Jennifer Messerly
2012/10/10 01:18:29
likewise, I think this should be "TodoMVC" to matc
Siggi Cherem (dart-lang)
2012/10/10 01:50:41
Done.
| |
507 | |
508 If you have any questions, feel free to send us email directly at | |
509 [misc@dartlang.org](mailto:misc@dartlang.org). | |
510 | |
511 | |
512 [wc]: http://dvcs.w3.org/hg/webcomponents/raw-file/tip/explainer/index.html | |
513 [mdv]: http://code.google.com/p/mdv/ | |
514 [dwc]: https://github.com/dart-lang/dart-web-components/ | |
515 [bugs]: https://github.com/dart-lang/dart-web-components/issues | |
516 [backbone]: http://backbonejs.org/ | |
517 [angular]: http://angularjs.org/ | |
518 [ember]: http://emberjs.com/ | |
519 [todomvcindwc]: https://github.com/dart-lang/dart-web-components/tree/master/exa mple/todomvc | |
OLD | NEW |