Technologies to use:
- Vue CLI, How to install it here
- Sass CSS
- Vuex
Step 1
Generate a project with vue cli:
vue create infinite-scroll
Select this option
? Please pick a preset: (Use arrow keys)
tipical (node-sass, babel, router, vuex)
default (babel, eslint)
> Manually select features
Select these with tab space, then enter
? Check the features needed for your project:
> ◉ Babel
◯ TypeScript
◯ Progressive Web App (PWA) Support
◯ Router
> ◉ Vuex
> ◉ CSS Pre-processors
◯ Linter / Formatter
◯ Unit Testing
◯ E2E Testing
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default):
Sass/SCSS (with dart-sass)
> Sass/SCSS (with node-sass)
Less
Stylus
Step 2
In our components folder we will add this 2 components:
Button.vue
<template>
<button
v-if="ui !== 'submit'"
@click="btnClick"
:type="type"
:class="ui ? 'btn__' + ui : 'btn__primary'"
>
{{ text }}
</button>
<button
v-else
@click="btnClick"
formData.action="/submit"
:type="type"
:class="ui ? 'btn__' + ui : 'btn__primary'"
>
{{ text }}
</button>
</template>
<script>
export default {
name: "btn-fc",
props: ["text", "type", "disabled", "ui"],
methods: {
btnClick() {
this.$emit("btnClick");
},
},
};
</script>
CardItem.vue
<template>
<div class="carditem">
<div class="carditem__image">
<img :src="pathBase+'futurama'+character.id+'.jpg'" />
</div>
<div class="carditem__info">
<strong class="carditem__title">{{ character.name.first }} {{ character.name.last }}</strong>
<span>{{ character.occupation }}</span>
<span>{{ character.species }}</span>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
export default {
name: "CardItem",
props: {
character: {
type: Object,
},
index: Number,
showPage: {
type: Boolean,
default: false,
},
},
data() {
return {
pathBase: "https://res.cloudinary.com/lariicsa/image/upload/v1602727260/futurama/",
charImages: [
{ name: "Philip", url: "" },
{ name: "Turanga", url: "" },
{ name: "Bender", url: "futurama4_pxj04y.jpg" },
{ name: "Hubert", url: "" },
{ name: "Amy", url: "" },
{ name: "Hermes", url: "" },
{ name: "Carol", url: "" },
{ name: "John", url: "" },
{ name: "Zapp", url: "" },
{ name: "Scruffy", url: "" },
{ name: "Cubert", url: "" },
{ name: "Kif", url: "" },
{ name: "Dwight", url: "" },
{ name: "LaBarbara", url: "" },
],
};
},
mounted() {
this.index;
},
};
</script>
Step 3
In a new folder called assets we will add new folder called scss and then add these scss files
And we will modify the main.js file
import Vue from 'vue'
import axios from "axios";
import VueAxios from "vue-axios";
import App from './App.vue'
import Vuex from 'vuex'
import router from './router'
import store from './store'
import "./assets/scss/index.scss";
Vue.use(axios);
Vue.use(Vuex)
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
Step 4
We will create a new folder called service and in a index.js file we will call our service from a Futurama API from sampleapis.com
import axios from "axios";
const headers = {
"Content-Type": "application/json",
};
const baseURL = () => {
(axios.defaults.baseURL = "https://sampleapis.com/futurama/api"), headers;
};
const FUTURAMA_SERVICE = axios.create(baseURL());
const GET_DIMENSION = "/characters";
const characterInfo = (idCharacter) => FUTURAMA_SERVICE.get(GET_DIMENSION+idCharacter);
const characterAll = () => FUTURAMA_SERVICE.get(GET_DIMENSION);
export { characterInfo, characterAll };
And in our Store folder at file index.js we will add next:
import Vue from 'vue'
import Vuex from 'vuex'
import { characterInfo, characterAll } from "../service/index"
Vue.use(Vuex)
export default new Vuex.Store({
state: {
characterInfo: {},
charactersList: []
},
mutations: {
setCharactersList:(state, payload) => (state.charactersList = payload),
setCharacterInfo:(state, payload) => (state.characterInfo = payload)
},
actions: {
async getCharactersList({commit}) {
try {
const response = await characterAll()
const list = response.data
commit("setCharactersList", list)
console.log('all',list);
} catch (error) {
console.log(error.response);
}
},
async getCharactersDimension({ commit }, idCharacter) {
try {
const response = await characterInfo(idCharacter)
console.log('response', response);
} catch (error) {
console.log(error.response);
}
}
},
getters: {
gtrCharacters(state) {
return state.charactersList
}
}
})
Step 5
Now we will create our important component InfiniteScroll.vue
<template>
<div class="infscroll__container">
<div id="infinite-list" class="infscroll__items">
<transition name="fade">
<div class="infscroll__loader-bg" v-show="showLoading">
<div class="infscroll__loader">{{ textLoading }}</div>
</div>
</transition>
<slot></slot>
<ButtonFan
v-show="isMobile"
@btnClick="loadMore()"
text="Load more ..."
ui="primary infscroll"
/>
</div>
</div>
</template>
<script>
import ButtonFan from "@/components/Button";
export default {
name: "InfiniteScroll",
components: {
ButtonFan,
},
props: {
showLoading: {
type: Boolean,
default: false,
},
textLoading: {
type: String,
default: "Loading ...",
},
},
data() {
return {
isMobile: false,
};
},
mounted() {
const listElm = document.querySelector("#infinite-list");
listElm.addEventListener("scroll", (e) => {
if (
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent
)
) {
if (listElm.scrollTop + listElm.clientHeight >= listElm.scrollHeight) {
this.isMobile = true;
}
} else {
if (listElm.scrollTop + listElm.clientHeight >= listElm.scrollHeight) {
this.loadMore();
}
}
});
},
methods: {
loadMore() {
this.$emit("loadMore");
},
},
};
</script>
Step 6
And finally we will set the Home View in Home.vue
<template>
<div class="container">
<div class="row center">
<h1>Futurama <span>{{cards.length}} from {{gtrCharacters.length}}</span></h1>
</div>
<div class="row center">
<InfiniteScroll :showLoading="loading" @loadMore="loadMore()">
<div class="row between">
<CardItem
v-for="(character, index) in cards"
:character="character"
:key="index"
:index="parseInt(index + 1)"
/>
</div>
</InfiniteScroll>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters, mapState, mapMutations } from "vuex";
import CardItem from "@/components/CardItem";
import InfiniteScroll from "@/components/InfiniteScroll";
export default {
name: "Home",
components: {
CardItem,
InfiniteScroll,
},
data() {
return {
loading: false,
items: [],
upto: 6,
pages: [],
pageArea: "",
isMobile: false,
};
},
static: {
limitScrollItems: 6,
},
async created() {
await this.getCharactersList();
},
mounted() {
this.cards;
},
methods: {
...mapActions(["getCharactersList"]),
loadMore() {
const listItems = document.querySelector("#infinite-list");
let heightToTop = listItems.scrollTop;
if (
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent
)
) {
setTimeout((e) => {
listItems.scrollTo(0, heightToTop);
}, 1000);
}
this.loading = true;
setTimeout((e) => {
const card = this.cards.map((item) => {
this.cards.push(item);
});
this.upto += this.$options.static.limitScrollItems;
this.loading = false;
}, 800);
},
},
computed: {
...mapState(["charactersList"]),
...mapGetters(["gtrCharacters"]),
cards() {
const card = this.gtrCharacters.slice(0, this.upto).map((item) => {
return item;
});
return card;
},
},
};
</script>
And that's all folks!
You can check the code here https://github.com/Lariicsa/infinite-scroll/blob/master/src/components/InfiniteScroll.vue
And the demo here Infinite Scroll Futurama
Top comments (0)