In this step we are going to show or hide different parts of the app's UI depending the user's current state: either logged-in or anonymous.
As you may know, Angular 1 has ng-show and ng-hide attribute directives for controlling the visibility of content. We'll look at how visibility is handled differently in Angular 2.
Angular 2 binds an attribute to an element's property. As you already know, one can directly bind to the component attribute directives, for example:
<my-component [foo]="fooValue" />
The Angular 2 team went further and implemented the same direct binding support for the DOM element attributes, including additional attributes like hidden and disabled, and which seems logical.
The hidden
attribute arrived in the DOM with HTML 5.
It's essentially similar to the old and well-known disabled
attribute, but only
makes an element hidden. With the presence of the hidden
attribute and direct attribute
binding, it seems there is no further need for attribute directives like ng-hide
.
There is one exception, though.
The DOM property
hidden
is rather new, and not supported by older versions of Internet Explorer (less than 11). If you need to support older browsers, you must implement a new directive attribute similar to theng-hide
yourself or make use of an already existing directive. There are sure to be solutions in the future.
A user who hasn't logged-in does not have all the same permissions; we can hide functionality that anonymous users cannot access such as the "add party" form and the "remove" button for each party in the parties list.
Let's toggle on and off these components with the help of the hidden
attribute, but first let's inject
the user property into the PartiesList component, since this is what our attribute
binding will depend on. User injection was already mentioned in step 8,
so let's make practical use of it now:
5
6
7
8
9
10
11
27
28
29
30
31
32
33
38
39
40
41
42
43
44
107
108
109
110
111
112
113
114
115
116
import { MeteorObservable } from 'meteor-rxjs';
import { PaginationService } from 'ng2-pagination';
import { Counts } from 'meteor/tmeasday:publish-counts';
import { InjectUser } from "angular2-meteor-accounts-ui";
import 'rxjs/add/operator/combineLatest';
...some lines skipped...
selector: 'parties-list',
template
})
@InjectUser('user')
export class PartiesListComponent implements OnInit, OnDestroy {
parties: Observable<Party[]>;
partiesSub: Subscription;
...some lines skipped...
partiesSize: number = 0;
autorunSub: Subscription;
location: Subject<string> = new Subject<string>();
user: Meteor.User;
constructor(
private paginationService: PaginationService
...some lines skipped...
this.nameOrder.next(parseInt(nameOrder));
}
isOwner(party: Party): boolean {
return this.user && this.user._id === party.owner;
}
ngOnDestroy() {
this.partiesSub.unsubscribe();
this.optionsSub.unsubscribe();
As you can see, we've added a new isOwner
method to the component,
thus, we allow only a party owner to remove the party.
Then, change the template to use the hidden
attribute:
1
2
3
4
5
19
20
21
22
23
24
25
<div>
<parties-form [hidden]="!user" style="float: left"></parties-form>
<input type="text" #searchtext placeholder="Search by Location">
<button type="button" (click)="search(searchtext.value)">Search</button>
...some lines skipped...
<a [routerLink]="['/party', party._id]">{{party.name}}</a>
<p>{{party.description}}</p>
<p>{{party.location}}</p>
<button [hidden]="!isOwner(party)" (click)="removeParty(party)">X</button>
<div>
Who is coming:
Yes - {{party | rsvp:'yes'}}
Now run the app.
The "add party" form and "remove" buttons should disappear if you are not logged-in. Try to log in: everything should be visible again.
Note: CSS's
display
property has priority over thehidden
property. If one of the CSS classes of any element has this property set,hidden
gets over-ruled. In this case, you'll have to wrap the element into a container element such as a<div>
and assign CSS classes with the "display" on that parent container.
Next let's add the disabled
attribute to the PartyDetails component.
Currently, all users have access to the party details page and can
change the values of the inputs, though they are still prohibited from saving anything
(remember the parties security added in step 8?).
Let's disable these inputs for users that are not owners.
We will get an isOwner
property when the party owner matches the logged-in user id:
4
5
6
7
8
9
10
19
20
21
22
23
24
25
27
28
29
30
31
32
33
102
103
104
105
106
107
108
109
110
111
import { Subscription } from 'rxjs/Subscription';
import { Meteor } from 'meteor/meteor';
import { MeteorObservable } from 'meteor-rxjs';
import { InjectUser } from "angular2-meteor-accounts-ui";
import 'rxjs/add/operator/map';
...some lines skipped...
selector: 'party-details',
template
})
@InjectUser('user')
export class PartyDetailsComponent implements OnInit, OnDestroy {
partyId: string;
paramsSub: Subscription;
...some lines skipped...
partySub: Subscription;
users: Observable<User>;
uninvitedSub: Subscription;
user: Meteor.User;
constructor(
private route: ActivatedRoute
...some lines skipped...
});
}
get isOwner(): boolean {
return this.party && this.user && this.user._id === this.party.owner;
}
ngOnDestroy() {
this.paramsSub.unsubscribe();
this.partySub.unsubscribe();
isOwner
can be used before the subscription has finished, so we must check if the party
property is available before checking if the party owner matches.
Then, let's add our new [disabled]
condition to the party details template:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<form *ngIf="party" (submit)="saveParty()">
<label>Name</label>
<input [disabled]="!isOwner" type="text" [(ngModel)]="party.name" name="name">
<label>Description</label>
<input [disabled]="!isOwner" type="text" [(ngModel)]="party.description" name="description">
<label>Location</label>
<input [disabled]="!isOwner" type="text" [(ngModel)]="party.location" name="location">
<button [disabled]="!isOwner" type="submit">Save</button>
<a [routerLink]="['/']">Cancel</a>
</form>
ngIf
It's important to know the difference between the hidden
attribute and ngIf
directive.
While hidden
shows and hides a DOM element that is already rendered,
ngIf
adds or removes an element from the DOM, making it both heavier and slower.
It makes sense to use ngIf
if the decision to show or hide some part of the UI is made during page loading.
Regarding our party details page, we'll show or hide with the help of ngIf
.
We'll show or hide the invitation response buttons to those who are already invited,
and the invitation list to the party owners and to everybody if the party is public.
We've already added our isOwner
variable. Let's add two more: isPublic
and isInvited
.
81
82
83
84
85
86
87
88
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
$set: {
name: this.party.name,
description: this.party.description,
location: this.party.location,
'public': this.party.public
}
});
}
...some lines skipped...
return this.party && this.user && this.user._id === this.party.owner;
}
get isPublic(): boolean {
return this.party && this.party.public;
}
get isInvited(): boolean {
if (this.party && this.user) {
const invited = this.party.invited || [];
return invited.indexOf(this.user._id) !== -1;
}
return false;
}
ngOnDestroy() {
this.paramsSub.unsubscribe();
this.partySub.unsubscribe();
Then, make use of the properties in the template:
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<a [routerLink]="['/']">Cancel</a>
</form>
<div *ngIf="isOwner || isPublic">
<p>Users to invite:</p>
<ul>
<li *ngFor="let user of users | async">
<div>{{user | displayName}}</div>
<button (click)="invite(user)">Invite</button>
</li>
</ul>
</div>
<div *ngIf="isInvited">
<h2>Reply to the invitation</h2>
<input type="button" value="I'm going!" (click)="reply('yes')">
<input type="button" value="Maybe" (click)="reply('maybe')">
In this step we've become familiar with the binding to the DOM attributes in Angular 2 and
used two of the attributes to make our app better: hidden
and disabled
.
The difference between ngIf
and hidden
was highlighted, and based on that, ngIf
was used to make the party details page securer and visually better.