Skip to content

Commit 19f85fc

Browse files
committed
Finishing my second pass through section 4.2
1 parent 9854b91 commit 19f85fc

File tree

9 files changed

+73
-39
lines changed

9 files changed

+73
-39
lines changed

gulpfile.js

-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ gulp.task( "sass", function () {
4545

4646
gulp.task( "scripts", function() {
4747
return gulp.src([
48-
"src/scripts/fastclick.js",
4948
"src/scripts/jquery.js",
5049
"src/scripts/prism.js",
5150
"src/scripts/app.js"

images/chapter4/android/4.png

28.5 KB
Loading

images/chapter4/ios/4.png

34 KB
Loading

index.html

+32-16
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,6 @@ <h4 class="exercise-start">
924924
</code></pre>
925925
<p>Now that <code>app.component.ts</code> is empty, let’s add in the appropriate Angular 2 routing code. Open app/app.component.ts back up and paste in the following code:</p>
926926
<pre><code class="lang-TypeScript">import {Component} from &quot;angular2/core&quot;;
927-
import {HTTP_PROVIDERS} from &quot;angular2/http&quot;;
928927
import {RouteConfig} from &quot;angular2/router&quot;;
929928
import {NS_ROUTER_DIRECTIVES, NS_ROUTER_PROVIDERS} from &quot;nativescript-angular/router&quot;;
930929
import {LoginPage} from &quot;./pages/login/login.component&quot;;
@@ -1133,11 +1132,13 @@ <h4 class="exercise-start">
11331132
</h4>
11341133

11351134
<p>Open <code>app/pages/list/list.html</code> and replace its contents with the following code:</p>
1136-
<pre><code class="lang-XML">&lt;ListView [items]=&quot;groceryList&quot; id=&quot;grocery-list&quot; class=&quot;small-spacing&quot;&gt;
1137-
&lt;template #item=&quot;item&quot;&gt;
1138-
&lt;Label [text]=&quot;item.name&quot; class=&quot;medium-spacing&quot;&gt;&lt;/Label&gt;
1139-
&lt;/template&gt;
1140-
&lt;/ListView&gt;
1135+
<pre><code class="lang-XML">&lt;GridLayout&gt;
1136+
&lt;ListView [items]=&quot;groceryList&quot; id=&quot;grocery-list&quot; class=&quot;small-spacing&quot;&gt;
1137+
&lt;template #item=&quot;item&quot;&gt;
1138+
&lt;Label [text]=&quot;item.name&quot; class=&quot;medium-spacing&quot;&gt;&lt;/Label&gt;
1139+
&lt;/template&gt;
1140+
&lt;/ListView&gt;
1141+
&lt;/GridLayout&gt;
11411142
</code></pre>
11421143
<p>We’ll talk about the new syntax in a moment, but first let’s define the class names used in the previous example. Open <code>app/app.css</code> and paste the following code at the bottom of the file, which defines a few utility class names you can use throughout your app:</p>
11431144
<pre><code class="lang-CSS">.small-spacing {
@@ -1177,16 +1178,19 @@ <h4 class="exercise-start">
11771178
&lt;/template&gt;
11781179
&lt;/ListView&gt;
11791180
</code></pre>
1181+
<p>The <a href="http://docs.nativescript.org/ApiReference/ui/list-view/ListView"><code>&lt;ListView&gt;</code> UI element</a> requires an <code>items</code> property that points at an array of data—in this case, the <code>groceryList</code> array you added to your <code>ListPage</code> class. The list view element requires a child <code>&lt;template&gt;</code> element that specifies how to render each item in the <code>items</code> array.</p>
1182+
<p>The <code>#item</code> syntax is Angular 2’s for syntax for <a href="https://angular.io/docs/ts/latest/guide/template-syntax.html#!#star-template">creating local template variables</a>. This gives you the ability to refer to each item in the array as <code>item</code> within the template. For this template, you render each item in the array with a single <code>&lt;Label&gt;</code> UI element, and because of the <code>[text]=&quot;item.name&quot;</code> binding, those labels contain the text from the <code>name</code> property of each of the items in <code>groceryList</code> TypeScript array.</p>
1183+
<p>Now that you have a hardcoded list displaying, let’s see how to swap that out with live data.</p>
11801184
<h4 class="exercise-start">
1181-
<b>Exercise</b>: ???
1185+
<b>Exercise</b>: Populate the list view
11821186
</h4>
11831187

11841188
<p>Open <code>app/shared/grocery/grocery.ts</code> and paste in the following code:</p>
11851189
<pre><code class="lang-TypeScript">export class Grocery {
11861190
constructor(public id: string, public name: string) {}
11871191
}
11881192
</code></pre>
1189-
<p>Open <code>app/shared/grocery/grocery-list.service.ts</code> and paste in the following code:</p>
1193+
<p>This creates a simple <code>Grocery</code> model object that you can use throughout your app. Next, let’s create a simple service that reads grocery lists from our backend. Open <code>app/shared/grocery/grocery-list.service.ts</code> and paste in the following code:</p>
11901194
<pre><code class="lang-TypeScript">import {Injectable} from &quot;angular2/core&quot;;
11911195
import {Http, Headers} from &quot;angular2/http&quot;;
11921196
import {Config} from &quot;../config&quot;;
@@ -1222,7 +1226,8 @@ <h4 class="exercise-start">
12221226
}
12231227
}
12241228
</code></pre>
1225-
<p>Open <code>app/pages/list/list.component.ts</code> and add the following two lines to the top of the file:</p>
1229+
<p>The code here is very similar to the code you used in the <code>UserService</code> earlier in this guide. You use the <code>Http</code> service’s <code>get()</code> method to load JSON data, and RxJS’s <code>map()</code> function to format the data into an array of <code>Grocery</code> objects.</p>
1230+
<p>To use this service, open <code>app/pages/list/list.component.ts</code> and add the following two lines to the top of the file:</p>
12261231
<pre><code class="lang-TypeScript">import {Grocery} from &quot;../../shared/grocery/grocery&quot;;
12271232
import {GroceryListService} from &quot;../../shared/grocery/grocery-list.service&quot;;
12281233
</code></pre>
@@ -1232,15 +1237,15 @@ <h4 class="exercise-start">
12321237
<p>Then, add the following <code>constructor</code> function within the <code>ListPage</code> class:</p>
12331238
<pre><code class="lang-TypeScript">constructor(private _groceryListService: GroceryListService) {}
12341239
</code></pre>
1235-
<p>Because we’re injecting a service we must also add it as a provider within our component decorator:</p>
1240+
<p>Next, because you’re injecting a service into your constructor you must also include it as a provider within your component decorator. To do so, replace the existing <code>@Component</code> decorator with the code below:</p>
12361241
<pre><code class="lang-TypeScript">@Component({
12371242
selector: &quot;list&quot;,
12381243
templateUrl: &quot;pages/list/list.html&quot;,
12391244
styleUrls: [&quot;pages/list/list-common.css&quot;, &quot;pages/list/list.css&quot;],
12401245
providers: [GroceryListService]
12411246
})
12421247
</code></pre>
1243-
<p>Finally, replace the existing <code>ngOnInit()</code> function with the code below:</p>
1248+
<p>Finally, to kick off the call to <code>load()</code> when this page initializes, replace the existing <code>ngOnInit()</code> function with the code below:</p>
12441249
<pre><code class="lang-TypeScript">ngOnInit() {
12451250
this._groceryListService.load()
12461251
.subscribe(loadedGroceries =&gt; {
@@ -1252,7 +1257,6 @@ <h4 class="exercise-start">
12521257
</code></pre>
12531258
<p>The full version of your <code>app/pages/list/list.component.ts</code> file should now look like this:</p>
12541259
<pre><code class="lang-TypeScript">import {Component, OnInit} from &quot;angular2/core&quot;;
1255-
12561260
import {Grocery} from &quot;../../shared/grocery/grocery&quot;;
12571261
import {GroceryListService} from &quot;../../shared/grocery/grocery-list.service&quot;;
12581262

@@ -1279,16 +1283,24 @@ <h4 class="exercise-start">
12791283
</code></pre>
12801284
<div class="exercise-end"></div>
12811285

1282-
<p>Talk about the code you just added. But then note that if you try the code, you’ll get an exception about no provider for Http. Talk about best practices of where to declare shared providers. Let’s fix the problem now.</p>
1286+
<p>Once you run this code you may expect to see a list of groceries, but instead you’ll see this error in your terminal:</p>
1287+
<pre><code>JS: EXCEPTION: No provider for Http! (ListPage -&gt; GroceryListService -&gt; Http)
1288+
JS: STACKTRACE:
1289+
JS: Error: DI Exception
1290+
JS: at NoProviderError.BaseException [as constructor] (/data/data/org.nativescript.groceries/files/app/tns_modules/angular2/src/facade/exceptions.js:16:23)
1291+
JS: at NoProviderError.AbstractProviderError [as constructor] (/data/data/org.nativescript.groceries/files/app/tns_modules/angular2/src/core/di/exceptions.js:38:16)
1292+
</code></pre><p>The problem here is your new <code>GroceryListService</code> uses the <code>Http</code> service, but that <code>Http</code> service is never included in your component’s <code>providers</code> array. You could add <code>HTTP_PROVIDERS</code> to this array, as you did in <code>login.component.ts</code>, but it seems a little silly to add <code>HTTP_PROVIDERS</code> to every page that you build. And as it turns out, Angular 2 providers a simpler way of handling this, by allowing you to add common providers to parent components.</p>
12831293
<h4 class="exercise-start">
1284-
<b>Exercise</b>: ???
1294+
<b>Exercise</b>: Declaring providers
12851295
</h4>
12861296

12871297
<p>Open <code>app/pages/login/login.component.ts</code> and <em>remove</em> the following line from the top of the file:</p>
1298+
<div class="no-copy-button"></div>
1299+
12881300
<pre><code class="lang-TypeScript">import {HTTP_PROVIDERS} from &quot;angular2/http&quot;;
12891301
</code></pre>
12901302
<p>Next, in the same file, remove <code>HTTP_PROVIDERS</code> from the component decorator’s <code>providers</code> array. The array should now look this like:</p>
1291-
<pre><code class="lang-TypeScript">providers: [UserService]
1303+
<pre><code class="lang-TypeScript">providers: [UserService],
12921304
</code></pre>
12931305
<p>After that, open <code>app/app.component.ts</code> and <em>add</em> the following import to the top of the file:</p>
12941306
<pre><code class="lang-TypeScript">import {HTTP_PROVIDERS} from &quot;angular2/http&quot;;
@@ -1298,7 +1310,11 @@ <h4 class="exercise-start">
12981310
</code></pre>
12991311
<div class="exercise-end"></div>
13001312

1301-
<p>Show an image of the list with backend-driven data. Talk about how the next step is letting users add to the list.</p>
1313+
<p>Generally, it’s only a good idea to declare providers in parent components if all of the component’s children actually use that provider. Although you <em>could</em> declare all your providers in <code>AppComponent</code>, your <code>providers</code> would become unwieldy as your app grows, and difficult to refactor as your app changes.</p>
1314+
<p>If you load the list page with the account you created earlier you’ll see a blank page, as your account is newly created, and therefore your grocery list is empty. If you want to see some data to verify your changes worked, try logging in with the credentials “[email protected]” and “password”. You should some data that looks something like this:</p>
1315+
<p><img src="images/chapter4/android/4.png" alt="Grocery data on Android">
1316+
<img src="images/chapter4/ios/4.png" alt="Grocery data on iOS"></p>
1317+
<p>At this point you have a list of data associated with each account that you display in a list view control, but a grocery list isn’t very useful if you can’t add to the list. Let’s look at how to do that next.</p>
13021318
<h3 id="gridlayout">GridLayout</h3>
13031319
<p>Introduce what a grid layout actually is. Should be able to copy from the existing guide liberally.</p>
13041320
<h4 class="exercise-start">

scripts/built.js

-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/chapters/chapter3.md

-1
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,6 @@ Now that `app.component.ts` is empty, let’s add in the appropriate Angular 2 r
506506

507507
``` TypeScript
508508
import {Component} from "angular2/core";
509-
import {HTTP_PROVIDERS} from "angular2/http";
510509
import {RouteConfig} from "angular2/router";
511510
import {NS_ROUTER_DIRECTIVES, NS_ROUTER_PROVIDERS} from "nativescript-angular/router";
512511
import {LoginPage} from "./pages/login/login.component";

src/chapters/chapter4.md

+41-15
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,13 @@ The ListView element lets you show a list of things on the screen, which is exac
149149
Open `app/pages/list/list.html` and replace its contents with the following code:
150150

151151
``` XML
152-
<ListView [items]="groceryList" id="grocery-list" class="small-spacing">
153-
<template #item="item">
154-
<Label [text]="item.name" class="medium-spacing"></Label>
155-
</template>
156-
</ListView>
152+
<GridLayout>
153+
<ListView [items]="groceryList" id="grocery-list" class="small-spacing">
154+
<template #item="item">
155+
<Label [text]="item.name" class="medium-spacing"></Label>
156+
</template>
157+
</ListView>
158+
</GridLayout>
157159
```
158160

159161
Well talk about the new syntax in a moment, but first lets define the class names used in the previous example. Open `app/app.css` and paste the following code at the bottom of the file, which defines a few utility class names you can use throughout your app:
@@ -205,10 +207,14 @@ How does this work? Let’s return to this chunk of code:
205207
</ListView>
206208
```
207209
210+
The [`<ListView>` UI element](http://docs.nativescript.org/ApiReference/ui/list-view/ListView) requires an `items` property that points at an array of data—in this case, the `groceryList` array you added to your `ListPage` class. The list view element requires a child `<template>` element that specifies how to render each item in the `items` array.
211+
212+
The `#item` syntax is Angular 2’s for syntax for [creating local template variables](https://angular.io/docs/ts/latest/guide/template-syntax.html#!#star-template). This gives you the ability to refer to each item in the array as `item` within the template. For this template, you render each item in the array with a single `<Label>` UI element, and because of the `[text]="item.name"` binding, those labels contain the text from the `name` property of each of the items in `groceryList` TypeScript array.
208213
214+
Now that you have a hardcoded list displaying, let’s see how to swap that out with live data.
209215
210216
<h4 class="exercise-start">
211-
<b>Exercise</b>: ???
217+
<b>Exercise</b>: Populate the list view
212218
</h4>
213219
214220
Open `app/shared/grocery/grocery.ts` and paste in the following code:
@@ -219,7 +225,7 @@ export class Grocery {
219225
}
220226
```
221227
222-
Open `app/shared/grocery/grocery-list.service.ts` and paste in the following code:
228+
This creates a simple `Grocery` model object that you can use throughout your app. Next, let’s create a simple service that reads grocery lists from our backend. Open `app/shared/grocery/grocery-list.service.ts` and paste in the following code:
223229
224230
``` TypeScript
225231
import {Injectable} from "angular2/core";
@@ -258,7 +264,9 @@ export class GroceryListService {
258264
}
259265
```
260266
261-
Open `app/pages/list/list.component.ts` and add the following two lines to the top of the file:
267+
The code here is very similar to the code you used in the `UserService` earlier in this guide. You use the `Http` service’s `get()` method to load JSON data, and RxJSs `map()` function to format the data into an array of `Grocery` objects.
268+
269+
To use this service, open `app/pages/list/list.component.ts` and add the following two lines to the top of the file:
262270

263271
``` TypeScript
264272
import {Grocery} from "../../shared/grocery/grocery";
@@ -277,7 +285,7 @@ Then, add the following `constructor` function within the `ListPage` class:
277285
constructor(private _groceryListService: GroceryListService) {}
278286
```
279287

280-
Because were injecting a service we must also add it as a provider within our component decorator:
288+
Next, because youre injecting a service into your constructor you must also include it as a provider within your component decorator. To do so, replace the existing `@Component` decorator with the code below:
281289

282290
``` TypeScript
283291
@Component({
@@ -288,7 +296,7 @@ Because we’re injecting a service we must also add it as a provider within our
288296
})
289297
```
290298

291-
Finally, replace the existing `ngOnInit()` function with the code below:
299+
Finally, to kick off the call to `load()` when this page initializes, replace the existing `ngOnInit()` function with the code below:
292300

293301
``` TypeScript
294302
ngOnInit() {
@@ -305,7 +313,6 @@ The full version of your `app/pages/list/list.component.ts` file should now look
305313
306314
``` TypeScript
307315
import {Component, OnInit} from "angular2/core";
308-
309316
import {Grocery} from "../../shared/grocery/grocery";
310317
import {GroceryListService} from "../../shared/grocery/grocery-list.service";
311318

@@ -333,22 +340,34 @@ export class ListPage implements OnInit {
333340
334341
<div class="exercise-end"></div>
335342
336-
Talk about the code you just added. But then note that if you try the code, you’ll get an exception about no provider for Http. Talk about best practices of where to declare shared providers. Let’s fix the problem now.
343+
Once you run this code you may expect to see a list of groceries, but instead you’ll see this error in your terminal:
344+
345+
```
346+
JS: EXCEPTION: No provider for Http! (ListPage -> GroceryListService -> Http)
347+
JS: STACKTRACE:
348+
JS: Error: DI Exception
349+
JS: at NoProviderError.BaseException [as constructor] (/data/data/org.nativescript.groceries/files/app/tns_modules/angular2/src/facade/exceptions.js:16:23)
350+
JS: at NoProviderError.AbstractProviderError [as constructor] (/data/data/org.nativescript.groceries/files/app/tns_modules/angular2/src/core/di/exceptions.js:38:16)
351+
```
352+
353+
The problem here is your new `GroceryListService` uses the `Http` service, but that `Http` service is never included in your component’s `providers` array. You could add `HTTP_PROVIDERS` to this array, as you did in `login.component.ts`, but it seems a little silly to add `HTTP_PROVIDERS` to every page that you build. And as it turns out, Angular 2 providers a simpler way of handling this, by allowing you to add common providers to parent components.
337354
338355
<h4 class="exercise-start">
339-
<b>Exercise</b>: ???
356+
<b>Exercise</b>: Declaring providers
340357
</h4>
341358
342359
Open `app/pages/login/login.component.ts` and _remove_ the following line from the top of the file:
343360
361+
<div class="no-copy-button"></div>
362+
344363
``` TypeScript
345364
import {HTTP_PROVIDERS} from "angular2/http";
346365
```
347366
348367
Next, in the same file, remove `HTTP_PROVIDERS` from the component decorator’s `providers` array. The array should now look this like:
349368
350369
``` TypeScript
351-
providers: [UserService]
370+
providers: [UserService],
352371
```
353372
354373
After that, open `app/app.component.ts` and _add_ the following import to the top of the file:
@@ -365,7 +384,14 @@ providers: [HTTP_PROVIDERS, NS_ROUTER_PROVIDERS],
365384
366385
<div class="exercise-end"></div>
367386
368-
Show an image of the list with backend-driven data. Talk about how the next step is letting users add to the list.
387+
Generally, it’s only a good idea to declare providers in parent components if all of the component’s children actually use that provider. Although you _could_ declare all your providers in `AppComponent`, your `providers` would become unwieldy as your app grows, and difficult to refactor as your app changes.
388+
389+
If you load the list page with the account you created earlier you’ll see a blank page, as your account is newly created, and therefore your grocery list is empty. If you want to see some data to verify your changes worked, try logging in with the credentials “[email protected]” and “password”. You should some data that looks something like this:
390+
391+
![Grocery data on Android](images/chapter4/android/4.png)
392+
![Grocery data on iOS](images/chapter4/ios/4.png)
393+
394+
At this point you have a list of data associated with each account that you display in a list view control, but a grocery list isn’t very useful if you can’t add to the list. Let’s look at how to do that next.
369395
370396
### GridLayout
371397

src/scripts/app.js

-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
(function() {
22
"use strict";
33

4-
FastClick.attach(document.body);
5-
64
function isMobile() {
75
return $(window).width() <= 700;
86
}

0 commit comments

Comments
 (0)