Skip to content

Commit b5e8256

Browse files
committed
CSS animations!
1 parent 51f692a commit b5e8256

File tree

2 files changed

+71
-40
lines changed

2 files changed

+71
-40
lines changed

index.html

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,7 +1193,7 @@ <h4 class="exercise-start">
11931193

11941194
<p>Open <code>app/pages/list/list.html</code> and replace its contents with the following code:</p>
11951195
<pre><code class="lang-XML">&lt;GridLayout&gt;
1196-
&lt;ListView [items]=&quot;groceryList&quot; #groceryListView class=&quot;small-spacing&quot;&gt;
1196+
&lt;ListView [items]=&quot;groceryList&quot; class=&quot;small-spacing&quot;&gt;
11971197
&lt;template #item=&quot;item&quot;&gt;
11981198
&lt;Label [text]=&quot;item.name&quot; class=&quot;medium-spacing&quot;&gt;&lt;/Label&gt;
11991199
&lt;/template&gt;
@@ -1210,7 +1210,6 @@ <h4 class="exercise-start">
12101210
</code></pre>
12111211
<p>Next, open <code>app/pages/list/list.component.ts</code> and replace its contents with the code below:</p>
12121212
<pre><code class="lang-TypeScript">import {Component, ElementRef, OnInit, ViewChild} from &quot;angular2/core&quot;;
1213-
import {ListView} from &quot;ui/list-view&quot;;
12141213

12151214
@Component({
12161215
selector: &quot;list&quot;,
@@ -1219,7 +1218,6 @@ <h4 class="exercise-start">
12191218
})
12201219
export class ListPage implements OnInit {
12211220
groceryList: Array&lt;Object&gt; = [];
1222-
@ViewChild(&quot;groceryListView&quot;) groceryListView: ElementRef;
12231221

12241222
ngOnInit() {
12251223
this.groceryList.push({ name: &quot;Apples&quot; });
@@ -1230,11 +1228,11 @@ <h4 class="exercise-start">
12301228
</code></pre>
12311229
<div class="exercise-end"></div>
12321230

1233-
<p>Your <code>ListPage</code> class now has a <code>groceryList</code> property that you fill with three objects in an <code>ngOnInit</code> handler, and a <code>@ViewChild</code> reference to the <code>&lt;ListView&gt;</code> element that you’ll use later. If you run your app and login, you should see the same list of groceries on the screen:</p>
1231+
<p>Your <code>ListPage</code> class now has a <code>groceryList</code> property that you fill with three objects in an <code>ngOnInit</code> handler. If you run your app and login, you should see the same list of groceries on the screen:</p>
12341232
<p><img src="images/chapter4/android/3.png" alt="List view on Android">
12351233
<img src="images/chapter4/ios/3.png" alt="List view on iOS"></p>
12361234
<p>How does this work? Let’s return to this chunk of code:</p>
1237-
<pre><code class="lang-XML">&lt;ListView [items]=&quot;groceryList&quot; #groceryListView class=&quot;small-spacing&quot;&gt;
1235+
<pre><code class="lang-XML">&lt;ListView [items]=&quot;groceryList&quot; class=&quot;small-spacing&quot;&gt;
12381236
&lt;template #item=&quot;item&quot;&gt;
12391237
&lt;Label [text]=&quot;item.name&quot; class=&quot;medium-spacing&quot;&gt;&lt;/Label&gt;
12401238
&lt;/template&gt;
@@ -1319,7 +1317,6 @@ <h4 class="exercise-start">
13191317
</code></pre>
13201318
<p>The full version of your <code>app/pages/list/list.component.ts</code> file should now look like this:</p>
13211319
<pre><code class="lang-TypeScript">import {Component, ElementRef, OnInit, ViewChild} from &quot;angular2/core&quot;;
1322-
import {ListView} from &quot;ui/list-view&quot;;
13231320
import {Grocery} from &quot;../../shared/grocery/grocery&quot;;
13241321
import {GroceryListService} from &quot;../../shared/grocery/grocery-list.service&quot;;
13251322

@@ -1392,7 +1389,7 @@ <h4 class="exercise-start">
13921389
&lt;Image src=&quot;res://add&quot; col=&quot;1&quot;&gt;&lt;/Image&gt;
13931390
&lt;/GridLayout&gt;
13941391

1395-
&lt;ListView [items]=&quot;groceryList&quot; #groceryListView row=&quot;1&quot; class=&quot;small-spacing&quot;&gt;
1392+
&lt;ListView [items]=&quot;groceryList&quot; row=&quot;1&quot; class=&quot;small-spacing&quot;&gt;
13961393
&lt;template #item=&quot;item&quot;&gt;
13971394
&lt;Label [text]=&quot;item.name&quot; class=&quot;medium-spacing&quot;&gt;&lt;/Label&gt;
13981395
&lt;/template&gt;
@@ -1515,7 +1512,7 @@ <h4 class="exercise-start">
15151512
</h4>
15161513

15171514
<p>Open up <code>app/pages/list/list.html</code> and paste the following line immediately before the final <code>&lt;/GridLayout&gt;</code>:</p>
1518-
<pre><code class="lang-XML">&lt;ActivityIndicator [busy]=&quot;isLoading&quot; row=&quot;1&quot;&gt;&lt;/ActivityIndicator&gt;
1515+
<pre><code class="lang-XML">&lt;ActivityIndicator [busy]=&quot;isLoading&quot; row=&quot;1&quot; horizontalAlignment=&quot;center&quot; verticalAlignment=&quot;center&quot;&gt;&lt;/ActivityIndicator&gt;
15191516
</code></pre>
15201517
<p>This binds the ActivityIndicator’s <code>busy</code> attribute to an <code>isLoading</code> property in the <code>ListPage</code> component. To define that property, open <code>app/pages/list/list.component.ts</code> and add the following line of code immediately under <code>grocery: string = &quot;&quot;</code>:</p>
15211518
<pre><code class="lang-TypeScript">isLoading = false;
@@ -1543,17 +1540,33 @@ <h4 class="exercise-start">
15431540
&lt;ActivityIndicator row=&quot;1&quot;&gt;&lt;/ActivityIndicator&gt;
15441541
</code></pre>
15451542
</blockquote>
1546-
<p>To finish off this chapter, let’s look at how you can use the animation module to add a final bit of polish to how the list page loads.</p>
1543+
<p>To finish off this chapter, let’s look at how you can use another NativeScript CSS feature to add a final bit of polish to how the list page loads.</p>
1544+
<p>In the following exercise you’ll use an animation to fade in the page’s <code>&lt;ListView&gt;</code> after your data loads. However this time, instead of getting a reference to the <code>&lt;ListView&gt;</code> UI element and calling its <code>animate()</code> method, you’ll instead use NativeScript’s CSS animations.</p>
15471545
<h4 class="exercise-start">
1548-
<b>Exercise</b>: Add a fade-in animation
1546+
<b>Exercise</b>: Using CSS animations
15491547
</h4>
15501548

1551-
<p>In this exercise, you’ll use the animation module to animate the <code>opacity</code> of the list page’s <code>&lt;ListView&gt;</code> to add a fade-in effect. Start by opening <code>app/pages/list/list-common.css</code> and pasting in the following CSS at the top of the file:</p>
1549+
<p> Start by opening <code>app/pages/list/list-common.css</code> and pasting in the following CSS at the top of the file:</p>
15521550
<pre><code class="lang-CSS">ListView {
15531551
opacity: 0;
15541552
}
1553+
.visible {
1554+
animation-name: show;
1555+
animation-duration: 1s;
1556+
}
1557+
@keyframes show {
1558+
from { opacity: 0; }
1559+
to { opacity: 1; }
1560+
}
15551561
</code></pre>
1556-
<p>Next, open <code>app/pages/list/list-component.ts</code> and add replace the existing <code>ngOnInit()</code> function with the following code:</p>
1562+
<p>This code sets the starting opacity value of the <code>&lt;ListView&gt;</code> to <code>0</code> so that the control is hidden when the page loads. The code also defines a <code>visible</code> class name that changes the <code>opacity</code> of an element from <code>0</code> to <code>1</code> over one full second.</p>
1563+
<blockquote>
1564+
<p><strong>TIP</strong>: For background on how the CSS animations syntax works, feel free to refer to the <a href="https://github.com/NativeScript/docs/blob/master/ui/animation-css.md">NativeScript CSS animation documentation</a>, or <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations">external CSS animation guides</a>.</p>
1565+
</blockquote>
1566+
<p>Now that you have the CSS in place, your next step is to add the previously defined <code>&quot;visible&quot;</code> class name to the <code>&lt;ListView&gt;</code> control after data has loaded. To do that, start by opening <code>app/pages/list/list-component.ts</code> and adding the following new property right below the existing <code>isLoading = false;</code> line:</p>
1567+
<pre><code class="lang-TypeScript">listLoaded = false;
1568+
</code></pre>
1569+
<p>Next, in the same file, replace the existing <code>ngOnInit()</code> function with the following code, which sets the new <code>listLoaded</code> flag:</p>
15571570
<pre><code class="lang-TypeScript">ngOnInit() {
15581571
this.isLoading = true;
15591572
this._groceryListService.load()
@@ -1562,19 +1575,18 @@ <h4 class="exercise-start">
15621575
this.groceryList.unshift(groceryObject);
15631576
});
15641577
this.isLoading = false;
1565-
let list = &lt;ListView&gt;this.groceryListView.nativeElement;
1566-
list.animate({
1567-
opacity: 1,
1568-
duration: 1000
1569-
});
1578+
this.listLoaded = true;
15701579
});
15711580
}
15721581
</code></pre>
1582+
<p>Finally, open <code>app/pages/list/list.html</code> and replace the existing <code>&lt;ListView&gt;</code> tag with the following code:</p>
1583+
<pre><code class="lang-XML">&lt;ListView [items]=&quot;groceryList&quot; row=&quot;1&quot; class=&quot;small-spacing&quot; [class.visible]=&quot;listLoaded&quot;&gt;
1584+
</code></pre>
15731585
<div class="exercise-end"></div>
15741586

1575-
<p>A few things are happening in the code above.</p>
1576-
<p>First, in CSS, you assign an <code>opacity</code> of <code>0</code> to the list page’s <code>&lt;ListView&gt;</code>. This hides the grocery list completely when the page loads. Next, in TypeScript, after the <code>GroceryListService</code>’s <code>load()</code> call completes, you call the list view’s <code>animate()</code> function. This changes the element&#39;s <code>opacity</code> from <code>0</code> (completely hidden) to <code>1</code> (completely visible) over one full second.</p>
1577-
<p>The result of this code is a nice fade-in animation:</p>
1587+
<p>The key here is the list view’s <code>[class.visible]=&quot;listLoaded&quot;</code> binding, which automatically applies the <code>visible</code> CSS class name based on the state of the <code>listLoaded</code> TypeScript property.</p>
1588+
<p>The advantage of using CSS animations is that you avoid the need to reference specific UI elements in your TypeScript code; there was no need to create a local template variable. The CSS animation approach also help to keep your code decoupled. Your TypeScript code can focus on logic, and leave styling concerns to your CSS code.</p>
1589+
<p>If you try out your app you should now see a nice fade-in animation:</p>
15781590
<p><img src="images/chapter4/android/8.gif" alt="Loading animation on Android">
15791591
<img src="images/chapter4/ios/8.gif" alt="Loading animation on iOS"></p>
15801592
<p>Now that you have functional login and list pages, let’s enhance the app’s functionality as a grocery list management tool. In the next chapters you&#39;ll add functionality such as email validation, social sharing, and more. And you’ll use one of NativeScript&#39;s most useful features to do so: npm modules.</p>

src/chapters/chapter4.md

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ Open `app/pages/list/list.html` and replace its contents with the following code
167167

168168
``` XML
169169
<GridLayout>
170-
<ListView [items]="groceryList" #groceryListView class="small-spacing">
170+
<ListView [items]="groceryList" class="small-spacing">
171171
<template #item="item">
172172
<Label [text]="item.name" class="medium-spacing"></Label>
173173
</template>
@@ -190,7 +190,6 @@ Next, open `app/pages/list/list.component.ts` and replace its contents with the
190190

191191
``` TypeScript
192192
import {Component, ElementRef, OnInit, ViewChild} from "angular2/core";
193-
import {ListView} from "ui/list-view";
194193
195194
@Component({
196195
selector: "list",
@@ -199,7 +198,6 @@ import {ListView} from "ui/list-view";
199198
})
200199
export class ListPage implements OnInit {
201200
groceryList: Array<Object> = [];
202-
@ViewChild("groceryListView") groceryListView: ElementRef;
203201
204202
ngOnInit() {
205203
this.groceryList.push({ name: "Apples" });
@@ -211,15 +209,15 @@ export class ListPage implements OnInit {
211209

212210
<div class="exercise-end"></div>
213211

214-
Your `ListPage` class now has a `groceryList` property that you fill with three objects in an `ngOnInit` handler, and a `@ViewChild` reference to the `<ListView>` element that youll use later. If you run your app and login, you should see the same list of groceries on the screen:
212+
Your `ListPage` class now has a `groceryList` property that you fill with three objects in an `ngOnInit` handler. If you run your app and login, you should see the same list of groceries on the screen:
215213

216214
![List view on Android](images/chapter4/android/3.png)
217215
![List view on iOS](images/chapter4/ios/3.png)
218216

219217
How does this work? Lets return to this chunk of code:
220218

221219
``` XML
222-
<ListView [items]="groceryList" #groceryListView class="small-spacing">
220+
<ListView [items]="groceryList" class="small-spacing">
223221
<template #item="item">
224222
<Label [text]="item.name" class="medium-spacing"></Label>
225223
</template>
@@ -332,7 +330,6 @@ The full version of your `app/pages/list/list.component.ts` file should now look
332330

333331
``` TypeScript
334332
import {Component, ElementRef, OnInit, ViewChild} from "angular2/core";
335-
import {ListView} from "ui/list-view";
336333
import {Grocery} from "../../shared/grocery/grocery";
337334
import {GroceryListService} from "../../shared/grocery/grocery-list.service";
338335
@@ -431,7 +428,7 @@ Open `app/pages/list/list.html` and replace the contents of the file with the fo
431428
<Image src="res://add" col="1"></Image>
432429
</GridLayout>
433430
434-
<ListView [items]="groceryList" #groceryListView row="1" class="small-spacing">
431+
<ListView [items]="groceryList" row="1" class="small-spacing">
435432
<template #item="item">
436433
<Label [text]="item.name" class="medium-spacing"></Label>
437434
</template>
@@ -594,7 +591,7 @@ In NativeScript apps you can use the [ActivityIndicator](http://docs.nativescrip
594591
Open up `app/pages/list/list.html` and paste the following line immediately before the final `</GridLayout>`:
595592

596593
``` XML
597-
<ActivityIndicator [busy]="isLoading" row="1"></ActivityIndicator>
594+
<ActivityIndicator [busy]="isLoading" row="1" horizontalAlignment="center" verticalAlignment="center"></ActivityIndicator>
598595
```
599596

600597
This binds the ActivityIndicators `busy` attribute to an `isLoading` property in the `ListPage` component. To define that property, open `app/pages/list/list.component.ts` and add the following line of code immediately under `grocery: string = ""`:
@@ -631,21 +628,41 @@ When you first visit the list page, you should now see the following loading ind
631628
> <ActivityIndicator row="1"></ActivityIndicator>
632629
> ```
633630

634-
To finish off this chapter, lets look at how you can use the animation module to add a final bit of polish to how the list page loads.
631+
To finish off this chapter, lets look at how you can use another NativeScript CSS feature to add a final bit of polish to how the list page loads.
632+
633+
In the following exercise youll use an animation to fade in the pages `<ListView>` after your data loads. However this time, instead of getting a reference to the `<ListView>` UI element and calling its `animate()` method, youll instead use NativeScripts CSS animations.
635634

636635
<h4 class="exercise-start">
637-
<b>Exercise</b>: Add a fade-in animation
636+
<b>Exercise</b>: Using CSS animations
638637
</h4>
639638

640-
In this exercise, youll use the animation module to animate the `opacity` of the list pages `<ListView>` to add a fade-in effect. Start by opening `app/pages/list/list-common.css` and pasting in the following CSS at the top of the file:
639+
Start by opening `app/pages/list/list-common.css` and pasting in the following CSS at the top of the file:
641640

642641
``` CSS
643642
ListView {
644643
opacity: 0;
645644
}
645+
.visible {
646+
animation-name: show;
647+
animation-duration: 1s;
648+
}
649+
@keyframes show {
650+
from { opacity: 0; }
651+
to { opacity: 1; }
652+
}
653+
```
654+
655+
This code sets the starting opacity value of the `<ListView>` to `0` so that the control is hidden when the page loads. The code also defines a `visible` class name that changes the `opacity` of an element from `0` to `1` over one full second.
656+
657+
> **TIP**: For background on how the CSS animations syntax works, feel free to refer to the [NativeScript CSS animation documentation](https://github.com/NativeScript/docs/blob/master/ui/animation-css.md), or [external CSS animation guides](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations).
658+
659+
Now that you have the CSS in place, your next step is to add the previously defined `"visible"` class name to the `<ListView>` control after data has loaded. To do that, start by opening `app/pages/list/list-component.ts` and adding the following new property right below the existing `isLoading = false;` line:
660+
661+
``` TypeScript
662+
listLoaded = false;
646663
```
647664

648-
Next, open `app/pages/list/list-component.ts` and add replace the existing `ngOnInit()` function with the following code:
665+
Next, in the same file, replace the existing `ngOnInit()` function with the following code, which sets the new `listLoaded` flag:
649666

650667
``` TypeScript
651668
ngOnInit() {
@@ -656,22 +673,24 @@ ngOnInit() {
656673
this.groceryList.unshift(groceryObject);
657674
});
658675
this.isLoading = false;
659-
let list = <ListView>this.groceryListView.nativeElement;
660-
list.animate({
661-
opacity: 1,
662-
duration: 1000
663-
});
676+
this.listLoaded = true;
664677
});
665678
}
666679
```
667680

681+
Finally, open `app/pages/list/list.html` and replace the existing `<ListView>` tag with the following code:
682+
683+
``` XML
684+
<ListView [items]="groceryList" row="1" class="small-spacing" [class.visible]="listLoaded">
685+
```
686+
668687
<div class="exercise-end"></div>
669688

670-
A few things are happening in the code above.
689+
The key here is the list views `[class.visible]="listLoaded"` binding, which automatically applies the `visible` CSS class name based on the state of the `listLoaded` TypeScript property.
671690

672-
First, in CSS, you assign an `opacity` of `0` to the list pages `<ListView>`. This hides the grocery list completely when the page loads. Next, in TypeScript, after the `GroceryListService`s `load()` call completes, you call the list views `animate()` function. This changes the element's `opacity` from `0` (completely hidden) to `1` (completely visible) over one full second.
691+
The advantage of using CSS animations is that you avoid the need to reference specific UI elements in your TypeScript code; there was no need to create a local template variable. The CSS animation approach also help to keep your code decoupled. Your TypeScript code can focus on logic, and leave styling concerns to your CSS code.
673692

674-
The result of this code is a nice fade-in animation:
693+
If you try out your app you should now see a nice fade-in animation:
675694

676695
![Loading animation on Android](images/chapter4/android/8.gif)
677696
![Loading animation on iOS](images/chapter4/ios/8.gif)

0 commit comments

Comments
 (0)