• No results found

Adding the Article Component

In document AngularJs 2 A4 (Page 34-43)

Now we have a form to submit new articles, but we aren’t showing the new articles anywhere.

Because every article submitted is going to be displayed as a list on the page, this is the perfect candidate for a new component.

Let’s create a new component to represent the submitted articles.

For that, we can create a new component on the same file. Insert the following snippet above the declaration of theRedditAppcomponent:

code/first_app/angular2-reddit-completed/intermediates/app-02.ts

7 <div class="four wide column center aligned votes">

8 <div class="ui statistic">

17 <div class="twelve wide column">

18 <a class="ui large header" href="{{ link }}">

19 {{ title }}

20 </a>

21 <ul class="ui big horizontal list voters">

22 <li class="item">

23 <a href (click)="voteUp()">

24 <i class="arrow up icon"></i>

25 upvote

26 </a>

27 </li>

28 <li class="item">

29 <a href (click)="voteDown()">

30 <i class="arrow down icon"></i>

31 downvote

42

43 constructor() {

44 this.title = 'Angular 2';

45 this.link = 'http://angular.io';

Notice that we have three parts to defining this new component:

1. Describing the Component properties by annotating the class with@Component 2. Describing the Component view in thetemplateoption

3. Creating a component-definition class (ArticleComponent) which houses our component logic

Let’s talk through each part in detail:

Creating thereddit-articleComponent

First, we define a new Component with @Component. The selector says that this component is placed on the page by using the tag<reddit-article>(i.e. the selector is a tag name).

So the most essential way to use this component would be to place the following tag in our markup:

1 <reddit-article>

2 </reddit-article>

These tags will remain in our view when the page is rendered.

We want each reddit-article to be on its own row. We’re using Semantic UI, and Semantic provides aCSS class for rows¹⁶calledrow.

In Angular 2, a component host is the element this component is attached to. You’ll notice on our

@Componentwe’re passing the option:host: { class: 'row' }. This tells Angular that on the host element (thereddit-articletag) we want to set theclassattribute to have “row”.

Using thehost option is nice because it means we can encapsulate thereddit-article

markup within our component. That is, we don’t have to both use areddit-articletag and require aclass="row"in the markup of the parent view. By using thehost option, we’re able to configure our host element from within the component.

Creating thereddit-article template

Second, we define the template with thetemplateoption.

code/first_app/angular2-reddit-completed/intermediates/app-02.ts

1 template: `

2 <div class="four wide column center aligned votes">

3 <div class="ui statistic">

12 <div class="twelve wide column">

13 <a class="ui large header" href="{{ link }}">

14 {{ title }}

15 </a>

16 <ul class="ui big horizontal list voters">

17 <li class="item">

18 <a href (click)="voteUp()">

¹⁶http://semantic-ui.com/collections/grid.html

19 <i class="arrow up icon"></i>

25 <i class="arrow down icon"></i>

26 downvote

There’s a lot of markup here, so let’s break it down:

A Single reddit-article Row

We have two columns:

1. the number of votes on the left and 2. the article information on the right.

We specify these columns with the CSS classes four wide column and twelve wide column respectively.

We’re showingvotesand thetitlewith the template expansion strings{{ votes }}and{{ title }}. The values come from the value ofvotesandtitleproperty of theArticleComponentclass.

Notice that we can use template strings in attribute values, as in thehref of theatag:href="{{

link }}". In this case, the value of thehrefwill be dynamically populated with the value of link from the component class

On our upvote/downvote links we have an action. We use(click)to bindvoteUp()/voteDown()to their respective buttons. When the upvote button is pressed, thevoteUp()function will be called on theArticleComponentclass (similarly with downvote andvoteDown()).

Creating thereddit-articleArticleComponent Definition Class Finally, we create theArticleComponentdefinition class:

code/first_app/angular2-reddit-completed/intermediates/app-02.ts

7 this.title = 'Angular 2';

8 this.link = 'http://angular.io';

Here we create three properties onArticleComponent:

1. votes- anumberrepresenting the sum of all upvotes, minus the downvotes 2. title- astringholding the title of the article

3. link- astringholding the URL of the article

In theconstructor()we set some default attributes:

code/first_app/angular2-reddit-completed/intermediates/app-02.ts

1 constructor() {

2 this.title = 'Angular 2';

3 this.link = 'http://angular.io';

4 this.votes = 10;

5 }

And we define two functions for voting, one for voting upvoteUpand one for voting downvoteDown:

code/first_app/angular2-reddit-completed/intermediates/app-02.ts

InvoteUpwe incrementthis.votesby one. Similarly we decrement forvoteDown. Using thereddit-articleComponent

In order to use this component and make the data visible, we have to add a <reddit-article></reddit-article>tag somewhere in our markup.

In this case, we want theRedditAppcomponent to render this new component, so let’s change the code in that component. Add the<reddit-article>tag to theRedditApp’s template right after the closing</form>tag:

1 <button (click)="addArticle(newtitle, newlink)"

2 class="ui positive right floated button">

3 Submit link 4 </button>

5 </form>

6

7 <div class="ui grid posts">

8 <reddit-article>

9 </reddit-article>

10 </div>

11 `

If we reload the browser now, you will see that the<reddit-article>tag wasn’t compiled. Oh no!

Whenever hitting a problem like this, the first thing to do is open up your browser’s developer console. If we inspect our markup (see screenshot below), we can see that thereddit-articletag is on our page, but it hasn’t been compiled into markup. Why not?

Unexpanded tag when inspecting the DOM

This happens because the RedditApp component doesn’t know about the ArticleComponent component yet.

Angular 1 Note: If you’ve used Angular 1 it might be surprising that our app doesn’t know about our newreddit-articlecomponent. This is because in Angular 1, directives match globally. However, in Angular 2 you need explicitly specify which components (and therefore, which selectors) you want to use.

On the one hand, this requires a little more configuration. On the other hand, it’s great for building scalable apps because it means you don’t have to share your directive selectors in a global namespace.

In order to tell our RedditAppabout our new ArticleComponent component, we need to add the directivesproperty on ourRedditApp:

1 // for RedditApp 2 @Component({

3 selector: 'reddit',

4 directives: [ArticleComponent], 5 template: `

6 // ...

And now, when we reload the browser we should see the article properly rendered:

Rendered ArticleComponent component

However, if you try to click the vote up or vote down links, you’ll see that the page unexpectedly reloads.

This is because Javascript, by default, propagates theclickevent to all the parent components.

Because theclickevent is propagated to parents, our browser is trying to follow the empty link.

To fix that, we need to make the click event handler to returnfalse. This will ensure the browser won’t try to refresh the page. We change our code like so:

1 voteUp(): boolean {

Now if you click the links, you’ll see that the votes increase and decrease properly without a page refresh.

In document AngularJs 2 A4 (Page 34-43)

Related documents