I have put together this small application that uses the JSONPlaceholder API and Vue.js.
The application crosses users and posts and displays the posts by user (author):
var app = new Vue({
el: "#app",
data: {
base_url: "https://jsonplaceholder.typicode.com",
uid: 1,
currentUser: null,
userData: null,
users: [],
posts: []
},
methods: {
toggleActiveUser(user) {
this.uid = user.id;
},
getUsers() {
axios
.get(`${this.base_url}/users`)
.then((response) => {
this.users = response.data;
})
.catch((error) => {
console.log(error);
this.errored = true;
})
.finally(() => (this.loading = false));
},
getCurrentUser() {
axios
.get(`${this.base_url}/users/${this.uid}`)
.then((response) => {
this.currentUser = response.data;
})
.catch((error) => {
console.log(error);
this.errored = true;
})
.finally(() => (this.loading = false));
},
getPosts() {
axios
.get(`${this.base_url}/posts?userId=${this.uid}`)
.then((response) => {
this.posts = response.data;
})
.catch((error) => {
console.log(error);
this.errored = true;
})
.finally(() => (this.loading = false));
}
},
created() {
this.getUsers();
this.getCurrentUser();
this.getPosts();
},
watch: {
currentUser() {
this.getCurrentUser();
},
posts() {
this.getPosts();
}
},
filters: {
capitalize(value) {
return value.charAt(0).toUpperCase() + value.slice(1);
},
titlecase(value) {
return value.toLowerCase().replace(/(?:^|[\s-/])\w/g, function(match) {
return match.toUpperCase();
});
},
lowercase(value) {
return value.toLowerCase();
}
}
});
.logo {
width: 30px;
}
.nav-item {
width: 100%;
}
@media (min-width: 768px) {
.nav-item {
width: auto;
}
}
.post-grid {
margin-top: 1rem;
justify-content: center;
display: flex;
flex-wrap: wrap;
}
.post {
display: flex;
flex-direction: column;
margin-bottom: 30px;
}
.post-container {
flex: 1;
padding: 15px;
background: #fcfcfc;
border: 1px solid #eaeaea;
}
.post-title {
font-size: 18px !important;
}
.short-desc {
text-align: justify;
}
/* User Info */
.table.user-info {
margin-bottom: 0;
}
.table.user-info>tbody>tr:first-child>td {
border-top: none !important;
}
.table.user-info td {
padding: 6px !important;
}
.table.user-info tr td:first-child {
font-weight: 500;
width: 32% !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" />
<div id="app">
<nav class="navbar navbar-expand-md bg-dark navbar-dark">
<!-- Brand -->
<a class="navbar-brand p-0" href="#">
<img src="https://www.pngrepo.com/png/303293/180/bootstrap-4-logo.png" alt="" class="logo">
</a>
<button class="navbar-toggler d-md-none ml-auto" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<!-- Links -->
<div class="navbar-nav navbar-expand-md w-100">
<ul class="navbar-nav navbar-expand-md collapse navbar-collapse" id="navbarSupportedContent">
<li class="nav-item">
<a class="nav-link" href="#">Home</a>
</li>
<!-- Dropdown -->
<li class="nav-item dropdown nav-item">
<a class="nav-link dropdown-toggle" href="#" id="navbardrop" data-toggle="dropdown">Authors</a>
<div class="dropdown-menu" v-if="users">
<a class="dropdown-item" href="#" v-for="user in users" :key="user.id" @click="toggleActiveUser(user)">{{user.name}}</a>
</div>
</li>
</ul>
</div>
</nav>
<div class="container">
<h1 class="h4 mt-3">Posts by {{currentUser?.name}}</h1>
<div v-if="currentUser != null">
<button type="button" class="btn btn-md btn-success" data-toggle="modal" data-target="#exampleModalCenter">
More about this author
</button>
</div>
<div class="row post-grid" v-if="posts">
<div class="col-sm-6 post" v-for="post in posts">
<div class="post-container">
<h2 class="display-4 post-title">{{post.title | titlecase}}</h2>
<div class="short-desc">
{{post.body | capitalize}}
</div>
</div>
</div>
</div>
</div>
<div v-if="currentUser != null" class="modal fade" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">{{currentUser?.name}}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<table class="table user-info">
<tr>
<td>Lives in</td>
<td>{{currentUser?.address.city}}</td>
</tr>
<tr>
<td>Address</td>
<td>{{currentUser?.address.street}}, {{currentUser?.address.suite}}</td>
</tr>
<tr>
<td>Zipcode</td>
<td>{{currentUser?.address.zipcode}}</td>
</tr>
<tr>
<td>Email</td>
<td>
<a href="mailto:{{currentUser?.email | lowercase}}">{{currentUser?.email | lowercase}}</a>
</td>
</tr>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
Questions:
- Is the code verbose?
- Does the application leave a lot to be desired in regards to optimization?