Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(213)

Side by Side Diff: src/site/articles/dart-web-components/index.markdown

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

Powered by Google App Engine
This is Rietveld 408576698