Skip to content

Commit c57ea4b

Browse files
committed
Merge pull request #27 from cleandart/update_examples
Updated examples
2 parents 961f7f4 + 46ad4aa commit c57ea4b

11 files changed

+370
-1
lines changed

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,12 @@ myComponent({"text":"Somehting"})
9898

9999
```dart
100100
typedef MyComponentType({String headline, String text});
101+
102+
var _myComponent = registerComponent(() => new MyComponent())
103+
101104
MyComponentType myComponent = ({headline, text}) =>
102-
registerComponent(() => new MyComponent())({'headline':headline, 'text':text});
105+
_myComponent({'headline':headline, 'text':text});
106+
103107
class MyComponent extends Component {
104108
get headline => props['headline'];
105109
get text => props['text'];
@@ -119,6 +123,10 @@ void main() {
119123
}
120124
```
121125

126+
## Geocodes Example
127+
128+
For more robust example take a look at [example/geocodes/geocodes.dart]().
129+
122130
## Life-cycle methods of a component
123131

124132
These are quite similar to React life-cycle methods, so refer to React tutorial for further

example/geocodes/geocodes.dart

Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
import "package:react/react.dart" as react;
2+
import "package:react/react_client.dart";
3+
import "dart:html";
4+
import "dart:convert";
5+
import "dart:async";
6+
7+
/*
8+
* Hello,
9+
*
10+
* this is a part of the tutorial to the react-dart package. We'll go through
11+
* a simple app that is quering Google Maps API and showing the result to the
12+
* user. It also stores the search history and allows to reload past queries.
13+
*
14+
* In this file you'll find the structure and the logic of the app.
15+
* There is also a `geocodes.html` file, that contains the mountpoint.
16+
*
17+
* Be sure that you understand the basic concepts of
18+
* [React](http://facebook.github.io/react/) before reading this tutorial.
19+
*
20+
* Enjoy!
21+
*/
22+
23+
24+
/*
25+
* Divide your app to the components and conquer!
26+
* There is a first custom component. It is just a table row showing
27+
* one item of the response to the user.
28+
*/
29+
class _GeocodesResultItem extends react.Component {
30+
31+
/*
32+
* The only function you must implement in the custom component is `render`.
33+
* It just returns the the structure of the other components.
34+
*
35+
* Every component has a map of properties called `props`. It can be
36+
* specified during creation.
37+
*/
38+
render() {
39+
return react.tr({}, [
40+
react.td({}, props['lat']),
41+
react.td({}, props['lng']),
42+
react.td({}, props['formatted'])
43+
]);
44+
}
45+
}
46+
47+
/*
48+
* Now we need to tell React that there exists our custom component.
49+
* As a reward, it gives us a function, that takes the properties
50+
* and returns our element. You'll see it in action shortly.
51+
* It is the only correct way to create your component. Do not use the
52+
* constructor!
53+
*/
54+
var geocodesResultItem =
55+
react.registerComponent(() => new _GeocodesResultItem());
56+
57+
58+
/*
59+
* In this component we'll
60+
*/
61+
class _GeocodesResultList extends react.Component {
62+
63+
render() {
64+
/*
65+
* Built-in components have also properties.
66+
* They correspond to the html props.
67+
*/
68+
return react.div({'id': 'results'}, [
69+
react.h2({}, "Results:"),
70+
/*
71+
* However, `class` is a keyword in javascript, therefore
72+
* `className` is used instead
73+
*/
74+
react.table({'className': 'table'}, [
75+
react.thead({}, [
76+
react.th({}, 'Latitude'),
77+
react.th({}, 'Longitude'),
78+
react.th({}, 'Address')
79+
]),
80+
react.tbody({},
81+
/*
82+
* The second argument contains the body of the component
83+
* (as you have already seen). It can be a string,
84+
* a component or an iterable.
85+
*/
86+
props['data'].map(
87+
// Usecase for our custom component.
88+
(item) => geocodesResultItem({
89+
'lat': item['geometry']['location']['lat'],
90+
'lng': item['geometry']['location']['lng'],
91+
'formatted': item['formatted_address']
92+
})
93+
)
94+
)
95+
])
96+
]);
97+
}
98+
}
99+
100+
var geocodesResultList =
101+
react.registerComponent(() => new _GeocodesResultList());
102+
103+
104+
/*
105+
* On the search form is ilustrated that:
106+
* - the functions can be component parameters (handy for callbacks)
107+
* - the DOM elements can accessed using refs.
108+
*/
109+
class _GeocodesForm extends react.Component {
110+
111+
render() {
112+
return react.div({}, [
113+
react.h2({}, "Search"),
114+
// Component function is passed as callback
115+
react.form({'onSubmit': onFormSubmit}, [
116+
react.input({
117+
'type': 'text',
118+
'placeholder': 'Enter address',
119+
// Input is referenced to access it's value
120+
'ref': 'addressInput'
121+
}),
122+
react.input({'type': 'submit'}),
123+
])
124+
]);
125+
}
126+
127+
// This is called when form is submited
128+
onFormSubmit(e) {
129+
e.preventDefault();
130+
// The input's value is accessed.
131+
var addr = ref('addressInput').value;
132+
ref('addressInput').value = "";
133+
// And the callback from the parent element is called.
134+
// (Yes, you haven't seen it yet.)
135+
props['submiter'](addr);
136+
}
137+
}
138+
139+
var geocodesForm = react.registerComponent(() => new _GeocodesForm());
140+
141+
/*
142+
* Nothing new here. Item in history list.
143+
*/
144+
class _GeocodesHistoryItem extends react.Component {
145+
146+
reload(e) {
147+
props['reloader'](props['query']);
148+
}
149+
150+
render() {
151+
return react.li({}, [
152+
react.button({'onClick': reload}, 'Reload'),
153+
" (${props['status']}) ${props['query']}"
154+
]);
155+
}
156+
}
157+
158+
var geocodesHistoryItem =
159+
react.registerComponent(() => new _GeocodesHistoryItem());
160+
161+
162+
/*
163+
* And the whole history list. Note, that it just
164+
* passes the callback from the parent.
165+
*/
166+
class _GeocodesHistoryList extends react.Component {
167+
168+
render() {
169+
return react.div({}, [
170+
react.h3({}, "History:"),
171+
react.ul({},
172+
new List.from(props['data'].keys.map(
173+
(key) => geocodesHistoryItem({
174+
'key': key,
175+
'query': props['data'][key]["query"],
176+
'status': props['data'][key]["status"],
177+
'reloader': props['reloader']
178+
})
179+
)).reversed
180+
)
181+
]);
182+
}
183+
}
184+
185+
var geocodesHistoryList =
186+
react.registerComponent(() => new _GeocodesHistoryList());
187+
188+
189+
/*
190+
* The core component of the App.
191+
*
192+
* Introduces the state. State and the properties are the two places to store
193+
* the component's data. However they differ in the use:
194+
* - the properties contain data dictated by the parent component
195+
* - the state is an internal storage of the component that can't
196+
* be accessed by the parent. When the state is changed,
197+
* the whole component is repainted.
198+
*
199+
* It's a common practice to store the aplication data in the state of the
200+
* root component. It will redpaint every time, the state is changed. However,
201+
* it is not required - you can use normal variables and repaint manualy.
202+
*
203+
* When the request is sent, it has `pending` status in the history.
204+
* This changes to `OK` or `error` when the answer (or timeout) comes.
205+
* If the new request is sent meanwhile, the old one is `canceled`.
206+
*/
207+
class _GeocodesApp extends react.Component {
208+
209+
getInitialState() => {
210+
'shown_addresses': [], // Data from last query.
211+
'history': {} // Map of past queries.
212+
};
213+
214+
var last_id = 0; // The id of the last query.
215+
216+
/*
217+
* Sends the query to the API and processes the result
218+
*/
219+
newQuery(String addr) {
220+
221+
/*
222+
* Once the query is being sent, it appears in the history
223+
* and is given an id.
224+
*/
225+
var id = addQueryToHistory(addr);
226+
227+
// Prepare the URL
228+
addr = Uri.encodeQueryComponent(addr);
229+
var path =
230+
'https://maps.googleapis.com/maps/api/geocode/json?address=$addr';
231+
232+
// Send the request
233+
HttpRequest.getString(path)
234+
.then((value) =>
235+
// Delay the answer 2 more seconds, for the test purposes
236+
new Future.delayed(new Duration(seconds:2), ()=>value)
237+
)
238+
.then((String raw) {
239+
// Is this the answer to the last request?
240+
if(id == last_id){
241+
// If yes, query was `OK` and `shown_adresses` are replaced
242+
state['history'][id]['status']='OK';
243+
var data = JSON.decode(raw);
244+
/*
245+
* Calling `setState` will update the state and then
246+
* repaint the component.
247+
*
248+
* In theory, state should be considered as immutable
249+
* and `setState` or `replaceState` should be the only way
250+
* to change it.
251+
*
252+
* It is possible to do this, when the whole state value is parsed
253+
* from the server response (the case of `shown_addresses`).
254+
* However, it would be inefficient to copy whole `history` just to
255+
* change one item. Therefore we mutate it and then
256+
* replace it by itself.
257+
*
258+
* Have a look at vacuum_persistent package to achieve
259+
* the immutability of state.
260+
*/
261+
setState({
262+
'shown_addresses': data['results'],
263+
'history': state['history']
264+
});
265+
} else {
266+
// Otherwise, query was `canceled`
267+
state['history'][id]['status']='canceled';
268+
setState({'history': state['history']});
269+
}
270+
})
271+
.catchError((Error error) {
272+
state['history'][id]['status']='error';
273+
setState({'history': state['history']});
274+
});
275+
}
276+
277+
/*
278+
* Add a new query to the history with the `pending` state,
279+
* and return it's id.
280+
*/
281+
addQueryToHistory(String query) {
282+
var id = ++last_id;
283+
state['history'][id] = {
284+
"query": query,
285+
"status": "pending"
286+
};
287+
setState({'history': state['history']});
288+
return id;
289+
}
290+
291+
render() {
292+
return react.div({}, [
293+
react.h1({}, "Geocode resolver"),
294+
geocodesResultList({
295+
// The state values are passed to the children as the properties.
296+
'data': state['shown_addresses']
297+
}),
298+
geocodesForm({
299+
// `newQuery` is the final callback of the button presses.
300+
'submiter': newQuery
301+
}),
302+
geocodesHistoryList({
303+
'data': state['history'],
304+
// The same here.
305+
'reloader': newQuery
306+
})
307+
]);
308+
}
309+
}
310+
311+
var geocodesApp = react.registerComponent(() => new _GeocodesApp());
312+
313+
/*
314+
* And, finally, few magic commands to make it work!
315+
*
316+
* Select the root of the app and the place, where it lives.
317+
*/
318+
void main() {
319+
setClientConfiguration();
320+
react.render(geocodesApp({}), querySelector('#content'));
321+
}

example/geocodes/geocodes.html

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<!DOCTYPE html>
2+
3+
<!--
4+
* Hello,
5+
*
6+
* this is a part of the tutorial to the react-dart package. We'll go through
7+
* a simple app that is quering Google Maps API and showing the result to the
8+
* user. It also stores the search history and allows to reload past queries.
9+
*
10+
* In this file you'll find the mountpoint of the app. There is also
11+
* a `geocodes.dart` file, that contains the structure and the logic.
12+
*
13+
* Be sure that you understand the basic concepts of
14+
* [React](http://facebook.github.io/react/) before reading this tutorial.
15+
*
16+
* Enjoy!
17+
-->
18+
19+
<html>
20+
<head>
21+
<title>Geocode resolver</title>
22+
<link
23+
rel="stylesheet"
24+
href="http://getbootstrap.com/dist/css/bootstrap.min.css"
25+
>
26+
</head>
27+
28+
<body>
29+
<!--
30+
This is the mountpoint. Every app is inside this div.
31+
The div is referenced in the Dart code by id.
32+
-->
33+
<div class="container" id="content"></div>
34+
35+
<!-- Load React, Your code and Dart -->
36+
<script src="packages/react/react.js"></script>
37+
<script type="application/dart" src="geocodes.dart"></script>
38+
<script src="packages/browser/dart.js"></script>
39+
</body>
40+
</html>
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)