Skip to content

Commit bc6bc01

Browse files
committed
Finishing a second pass of the services section
1 parent cf762b7 commit bc6bc01

File tree

6 files changed

+252
-167
lines changed

6 files changed

+252
-167
lines changed

images/chapter3/android/5.png

34.5 KB
Loading

images/chapter3/android/6.png

32.9 KB
Loading

images/chapter3/ios/5.png

38.4 KB
Loading

images/chapter3/ios/6.png

37.5 KB
Loading

index.html

Lines changed: 110 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,7 @@ <h4 class="exercise-start">
737737
</blockquote>
738738
<p>With this setup you now have a <code>User</code> class that you can share across pages in your app and even across applications. But a model object that’s four simple lines of code isn’t all that exciting. Where this approach really pays off is when you’re able to share your business logic, and the code that hits your backend systems. In Angular 2 those classes are known as services. Let’s look at them next.</p>
739739
<h3 id="services">Services</h3>
740-
<p>A login screen isn’t all that useful if it doesn’t actually log users into anything. Therefore, our next task is to take the user’s email address and password, and send them to a backend endpoint to retrieve an authentication token we’ll use later in this guide. We’ll build this functionality as an Angular 2 service.</p>
740+
<p>A login screen isn’t all that useful if it doesn’t actually log users into anything. Therefore, our next task is to allow users to create and log into accounts. We’ll build this functionality as an <a href="https://angular.io/docs/ts/latest/tutorial/toh-pt4.html">Angular 2 service</a>, which is Angular 2’s mechanism for reusable classes that operate on data.</p>
741741
<p>For the purposes of this tutorial we prebuilt a handful of backend endpoints using <a href="http://www.telerik.com/platform/backend-services">Telerik Backend Services</a>, and we’ll be using those endpoints to make this app functional. Let’s see how they work.</p>
742742
<blockquote>
743743
<p><strong>NOTE</strong>: You don&#39;t have to use Telerik Backend Services to power your app’s backend; you can use any HTTP API in a NativeScript app. Telerik Backend Services is convenient for us to use for this tutorial because it lets us spin up HTTP endpoints quickly.</p>
@@ -746,6 +746,68 @@ <h4 class="exercise-start">
746746
<b>Exercise</b>: Add an Angular 2 service
747747
</h4>
748748

749+
<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>
750+
<pre><code class="lang-TypeScript">import {Injectable} from &quot;angular2/core&quot;;
751+
import {User} from &quot;./user&quot;;
752+
753+
@Injectable()
754+
export class UserService {
755+
register(user: User) {
756+
alert(&quot;About to register: &quot; + user.email);
757+
}
758+
}
759+
</code></pre>
760+
<p>This creates a basic Angular service with a single method that takes an instance of the <code>User</code> object you created in the previous section. The one new thing is the <code>@Injectable</code> decorator. This decorator denotes this class as a candidate for <a href="https://angular.io/docs/ts/latest/guide/dependency-injection.html">Angular’s dependency injection mechanism</a>. For now just think of adding the <code>@Injectable</code> as a required convention for all services that you write.</p>
761+
<p>Next, add the following line to the top of <code>app/app.component.ts</code>, which imports the service you just defined:</p>
762+
<pre><code class="lang-TypeScript">import {UserService} from &quot;./shared/user/user.service&quot;;
763+
</code></pre>
764+
<p>After that, add a new <code>providers</code> property to the existing <code>@Component</code> decorator. The full <code>@Component</code> declaration should now look like this:</p>
765+
<pre><code class="lang-TypeScript">@Component({
766+
selector: &quot;my-app&quot;,
767+
providers: [UserService],
768+
templateUrl: &quot;pages/login/login.html&quot;,
769+
styleUrls: [&quot;pages/login/login-common.css&quot;, &quot;pages/login/login.css&quot;],
770+
})
771+
</code></pre>
772+
<p>The <code>providers</code> array is a simple list of all the Angular 2 services that you need to use in your component. At the moment you only have one service, so your <code>providers</code> array only has one entry.</p>
773+
<p>Next, replace <code>AppComponent</code>’s existing <code>constructor</code> with the code below:</p>
774+
<pre><code class="lang-TypeScript">constructor(private _userService: UserService) {
775+
this.user = new User();
776+
}
777+
</code></pre>
778+
<p>We’ll discuss what this <code>constructor</code> syntax is doing momentarily, as it can be confusing if you’ve never worked with Angular 2 before, but first let’s make the final change to get this example running. Find the existing <code>submit()</code> function in <code>AppComponent</code>, and replace it with the following three functions:</p>
779+
<pre><code class="lang-TypeScript">submit() {
780+
if (this.isLoggingIn) {
781+
this.login();
782+
} else {
783+
this.signUp();
784+
}
785+
}
786+
login() {
787+
// TODO: Define
788+
}
789+
signUp() {
790+
this._userService.register(this.user);
791+
}
792+
</code></pre>
793+
<div class="exercise-end"></div>
794+
795+
<p>Now, in your app, tap the “Sign Up” button, type an email address, and tap the “Sign Up” button again. If all went well, you should see the alert below:</p>
796+
<p><img src="images/chapter3/android/5.png" alt="Alert on Android">
797+
<img src="images/chapter3/ios/5.png" alt="Alert on iOS"></p>
798+
<p>How does this code work? Let’s return to this bit of code:</p>
799+
<pre><code class="lang-TypeScript">constructor(private _userService: UserService) {
800+
this.user = new User();
801+
}
802+
</code></pre>
803+
<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>
804+
<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>
805+
<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 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>
806+
<p>Let’s return to our example and make the registration process actually work.</p>
807+
<h4 class="exercise-start">
808+
<b>Exercise</b>: Use an Angular 2 service
809+
</h4>
810+
749811
<p>Open <code>app/shared/user/user.service.ts</code> and paste in the following code:</p>
750812
<pre><code class="lang-TypeScript">import {Injectable} from &quot;angular2/core&quot;;
751813
import {Http, Headers, Response} from &quot;angular2/http&quot;;
@@ -781,33 +843,8 @@ <h4 class="exercise-start">
781843
}
782844
}
783845
</code></pre>
784-
<p>TODO: Explain that mess above, and probably break it into a lot of steps.</p>
785-
<p>In <code>app.component.ts</code>, add the lines below to the top:</p>
786-
<pre><code class="lang-TypeScript">import {HTTP_PROVIDERS} from &quot;angular2/http&quot;;
787-
import {UserService} from &quot;./shared/user/user.service&quot;;
788-
</code></pre>
789-
<p>Then change the constructor to this:</p>
790-
<pre><code class="lang-TypeScript">constructor(private _userService: UserService) {
791-
this.user = new User();
792-
}
793-
</code></pre>
794-
<p>Add this line to the @Component:</p>
795-
<pre><code class="lang-TypeScript">providers: [UserService, HTTP_PROVIDERS],
796-
</code></pre>
797-
<p>Then change the submit function to this:</p>
798-
<pre><code class="lang-TypeScript">submit() {
799-
if (this.isLoggingIn) {
800-
this.login();
801-
} else {
802-
this.signUp();
803-
}
804-
}
805-
</code></pre>
806-
<p>Then add these two functions:</p>
807-
<pre><code class="lang-TypeScript">login() {
808-
// TODO: Define
809-
}
810-
signUp() {
846+
<p>Next, in <code>app/app.component.ts</code>, replace the existing <code>signUp()</code> function with the following code:</p>
847+
<pre><code class="lang-TypeScript">signUp() {
811848
this._userService.register(this.user)
812849
.subscribe(
813850
() =&gt; {
@@ -818,62 +855,56 @@ <h4 class="exercise-start">
818855
);
819856
}
820857
</code></pre>
821-
<p>Create an account, then hardcode those credentials in your constructor to make testing easier:</p>
822-
<pre><code class="lang-TypeScript">constructor(private _userService: UserService) {
823-
this.user = new User();
824-
this.user.email = &quot;nativescriptrocks@telerik.com&quot;;
825-
this.user.password = &quot;password&quot;;
826-
}
827-
</code></pre>
828-
<p>Full app.component.ts:</p>
829-
<pre><code class="lang-TypeScript">import {Component} from &quot;angular2/core&quot;;
830-
import {HTTP_PROVIDERS} from &quot;angular2/http&quot;;
831-
import {User} from &quot;./shared/user/user&quot;;
832-
import {UserService} from &quot;./shared/user/user.service&quot;;
858+
<div class="exercise-end"></div>
833859

834-
@Component({
835-
selector: &quot;my-app&quot;,
836-
templateUrl: &quot;pages/login/login.html&quot;,
837-
styleUrls: [&quot;pages/login/login-common.css&quot;, &quot;pages/login/login.css&quot;],
838-
providers: [UserService, HTTP_PROVIDERS]
839-
})
840-
export class AppComponent {
841-
user: User;
842-
isLoggingIn = true;
860+
<p>The first thing to note here is the new <code>constructor</code> code in <code>user.service.ts</code>:</p>
861+
<pre><code class="lang-TypeScript">constructor(private _http: Http) {}
862+
</code></pre>
863+
<p>The <code>UserService</code> class is using the same dependency injection technique to bring in a service that it needs, in this case the Http class, which is Angular 2’s way of letting you perform HTTP calls. And thanks to NativeScript, those same HTTP APIs work on iOS and Android without any extra work.</p>
864+
<blockquote>
865+
<p><strong>TIP</strong>: Refer to <a href="https://angular.io/docs/ts/latest/api/http/Http-class.html">Angular 2’s docs on Http</a> for specifics on what HTTP APIs are available.</p>
866+
</blockquote>
867+
<p>The other new bit of code is the return value of this new <code>register()</code> method. <code>register()</code> returns <code>this._http.post()</code>, which is an RxJS <code>Observable</code>. You can refer to docs for a <a href="https://angular.io/docs/ts/latest/guide/server-communication.html">full tutorial on how RxJS observables work</a>, but for now just know that the most common thing you’ll need to do with observables is subscribe to them, which is what the new code you added to <code>app.component.ts</code> does:</p>
868+
<pre><code class="lang-TypeScript">this._userService.register(this.user)
869+
.subscribe(
870+
() =&gt; {
871+
alert(&quot;Your account was successfully created.&quot;);
872+
this.toggleDisplay();
873+
},
874+
() =&gt; alert(&quot;Unfortunately we were unable to create your account.&quot;)
875+
);
876+
</code></pre>
877+
<blockquote>
878+
<p><strong>NOTE</strong>: The <code>() =&gt; {}</code> syntax defines an <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions">ES2015 arrow function</a>, which TypeScript supports out of the box. In this example, the arrow functions are the equivalent of writing <code>function() {}</code>.</p>
879+
</blockquote>
880+
<p>The two functions you provide are success and failure handlers. If The call to <code>register()</code> succeeds, your first alert will fire, and if the call to <code>register()</code> fails, your second alert will fire. Now that your service code is setup and ready to go, let’s create make the final few changes and create an account.</p>
881+
<h4 class="exercise-start">
882+
<b>Exercise</b>: Create an account
883+
</h4>
843884

844-
constructor(private _userService: UserService) {
845-
this.user = new User();
846-
this.user.email = &quot;nativescriptrocks@telerik.com&quot;;
847-
this.user.password = &quot;password&quot;;
848-
}
849-
submit() {
850-
if (this.isLoggingIn) {
851-
this.login();
852-
} else {
853-
this.signUp();
854-
}
855-
}
856-
login() {
857-
// TODO: Define
858-
}
859-
signUp() {
860-
this._userService.register(this.user)
861-
.subscribe(
862-
() =&gt; {
863-
alert(&quot;Your account was successfully created.&quot;);
864-
this.toggleDisplay();
865-
},
866-
() =&gt; alert(&quot;Unfortunately we were unable to create your account.&quot;)
867-
);
868-
}
869-
toggleDisplay() {
870-
this.isLoggingIn = !this.isLoggingIn;
871-
}
872-
}
885+
<p>Because the <code>UserService</code> makes use of the Http service, your final step is registering the Http provider in <code>AppComponent</code>. Start by opening <code>app/app.component.ts</code> and adding the following import to the top of the file:</p>
886+
<pre><code class="lang-TypeScript">import {HTTP_PROVIDERS} from &quot;angular2/http&quot;;
873887
</code></pre>
888+
<p>Next, in the same file, replace the current <code>providers</code> array with the following code:</p>
889+
<pre><code class="lang-TypeScript">providers: [UserService, HTTP_PROVIDERS],
890+
</code></pre>
891+
<p>At this point you should be ready to create an account to verify this whole setup worked.</p>
892+
<p>After the provider changes have livesync’d, click the “Sign Up” button in your app, type in an email address and password (fake credentials are fine, just make up something you can remember), and then click the orange “Sign Up” button.</p>
874893
<div class="exercise-end"></div>
875894

876-
<p>TODO: Show that user account creation now works and transition to routing.</p>
895+
<p>If all went well, you should see a confirmation dialog that looks like this:</p>
896+
<p><img src="images/chapter3/android/6.png" alt="Alert on Android">
897+
<img src="images/chapter3/ios/6.png" alt="Alert on iOS"></p>
898+
<blockquote>
899+
<p><strong>TIP</strong>: After creating your account, you may wish to hardcore your credentials in your <code>AppComponent</code>’s <code>constructor</code> to make development faster for the rest of this guide.</p>
900+
<pre><code class="lang-TypeScript">constructor(private _userService: UserService) {
901+
this.user = new User();
902+
this.user.email = &quot;user@nativescript.org&quot;;
903+
this.user.password = &quot;password&quot;;
904+
}
905+
</code></pre>
906+
</blockquote>
907+
<p>Your app now has a fully functional registration process, but users can’t do anything with the accounts they create. Our next step is to allow the users to login and navigate to a new list page. And to do that we need to introduce the concept of routing.</p>
877908
<h3 id="routing">Routing</h3>
878909
<p>TODO: Intro</p>
879910
<h4 class="exercise-start">

0 commit comments

Comments
 (0)