Skip to content

Commit 4667216

Browse files
committed
Merge branch 'pr/12' into gh-pages
Conflicts: index.html src/chapters/chapter3.md
2 parents 51867aa + d41a528 commit 4667216

File tree

6 files changed

+68
-52
lines changed

6 files changed

+68
-52
lines changed

index.html

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ <h4 class="exercise-start">
353353

354354
<blockquote>
355355
<p><strong>NOTE</strong>: Notice the back-tick character (`) used with the <code>template</code> property. This character is used to define an <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals">ES2015 template literal</a>, which TypeScript supports, and which allows you to write multi-line strings without using messy string concatenation.</p>
356+
<p><strong>WARNING</strong>: Take special care to properly close all your UI elements and <em>not</em> use self-closing declarations like <code>&lt;Button text=&quot;Sign in&quot; /&gt;</code>. The limitation is related to the <a href="https://github.com/inikulin/parse5">parse5 library</a> Angular 2 uses to parse templates, and you can read <a href="https://github.com/NativeScript/nativescript-angular#known-issues">details about the issue on GitHub</a>.</p>
356357
</blockquote>
357358
<p>This code adds two new NativeScript UI elements: a <a href="http://docs.nativescript.org/ApiReference/ui/text-field/how-to.html">text field</a> and a <a href="http://docs.nativescript.org/ApiReference/ui/button/how-to.html">button</a>. Much like HTML elements, NativeScript UI elements provide attributes to let you configure their behavior and appearance. The code you just added uses the following attributes:</p>
358359
<ul>
@@ -634,7 +635,7 @@ <h4 class="exercise-start">
634635
<b>Exercise</b>: Two-way data binding with Angular 2
635636
</h4>
636637

637-
<p>In <code>app/app.component.ts</code>, find the find the first <code>&lt;TextField&gt;</code>, and replace it with the <code>&lt;TextField&gt;</code> below, which introduces a new <code>[(ngModel)]</code> attribute:</p>
638+
<p>In <code>app/app.component.ts</code>, find the first <code>&lt;TextField&gt;</code>, and replace it with the <code>&lt;TextField&gt;</code> below, which introduces a new <code>[(ngModel)]</code> attribute:</p>
638639
<pre><code class="lang-XML">&lt;TextField hint=&quot;Email Address&quot; id=&quot;email&quot; keyboardType=&quot;email&quot; [(ngModel)]=&quot;email&quot;
639640
autocorrect=&quot;false&quot; autocapitalizationType=&quot;none&quot;&gt;&lt;/TextField&gt;
640641
</code></pre>
@@ -667,7 +668,7 @@ <h4 class="exercise-start">
667668
</code></pre>
668669
<div class="exercise-end"></div>
669670

670-
<p>The previous examples switches each button’s <code>text</code> attribute from a simple string—e.g. <code>&lt;Button text=&quot;Sign Up&quot;&gt;</code>—to an attribute binding that is dependent on a value defined in the <code>AppComponent</code> class—e.g. <code>&lt;Button [text]=&quot;isLoggingIn ? &#39;Sign in&#39; : &#39;Sign up&#39;&quot;&gt;&quot;</code>. Now, when the value of the <code>isLoggingIn</code> attributes changes after the user taps the bottom button, Angular is smart enough to update the text of the button automatically. The result looks like this:</p>
671+
<p>The previous examples switches each button’s <code>text</code> attribute from a simple string—e.g. <code>&lt;Button text=&quot;Sign Up&quot;&gt;</code>—to an attribute binding that is dependent on a value defined in the <code>AppComponent</code> class—e.g. <code>&lt;Button [text]=&quot;isLoggingIn ? &#39;Sign in&#39; : &#39;Sign up&#39;&quot;&gt;&quot;</code>. Now, when the value of the <code>isLoggingIn</code> attributes changes after the user taps the bottom button, Angular is smart enough to update the text of the button automatically. The result looks like this:</p>
671672
<p><img src="images/chapter3/android/4.gif" alt="Text changing on Android">
672673
<img src="images/chapter3/ios/4.gif" alt="Text changing on iOS"></p>
673674
<p>At this point, you have a basic login screen setup with two-way data binding—which isn’t bad for 20 some lines of TypeScript. (Think about how much code you’d have to write in Android Studio <em>and</em> Xcode to accomplish the same task.) To this point though you’ve been placing all of your logic in a single TypeScript file, which doesn’t scale all that well for real-world applications.</p>
@@ -772,7 +773,7 @@ <h4 class="exercise-start">
772773
<b>Exercise</b>: Add an Angular 2 service
773774
</h4>
774775

775-
<p>There’s are several new concepts to introduce with Angular services, so we’re going to start by stubbing out a new <code>register()</code> method, and then come back to the implementation later in this section. With that in mind, open <code>app/shared/user/user.service.ts</code> and paste in the following code:</p>
776+
<p>There are several new concepts to introduce with Angular services, so we’re going to start by stubbing out a new <code>register()</code> method, and then come back to the implementation later in this section. With that in mind, open <code>app/shared/user/user.service.ts</code> and paste in the following code:</p>
776777
<pre><code class="lang-TypeScript">import {Injectable} from &quot;angular2/core&quot;;
777778
import {User} from &quot;./user&quot;;
778779

@@ -828,7 +829,15 @@ <h4 class="exercise-start">
828829
</code></pre>
829830
<p>This is Angular 2’s dependency injection implementation in action. Because you registered <code>UserService</code> as a provider in this component’s <code>providers</code> array, when Angular sees this syntax it creates an instance of the <code>UserService</code> class, and passes that instance into the component’s constructor.</p>
830831
<p>This begs a bigger question though: why bother with all of this? Why not run <code>this._userService = new UserService()</code> in the component’s constructor and forget the complexity of <code>@Injectable</code> and <code>providers</code>?</p>
831-
<p>The short answer is a dependency-injection-based approach to coding keeps your classes less coupled, and therefore more maintainable and testable as your application evolves over time. For a longer answer, head over to the Angular’s docs for a <a href="https://angular.io/docs/ts/latest/guide/dependency-injection.html">more thorough discussion of the benefits of dependency injection</a>.</p>
832+
<p>The short answer is a dependency-injection-based approach to coding keeps your classes less coupled, and therefore more maintainable and testable as your application evolves over time. For a longer answer, head over to the Angular’s docs for a <a href="https://angular.io/docs/ts/latest/guide/dependency-injection.html">thorough discussion of the benefits of dependency injection</a>.</p>
833+
<blockquote>
834+
<p><strong>NOTE</strong>: If you’re looking to learn more about Angular 2’s <code>providers</code> array and dependency injection implementation, the <a href="http://blog.thoughtram.io/">thoughtram blog</a> has a great 3-part series that covers the topic in more detail than this guide.</p>
835+
<ul>
836+
<li><a href="http://blog.thoughtram.io/angular/2015/05/18/dependency-injection-in-angular-2.html">Part 1—Dependency Injection</a></li>
837+
<li><a href="http://blog.thoughtram.io/angular/2015/08/20/host-and-visibility-in-angular-2-dependency-injection.html">Part 2—Host and Visibility</a></li>
838+
<li><a href="http://blog.thoughtram.io/angular2/2015/11/23/multi-providers-in-angular-2.html">Part 3—Multi Providers</a>.</li>
839+
</ul>
840+
</blockquote>
832841
<p>Let’s return to our example and make the registration process actually work.</p>
833842
<h4 class="exercise-start">
834843
<b>Exercise</b>: Use an Angular 2 service
@@ -848,7 +857,7 @@ <h4 class="exercise-start">
848857
constructor(private _http: Http) {}
849858

850859
register(user: User) {
851-
var headers = new Headers();
860+
let headers = new Headers();
852861
headers.append(&quot;Content-Type&quot;, &quot;application/json&quot;);
853862

854863
return this._http.post(
@@ -941,7 +950,7 @@ <h4 class="exercise-start">
941950

942951
<p>To this point you’ve been putting your login page code in <code>app.component.ts</code>. Let’s move that logic into the <code>pages/login</code> folder to make room for additional pages.</p>
943952
<p>First, open <code>app/app.component.ts</code> and copy its contents into <code>app/pages/login/login.component.ts</code>.</p>
944-
<p>Next, in <code>login.component.ts</code>, change the name of the class from “AppComponent” to “LoginPage, and update the two paths below accordingly:</p>
953+
<p>Next, in <code>login.component.ts</code>, change the name of the class from “AppComponent” to “LoginPage, and update the two paths below accordingly:</p>
945954
<pre><code class="lang-TypeScript">import {User} from &quot;../../shared/user/user&quot;;
946955
import {UserService} from &quot;../../shared/user/user.service&quot;;
947956
</code></pre>
@@ -996,10 +1005,10 @@ <h4 class="exercise-start">
9961005
{ path: &quot;/List&quot;, component: ListPage, name: &quot;List&quot; }
9971006
])
9981007
</code></pre>
999-
<p>Angular 2 now knows about the list page, but we still need to navigate the user to that page at the appropriate time. Our next step is to allow users to log into their accounts, and to take navigate them to the new list page after they successfully authenticate.</p>
1000-
<p>To do that, start by opening <code>app/shared/user/user.service.ts</code> and the <code>login()</code> function below to the existing <code>UserService</code> class:</p>
1008+
<p>Angular 2 now knows about the list page, but we still need to navigate the user to that page at the appropriate time. Our next step is to allow users to log into their accounts, then navigate them to the new list page after they have successfully authenticated.</p>
1009+
<p>To do that, start by opening <code>app/shared/user/user.service.ts</code> and add the <code>login()</code> function below to the existing <code>UserService</code> class:</p>
10011010
<pre><code class="lang-TypeScript">login(user: User) {
1002-
var headers = new Headers();
1011+
let headers = new Headers();
10031012
headers.append(&quot;Content-Type&quot;, &quot;application/json&quot;);
10041013

10051014
return this._http.post(
@@ -1022,7 +1031,7 @@ <h4 class="exercise-start">
10221031
<p>To use this <code>login()</code> function, return to <code>app/login/login.component.ts</code>, and add the following import to the top of the file:</p>
10231032
<pre><code class="lang-TypeScript">import {Router} from &quot;angular2/router&quot;;
10241033
</code></pre>
1025-
<p>Next, replace the current <code>constructor()</code> declaration with the code below, which adds Angular 2’s <code>Router</code> service:</p>
1034+
<p>Next, replace the current <code>constructor()</code> declaration with the code below, which injects Angular 2’s <code>Router</code> service:</p>
10261035
<pre><code class="lang-TypeScript">constructor(private _router: Router, private _userService: UserService) {
10271036
</code></pre>
10281037
<p>Finally, replace the <code>LoginPage</code>’s existing <code>login()</code> function with the code below:</p>
@@ -1226,15 +1235,15 @@ <h4 class="exercise-start">
12261235
constructor(private _http: Http) {}
12271236

12281237
load() {
1229-
var headers = new Headers();
1238+
let headers = new Headers();
12301239
headers.append(&quot;Authorization&quot;, &quot;Bearer &quot; + Config.token);
12311240

12321241
return this._http.get(Config.apiUrl + &quot;Groceries&quot;, {
12331242
headers: headers
12341243
})
12351244
.map(res =&gt; res.json())
12361245
.map(data =&gt; {
1237-
var groceryList = [];
1246+
let groceryList = [];
12381247
data.Result.forEach((grocery) =&gt; {
12391248
groceryList.push(new Grocery(grocery.Id, grocery.Name));
12401249
});
@@ -1412,7 +1421,7 @@ <h4 class="exercise-start">
14121421
}
14131422

14141423
// Dismiss the keyboard
1415-
var groceryTextField = &lt;TextField&gt;topmost().currentPage.getViewById(&quot;grocery&quot;);
1424+
let groceryTextField = &lt;TextField&gt;topmost().currentPage.getViewById(&quot;grocery&quot;);
14161425
groceryTextField.dismissSoftInput();
14171426

14181427
this._groceryListService.add(this.grocery)
@@ -1434,7 +1443,7 @@ <h4 class="exercise-start">
14341443
<p>In this function you first ensure the user didn’t submit without typing a grocery. If the user did type something, you dismiss the device’s keyboard with the TextField element’s <code>dismissSoftInput()</code> method, and then call a new <code>add()</code> method on the <code>GroceryListService</code>.</p>
14351444
<p>To finish this example you have to define that new <code>add()</code> method. To do so, open <code>app/shared/grocery/grocery-list.service.ts</code> and paste the following function into the <code>GroceryService</code> class:</p>
14361445
<pre><code class="lang-TypeScript">add(name: string) {
1437-
var headers = new Headers();
1446+
let headers = new Headers();
14381447
headers.append(&quot;Authorization&quot;, &quot;Bearer &quot; + Config.token);
14391448
headers.append(&quot;Content-Type&quot;, &quot;application/json&quot;);
14401449

@@ -1522,7 +1531,7 @@ <h4 class="exercise-start">
15221531
this.groceryList.unshift(groceryObject);
15231532
});
15241533
this.isLoading = false;
1525-
var groceryList = topmost().currentPage.getViewById(&quot;grocery-list&quot;);
1534+
let groceryList = topmost().currentPage.getViewById(&quot;grocery-list&quot;);
15261535
groceryList.animate({
15271536
opacity: 1,
15281537
duration: 1000
@@ -1663,11 +1672,11 @@ <h4 class="exercise-start">
16631672
</code></pre>
16641673
<p>Finally, now that you’ve installed and required the plugin, and setup a UI to use it, your last step is implementing the <code>&lt;ActionItem&gt;</code>&#39;s <code>tap</code> handler. Open <code>app/pages/list/list.component.ts</code> again and add the following function to the <code>ListPage</code> class:</p>
16651674
<pre><code class="lang-TypeScript">share() {
1666-
var list = [];
1667-
for (var i = 0, size = this.groceryList.length; i &lt; size ; i++) {
1675+
let list = [];
1676+
for (let i = 0, size = this.groceryList.length; i &lt; size ; i++) {
16681677
list.push(this.groceryList[i].name);
16691678
}
1670-
var listString = list.join(&quot;, &quot;).trim();
1679+
let listString = list.join(&quot;, &quot;).trim();
16711680
socialShare.shareText(listString);
16721681
}
16731682
</code></pre>
@@ -1708,7 +1717,7 @@ <h4 class="exercise-start">
17081717
declare var NSForegroundColorAttributeName: any;
17091718

17101719
export function setHintColor(args: { view: TextField, color: Color }) {
1711-
var dictionary = new NSDictionary(
1720+
let dictionary = new NSDictionary(
17121721
[args.color.ios],
17131722
[NSForegroundColorAttributeName]
17141723
);
@@ -1723,14 +1732,14 @@ <h4 class="exercise-start">
17231732
</code></pre>
17241733
<p>After that, add the following function to the file’s <code>LoginPage</code> class:</p>
17251734
<pre><code class="lang-TypeScript">setTextFieldColors() {
1726-
var email = &lt;TextField&gt;this.page.getViewById(&quot;email&quot;);
1727-
var password = &lt;TextField&gt;this.page.getViewById(&quot;password&quot;);
1735+
let email = &lt;TextField&gt;this.page.getViewById(&quot;email&quot;);
1736+
let password = &lt;TextField&gt;this.page.getViewById(&quot;password&quot;);
17281737

1729-
var mainTextColor = new Color(this.isLoggingIn ? &quot;black&quot; : &quot;#C4AFB4&quot;);
1738+
let mainTextColor = new Color(this.isLoggingIn ? &quot;black&quot; : &quot;#C4AFB4&quot;);
17301739
email.color = mainTextColor;
17311740
password.color = mainTextColor;
17321741

1733-
var hintColor = new Color(this.isLoggingIn ? &quot;#ACA6A7&quot; : &quot;#C4AFB4&quot;);
1742+
let hintColor = new Color(this.isLoggingIn ? &quot;#ACA6A7&quot; : &quot;#C4AFB4&quot;);
17341743
setHintColor({ view: email, color: hintColor });
17351744
setHintColor({ view: password, color: hintColor });
17361745
}
@@ -1743,7 +1752,7 @@ <h4 class="exercise-start">
17431752
<p>After your app refreshes with this change, you should now see a far more readable hint color:</p>
17441753
<p><img src="images/chapter6/ios/2.png" alt="Better contrast on iOS"></p>
17451754
<p>Let’s back up to the contents of the <code>setHintColor()</code> function so we can discuss what’s going on here.</p>
1746-
<pre><code class="lang-TypeScript">var dictionary = new NSDictionary(
1755+
<pre><code class="lang-TypeScript">let dictionary = new NSDictionary(
17471756
[args.color.ios],
17481757
[NSForegroundColorAttributeName]
17491758
);
@@ -1784,7 +1793,7 @@ <h4 class="exercise-start">
17841793
args.view.android.setHintTextColor(args.color.android);
17851794
}
17861795
if (args.view.ios) {
1787-
var dictionary = new NSDictionary(
1796+
let dictionary = new NSDictionary(
17881797
[args.color.ios],
17891798
[NSForegroundColorAttributeName]
17901799
);
@@ -1843,7 +1852,7 @@ <h4 class="exercise-start">
18431852
<p>As cool as Groceries is, it’s currently missing one crucial feature for a grocery management app: the ability to delete groceries from the list.</p>
18441853
<p>The Groceries backend already supports deleting, but it’s up to you to implement the feature in the app. You do get two hints though. First, this is a function you can use in the <code>GroceryListService</code> for performing the necessary HTTP call to delete a grocery:</p>
18451854
<pre><code class="lang-TypeScript">delete(id: string) {
1846-
var headers = new Headers();
1855+
let headers = new Headers();
18471856
headers.append(&quot;Authorization&quot;, &quot;Bearer &quot; + Config.token);
18481857
headers.append(&quot;Content-Type&quot;, &quot;application/json&quot;);
18491858

src/chapters/chapter2.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ Open `app/app.component.ts` and replace the existing `@Component` with the follo
154154

155155
> **NOTE**: Notice the back-tick character (\`) used with the `template` property. This character is used to define an [ES2015 template literal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals), which TypeScript supports, and which allows you to write multi-line strings without using messy string concatenation.
156156
157+
> **WARNING**: Take special care to properly close all your UI elements and _not_ use self-closing declarations like `<Button text="Sign in" />`. The limitation is related to the [parse5 library](https://github.com/inikulin/parse5) Angular 2 uses to parse templates, and you can read [details about the issue on GitHub](https://github.com/NativeScript/nativescript-angular#known-issues).
158+
157159
This code adds two new NativeScript UI elements: a [text field](http://docs.nativescript.org/ApiReference/ui/text-field/how-to.html) and a [button](http://docs.nativescript.org/ApiReference/ui/button/how-to.html). Much like HTML elements, NativeScript UI elements provide attributes to let you configure their behavior and appearance. The code you just added uses the following attributes:
158160

159161
- `<TextField>`

0 commit comments

Comments
 (0)