Profile Toolbar, Multi App

Creates a single Vue app for each profile on the page.

by Rob Cameron

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<div id="app">
  <div id="profile-1" class="profile" data-username="rob" data-following="1,2,3" data-favorites="1,3,5" data-lists="4,5,6" data-vouches="7,8,9" data-notes="1,5,9">
    <h1>Rob</h1>
    <ul>
      <li>Following: {{ isFollowing }}</li>
      <li>Favorite: {{ isFavorited }}</li>
      <li>Lists: {{ isListed }}</li>
      <li>VouchedFor: {{ isVouchedFor }}</li>
      <li>HasNote: {{ hasNote }}</li>
    </ul>

    <button v-bind:data-username="username" data-behavior="toggle-vouch">
      <span v-if="isVouchedFor">Remove Vouch</span>
      <span v-else>Add Vouch</span>
    </button>
    <button v-bind:data-username="username" data-behavior="toggle-follow">
      <span v-if="isFollowing">Unfollow</span>
      <span v-else>Follow</span>
    </button>
  </div>
  
  <div id="profile-2" class="profile" data-username="joel" data-following="2,3" data-favorites="3,5" data-lists="1,4,5,6" data-vouches="1,7,8,9" data-notes="">
    <h1>Joel</h1>
    <ul>
      <li>Following: {{ isFollowing }}</li>
      <li>Favorite: {{ isFavorited }}</li>
      <li>Lists: {{ isListed }}</li>
      <li>VouchedFor: {{ isVouchedFor }}</li>
      <li>HasNote: {{ hasNote }}</li>
    </ul>

    <button v-bind:data-username="username" data-behavior="toggle-vouch">
      <span v-if="isVouchedFor">Remove Vouch</span>
      <span v-else>Add Vouch</span>
    </button>
    <button v-bind:data-username="username" data-behavior="toggle-follow">
      <span v-if="isFollowing">Unfollow</span>
      <span v-else>Follow</span>
    </button>
  </div>
</div>

CSS

body {
  font-family: 'Helvetica Neue', sans-serif;
}
h1 {
  margin: 0;
  padding: 0;
}
ul {
  margin: 1em 0;
  padding: 0;
  list-style: none;
}
.profile {
  display: inline-block;
  border: 1px solid #cccccc;
  padding: 1em;
  margin: 1em;
}

JavaScript

// Helper method that turns the data attributes of the profile 
// into an array of integers (by default they'll be an array 
// of strings)
var attributeToData = function($profile, name) {
	return $profile.data(name).toString().split(',').map(function(id) {
  	return parseInt(id);
  });
};

// all the profiles on the page
var $profiles = $('.profile');

// collects all apps as a hash of { "username": app }
var profileApps = {};

// id of the user viewing this page
var userId = 1;

$.each($profiles, function(i, profile) {
	var $profile = $(profile),
    app = new Vue({
      el: '#' + $profile.attr('id'),
      data: {
        // username of the profile this app is for
        username: $profile.data('username'),
        // IDs of users following this user
        following: attributeToData($profile, 'following'),
        // IDs of users who have favorited this user
        favorites: attributeToData($profile, 'favorites'),
        // IDs of lists the user is in
        lists: attributeToData($profile, 'lists'),
        // IDs of users vouching for this user
        vouches: attributeToData($profile, 'vouches'),
        // IDs of users who have a note about this user
        notes: attributeToData($profile, 'notes')
      },
      computed: {
        isFollowing: function() {
          return this.following.indexOf(userId) != -1;
        },
        isFavorited: function() {
          return this.favorites.indexOf(userId) != -1;
        },
        isListed: function() {
          return this.lists.indexOf(userId) != -1;
        },
        isVouchedFor: function() {
          return this.vouches.indexOf(userId) != -1;
        },
        hasNote: function() {
          return this.notes.indexOf(userId) != -1;
        }
      }
    });
  profileApps[$profile.data('username')] = app;
});

// user vouches and the data structure is updated. response is the entire vouches structure so we just replace it.
var toggleVouch = function(e) {
	var $el = $(e.currentTarget),
   ...