Conditional Templates

Note: If you skipped ahead to this section, click here to download a zip of the tutorial at this point.

Angular 1 has great and very simple directives that help us show and hide DOM elements conditionally. You can bind them to an expression, variables or functions.

ng-show and ng-hide

First, let's learn about ng-show and ng-hide.

So one thing we want to hide and show is the form for creating a new party. If a user is not logged in, they can't create a party, so why displaying the form for them? If the user is not logged in, we want to display a message saying they need to log in to create a new party.

In PartiesList add a ng-show directive to the PartyAdd like that:

1
2
3
4
<party-add ng-show="partiesList.isLoggedIn"></party-add>
 
<input type="search" ng-model="partiesList.searchText" placeholder="Search" />
 

Now, we need to add the ability to detect if there is a user logged in at the moment, so let's add a helper for that:

45
46
47
48
49
50
51
52
53
      },
      partiesCount() {
        return Counts.get('numberOfParties');
      },
      isLoggedIn() {
        return !!Meteor.userId();
      }
    });
  }

Then right after the form, add this HTML:

1
2
3
4
5
6
7
<party-add ng-show="partiesList.isLoggedIn"></party-add>
<div ng-hide="partiesList.isLoggedIn">
  Log in to create a party!
</div>
 
<input type="search" ng-model="partiesList.searchText" placeholder="Search" />
 

That is exactly the opposite - if isLoggedIn is true or we're in the processing of logging in, hide that div.

Now add the same to the PartyRsvp:

14
15
16
17
18
19
20
    </a>
    <p>{{party.description}}</p>
    <party-remove party="party"></party-remove>
    <party-rsvp party="party" ng-show="partiesList.isLoggedIn"></party-rsvp>
    <party-rsvps-list rsvps="party.rsvps"></party-rsvps-list>
    <party-unanswered party="party"></party-unanswered>
    <party-creator party="party"></party-creator>

Add let's add this after the RSVP buttons:

15.5 Add message for not-logged in users on the RSVP buttons imports/ui/components/partiesList/partiesList.html
15
16
17
18
19
20
21
22
23
24
    <p>{{party.description}}</p>
    <party-remove party="party"></party-remove>
    <party-rsvp party="party" ng-show="partiesList.isLoggedIn"></party-rsvp>
    <div ng-hide="partiesList.isLoggedIn">
      <i>Sign in to RSVP for this party.</i>
    </div>
    
    <party-rsvps-list rsvps="party.rsvps"></party-rsvps-list>
    <party-unanswered party="party"></party-unanswered>
    <party-creator party="party"></party-creator>

Next thing we want to hide is the 'delete party' option, in case the logged-in user is not the party's owner.

Lets add ng-show to the delete button like that:

13
14
15
16
17
18
19
20
21
22
23
24
      {{party.name}}
    </a>
    <p>{{party.description}}</p>
    <party-remove party="party" ng-show="partiesList.isOwner(party)"></party-remove>
    <party-rsvp party="party" ng-show="partiesList.isLoggedIn"></party-rsvp>
    <div ng-hide="partiesList.isLoggedIn">
      <i>Sign in to RSVP for this party.</i>
    </div>
 
    <party-rsvps-list rsvps="party.rsvps"></party-rsvps-list>
    <party-unanswered party="party"></party-unanswered>
    <party-creator party="party"></party-creator>

In here you can see that ng-show can get a statement, in our case - the user exists (logged in) and is also the party's owner.

But we just missing the helper we used:

48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
      },
      isLoggedIn() {
        return !!Meteor.userId();
      },
      currentUserId() {
        return Meteor.userId();
      }
    });
  }
 
  isOwner(party) {
    return this.isLoggedIn && party.owner === this.currentUserId;
  }
 
  pageChanged(newPage) {
    this.page = newPage;
  }

ng-if

ng-if acts almost the same as ng-show but the difference between them is that ng-show hides the element by changing the display css property and ng-if simply removes it from the DOM completely.

So let's use ng-if to hide the outstanding invitations from a party, if the party is public (everyone is invited!):

15.8 Add ngIf to PartyUnanswered imports/ui/components/partiesList/partiesList.html
20
21
22
23
24
25
26
27
28
29
30
31
    </div>
 
    <party-rsvps-list rsvps="party.rsvps"></party-rsvps-list>
 
    <party-unanswered party="party" ng-if="!party.public"></party-unanswered>
    <div ng-if="party.public">
      Everyone is invited
    </div>
 
    <party-creator party="party"></party-creator>
  </li>
</ul>

Assigning a function

Now lets hide the 'PartyUninvited' inside PartyDetails in case the user is not logged in or can't invite to the party:

To do that we will create a scope function that returns a boolean and associate it with ng-show:

Create a new function inside partyDetails component, called canInvite:

15.9 Add canInvite method to the component imports/ui/components/partyDetails/partyDetails.js
31
32
33
34
35
36
37
38
39
40
41
42
43
44
    });
  }
 
  canInvite() {
    if (!this.party) {
      return false;
    }
 
    return !this.party.public && this.party.owner === Meteor.userId();
  }
 
  save() {
    Parties.update({
      _id: this.party._id

and add the ng-show to the PartyUninvited:

15.10 Add ngShow to PartyUninvited imports/ui/components/partyDetails/partyDetails.html
8
9
10
11
 
<button ui-sref="parties">Back</button>
 
<party-uninvited party="partyDetails.party" ng-show="partyDetails.canInvite()"></party-uninvited>

Let's add a li that tells the user that everyone is already invited, if that is the case

15.11 Add info that everyone are already invited imports/ui/components/partyUninvited/partyUninvited.html
4
5
6
7
8
9
10
    <div>{{ user | displayNameFilter }}</div>
    <button ng-click="partyUninvited.invite(user)">Invite</button>
  </li>
  <li ng-if="(partyUninvited.users | uninvitedFilter:partyUninvited.party).length <= 0">
    Everyone are already invited.
  </li>
</ul>

Here, we are taking the result of the uninvited users and checking for its length.

But we are just missing the helpers in this component, so let's add it here as well:

15.12 Add isOwner helper to the party details component imports/ui/components/partyDetails/partyDetails.js
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
      },
      users() {
        return Meteor.users.find({});
      },
      isLoggedIn() {
        return !!Meteor.userId();
      },
      isOwner() {
        if (!this.party) {
          return false;
        }
 
        return this.party.owner === Meteor.userId();
      }
    });
  }

ng-disabled

Now lets disable the PartyDetails input fields in case the user doesn't have permission to change them (currently, the server is stopping the user, but there is no visual feedback aside from the server overriding the local edit immediately after):

1
2
3
4
5
6
7
8
The party you selected is:
<form>
  Party name: <input type="text" ng-model="partyDetails.party.name" ng-disabled="!partyDetails.isOwner"/>
  Description: <input type="text" ng-model="partyDetails.party.description" ng-disabled="!partyDetails.isOwner"/>
  Public Party? <input type="checkbox" ng-model="partyDetails.party.public" ng-disabled="!partyDetails.isOwner"/>
  <button ng-click="partyDetails.save()">Save</button>
</form>
 

Summary

So now our example looks much better after we hide things based on the current situation.

In the next chapters we will add Google Maps and some CSS and styling to our app.