We will build a movie application using Vue.js. By the end, you’ll have a fully functional movie app that will allow users to search for movies, view their details, and get information such as title, year, rating, and plot.
Prerequisites:Approach - Vue Components: We will create two Vue components, MovieSearch and MovieDetail.
- HTTP Requests: Axios is used to make API calls to OMDB.
- Conditional Rendering: We will implement conditional rendering to display search results or movie details based on user interactions.
Steps to Create ApplicationStep 1: Setup Vue ProjectCreate a new Vue project using Vue CLI.
vue create movie-mania Step 2: Install AxiosAxios is used for making HTTP requests. Install it in your project.
npm install axios Updated dependencies will look like
"dependencies": { "axios": "^1.6.8", "core-js": "^3.8.3", "vue": "^3.2.13" }, Step 3: Creating and managing files and folders- Create MovieDetail.vue and MovieSearch.vue files inside the component folder
- Replace the content of src/App.vue with the given below code.
Example: This example shows the creation of a Movie App.
JavaScript
//App.vue
<template>
<div class="App">
<header class="App-header">
<img src=
"https://media.geeksforgeeks.org/gfg-gg-logo.svg"
alt="GeeksforGeeks Logo"
class="logo" />
<h1>Movie Mania</h1>
</header>
<main>
<MovieSearch @searchInput="searchInput"
@search="search" />
<div class="container">
<div
v-for="movie in filteredResults"
:key="movie.imdbID"
class="item"
@click="openDetail(movie.imdbID)"
>
<img :src="movie.Poster" alt="Movie Poster" />
<h3>{{ movie.Title }}</h3>
</div>
</div>
<MovieDetail v-if="selected.Title"
:selected="selected"
@closeDetail="closeDetail" />
</main>
</div>
</template>
<script>
import axios from "axios";
import MovieSearch from "./components/MovieSearch.vue";
import MovieDetail from "./components/MovieDetail.vue";
export default {
name: "App",
components: {
MovieSearch,
MovieDetail,
},
data() {
return {
s: "", // Initialize search query
results: [], // Initialize results array
selected: {}, // Initialize selected movie object
apiurl: "https://www.omdbapi.com/?apikey=a2526df0",
};
},
mounted() {
// Fetch initial movies based on specific keywords
this.fetchInitialMovies();
},
methods: {
fetchInitialMovies() {
// Keywords for initial movie search
const keywords = ["hindi", "avengers", "comedy"];
// Fetch movies for each keyword
// and combine the results
Promise.all(keywords.map(keyword => {
return axios(this.apiurl + "&s=" + keyword)
.then(({ data }) => data.Search || [])
.catch(error => {
console.error("Error fetching movies:", error);
return [];
});
})).then(results => {
this.results = results.flat();
// Combine arrays of movies
});
},
searchInput(e) {
this.s = e.target.value;
},
search(e) {
if (e.key === "Enter") {
axios(this.apiurl + "&s=" + this.s)
.then(({ data }) => {
this.results = data.Search || [];
});
}
},
openDetail(id) {
axios(this.apiurl + "&i=" + id).then(({ data }) => {
this.selected = data;
});
},
closeDetail() {
this.selected = {};
},
},
computed: {
filteredResults() {
// Filter results based on search query
return this.results.filter(movie => movie.Title.toLowerCase()
.includes(this.s.toLowerCase()));
}
},
};
</script>
<style>
.App {
text-align: center;
background-color: #f4f4f4;
min-height: 100vh;
}
.App-header {
background-color: #333;
color: white;
padding: 20px 0;
display: flex;
justify-content: center;
align-items: center;
}
.logo {
width: 50px;
margin-right: 10px;
}
.container {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 20px;
padding: 20px;
}
.item {
cursor: pointer;
width: 200px;
text-align: center;
padding: 10px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
}
.item:hover {
transform: translateY(-5px);
}
.item img {
width: 100%;
border-radius: 8px;
}
.MovieDetail {
background-color: rgba(0, 0, 0, 0.8);
color: white;
padding: 20px;
border-radius: 8px;
max-width: 600px;
margin: 20px auto;
}
.MovieDetail h2 {
margin-top: 0;
}
.MovieDetail span {
font-weight: bold;
}
.MovieDetail img {
max-width: 100%;
border-radius: 8px;
margin-bottom: 20px;
}
.MovieDetail p {
line-height: 1.5;
}
.close {
background-color: #333;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.close:hover {
background-color: #555;
}
.search-bar {
margin: 20px auto;
max-width: 400px;
}
.search {
width: 100%;
padding: 10px;
font-size: 16px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
transition: border-color 0.3s ease;
}
.search:focus {
outline: none;
border-color: #333;
}
</style>
JavaScript
//MovieSearch.vue
<template>
<div class="search-bar">
<input
type="text"
placeholder="Search for a Movie..."
class="search"
@input="searchInput"
@keypress.enter="search"
/>
</div>
</template>
<script>
export default {
name: "MovieSearch",
methods: {
searchInput(e) {
this.$emit("searchInput", e);
},
search(e) {
this.$emit("search", e);
},
},
};
</script>
<style scoped>
.search-bar {
margin: 20px auto;
max-width: 400px;
}
.search {
width: 100%;
padding: 10px;
font-size: 16px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
transition: border-color 0.3s ease;
}
.search:focus {
outline: none;
border-color: #333;
}
</style>
JavaScript
//MovieDetail.vue
<template>
<div class="modal"
@click.self="closeDetail">
<div class="modal-content">
<span class="close"
@click="closeDetail">×</span>
<h2>{{ selected.Title }}</h2>
<span>{{ selected.Year }}</span>
<p class="rating">Rating:
{{ selected.imdbRating }}</p>
<div class="about">
<img :src="selected.Poster" alt="Movie Poster" />
<p>{{ selected.Plot }}</p>
</div>
</div>
</div>
</template>
<script>
export default {
name: "MovieDetail",
props: {
selected: Object,
},
methods: {
closeDetail() {
this.$emit("closeDetail");
},
},
};
</script>
<style scoped>
.modal {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
z-index: 999;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
background-color: #fefefe;
border-radius: 8px;
padding: 20px;
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.close {
position: absolute;
top: 10px;
right: 10px;
font-size: 20px;
cursor: pointer;
}
</style>
Run the Project:
npm run dev Output:
|