用React来编写:
使用 React 来实现一个类似的滚动加载列表组件。我们将创建一个名为 ScrollableList 的组件。
import React, { useState, useEffect } from 'react';
import './ScrollableList.css';
const ScrollableList = ({ columns, fetchData }) => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const loadMoreData = async () => {
if (loading || !hasMore) return;
setLoading(true);
try {
const newData = await fetchData();
setData(prevData => [...prevData, ...newData]);
setHasMore(newData.length > 0);
} catch (error) {
console.error('Error fetching data:', error);
} finally {
setLoading(false);
}
};
// Initial data load
useEffect(() => {
loadMoreData();
}, []);
const renderRow = (rowData, index) => {
return (
<div key={index} className="list-row">
{columns.map((column, columnIndex) => (
<div key={columnIndex} className="list-cell">
{rowData[column]}
</div>
))}
</div>
);
};
const handleScroll = (e) => {
const { scrollTop, clientHeight, scrollHeight } = e.target;
if (scrollHeight - scrollTop === clientHeight) {
loadMoreData();
}
};
return (
<div className="scrollable-list" onScroll={handleScroll}>
<div className="list-header">
{columns.map((column, index) => (
<div key={index} className="list-header-cell">
{column}
</div>
))}
</div>
<div className="list-body">
{data.map((rowData, index) => renderRow(rowData, index))}
{loading && <div className="loading">Loading...</div>}
{!hasMore && <div className="end-message">No more items</div>}
</div>
</div>
);
};
export default ScrollableList;
然后,创建一个名为 ScrollableList.css 的 CSS 文件来定义列表组件的样式:
.scrollable-list {
width: 100%;
max-height: 300px;
overflow-y: auto;
}
.list-header {
display: flex;
border-bottom: 1px solid #ccc;
font-weight: bold;
}
.list-header-cell {
flex: 1;
padding: 8px;
text-align: center;
border-right: 1px solid #ccc;
}
.list-body {
display: flex;
flex-direction: column;
}
.list-row {
display: flex;
border-bottom: 1px solid #eee;
}
.list-cell {
flex: 1;
padding: 8px;
text-align: center;
border-right: 1px solid #eee;
}
.loading {
padding: 10px;
text-align: center;
}
.end-message {
padding: 10px;
text-align: center;
color: #999;
}
现在,我们来使用这个 ScrollableList 组件。在父组件中,你可以传递 columns 和 fetchData 方法给 ScrollableList:
import React, { useState } from 'react';
import ScrollableList from './ScrollableList';
const App = () => {
const [page, setPage] = useState(1);
const fetchData = async () => {
const response = await fetch(`https://api.example.com/data?page=${page}`);
const newData = await response.json();
setPage(prevPage => prevPage + 1);
return newData;
};
const columns = ['Name', 'Age', 'Location']; // Customize columns here
return (
<div className="App">
<h1>Scrollable List</h1>
<ScrollableList columns={columns} fetchData={fetchData} />
</div>
);
};
export default App;
在这个示例中,我们假设 fetchData 函数从服务器获取数据,并且服务器端根据 page 参数返回不同的数据。你可以根据实际需求来自定义 fetchData 函数和 columns。
这个 ScrollableList 组件会在滚动到底部时加载更多数据,直到服务器返回的数据为空为止。同时,根据传入的 columns 参数,会自动根据列数来渲染列表。
用Angular来编写
使用 Angular 来实现一个类似的滚动加载列表组件。我们将创建一个名为 ScrollableListComponent 的组件。
首先,创建 scrollable-list.component.ts 文件:
import { Component, Input, OnInit, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core';
@Component({
selector: 'app-scrollable-list',
templateUrl: './scrollable-list.component.html',
styleUrls: ['./scrollable-list.component.css']
})
export class ScrollableListComponent implements OnInit {
@Input() columns: string[] = [];
@Output() fetchData: EventEmitter<any> = new EventEmitter<any>();
data: any[] = [];
loading: boolean = false;
hasMore: boolean = true;
@ViewChild('scrollContainer') scrollContainer!: ElementRef;
constructor() { }
ngOnInit(): void {
this.loadMoreData();
}
loadMoreData() {
if (this.loading || !this.hasMore) return;
this.loading = true;
this.fetchData.emit().subscribe((newData: any[]) => {
this.data = [...this.data, ...newData];
this.hasMore = newData.length > 0;
this.loading = false;
});
}
onScroll() {
const scrollContainer = this.scrollContainer.nativeElement;
if (scrollContainer.scrollTop + scrollContainer.clientHeight === scrollContainer.scrollHeight) {
this.loadMoreData();
}
}
}
接着,创建 scrollable-list.component.html 文件:
<div class="scrollable-list" (scroll)="onScroll()" #scrollContainer>
<div class="list-header">
<div *ngFor="let column of columns" class="list-header-cell">
{{ column }}
</div>
</div>
<div class="list-body">
<div *ngFor="let item of data; let i = index" class="list-row">
<div *ngFor="let column of columns; let j = index" class="list-cell">
{{ item[column] }}
</div>
</div>
<div *ngIf="loading" class="loading">Loading...</div>
<div *ngIf="!hasMore" class="end-message">No more items</div>
</div>
</div>
最后,创建 scrollable-list.component.css 文件:
.scrollable-list {
width: 100%;
max-height: 300px;
overflow-y: auto;
}
.list-header {
display: flex;
border-bottom: 1px solid #ccc;
font-weight: bold;
}
.list-header-cell {
flex: 1;
padding: 8px;
text-align: center;
border-right: 1px solid #ccc;
}
.list-body {
display: flex;
flex-direction: column;
}
.list-row {
display: flex;
border-bottom: 1px solid #eee;
}
.list-cell {
flex: 1;
padding: 8px;
text-align: center;
border-right: 1px solid #eee;
}
.loading {
padding: 10px;
text-align: center;
}
.end-message {
padding: 10px;
text-align: center;
color: #999;
}
现在我们可以在父组件中使用这个 ScrollableListComponent:
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Component({
selector: 'app-root',
template: `
<h1>Scrollable List</h1>
<app-scrollable-list [columns]="columns" (fetchData)="fetchData($event)"></app-scrollable-list>
`,
styleUrls: ['./app.component.css']
})
export class AppComponent {
columns: string[] = ['Name', 'Age', 'Location'];
page: number = 1;
constructor(private http: HttpClient) {}
fetchData(): Observable<any[]> {
const url = `https://api.example.com/data?page=${this.page}`;
return this.http.get<any[]>(url);
}
}
在这个示例中,我们假设 fetchData 方法从服务器获取数据,并且服务器端根据 page 参数返回不同的数据。你可以根据实际需求自定义 fetchData 方法和 columns。
这个 ScrollableListComponent 组件会在滚动到底部时加载更多数据,直到服务器返回的数据为空为止。同时,根据传入的 columns 参数,会自动根据列数来渲染列表。
子组件:this.fetchData.emit().subscribe((newData: any[]) => {
this.data = [...this.data, ...newData];
this.hasMore = newData.length > 0;
this.loading = false;
});
父组件:
<app-scrollable-list [columns]="columns" (fetchData)="fetchData($event)"></app-scrollable-list>
fetchData(): Observable<any[]> {
const url = `https://api.example.com/data?page=${this.page}`;
return this.http.get<any[]>(url);
}
在这段代码中,this.fetchData.emit() 是通过输出属性 fetchData 发射一个事件,将这个事件传递给了父组件。在父组件中,这个事件被绑定到了一个方法 fetchData() 上。所以实际上,this.fetchData.emit() 的作用是通知父组件调用 fetchData() 方法。
而在 subscribe((newData: any[]) => { ... }) 中,确实是在订阅了父组件中 fetchData() 方法返回的 Observable。在 fetchData() 方法中,发送了一个 HTTP GET 请求,获取了数据。当这个 HTTP 请求返回数据时,subscribe 中的回调函数会被执行,其中的 newData 就是从服务器返回的数据。
接着,在这个订阅回调函数中,代码做了以下几件事情:
- 将新获取的数据 newData 与已有的数据 this.data 合并,使用了展开运算符 [...this.data, ...newData],将它们连接成一个新的数组,这样就保留了之前已经加载的数据,并加上了新加载的数据。
- 根据新加载的数据 newData 的长度判断是否还有更多数据,如果 newData.length 大于 0,则说明还有数据可以加载,设置 this.hasMore 为 true,否则设置为 false。
- 最后,将 loading 设置为 false,表示数据加载完成,不再处于加载状态。
用Vue3来编写
使用 Vue 3 来实现一个类似的滚动加载列表组件。我们将创建一个名为 ScrollableList 的组件。
首先,创建 ScrollableList.vue 文件:
<template>
<div class="scrollable-list" @scroll="onScroll" ref="scrollContainer">
<div class="list-header">
<div v-for="column in columns" :key="column" class="list-header-cell">
{{ column }}
</div>
</div>
<div class="list-body">
<div v-for="(item, index) in data" :key="index" class="list-row">
<div v-for="column in columns" :key="column" class="list-cell">
{{ item[column] }}
</div>
</div>
<div v-if="loading" class="loading">Loading...</div>
<div v-if="!hasMore" class="end-message">No more items</div>
</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
export default {
props: {
columns: {
type: Array,
required: true
},
fetchData: {
type: Function,
required: true
}
},
setup(props) {
const data = ref([]);
const loading = ref(false);
const hasMore = ref(true);
const scrollContainer = ref(null);
const loadMoreData = async () => {
if (loading.value || !hasMore.value) return;
loading.value = true;
try {
const newData = await props.fetchData();
data.value = [...data.value, ...newData];
hasMore.value = newData.length > 0;
} catch (error) {
console.error('Error fetching data:', error);
} finally {
loading.value = false;
}
};
const onScroll = () => {
const container = scrollContainer.value;
if (container.scrollTop + container.clientHeight === container.scrollHeight) {
loadMoreData();
}
};
onMounted(() => {
loadMoreData();
});
return {
data,
loading,
hasMore,
onScroll,
scrollContainer
};
}
};
</script>
<style>
.scrollable-list {
width: 100%;
max-height: 300px;
overflow-y: auto;
}
.list-header {
display: flex;
border-bottom: 1px solid #ccc;
font-weight: bold;
}
.list-header-cell {
flex: 1;
padding: 8px;
text-align: center;
border-right: 1px solid #ccc;
}
.list-body {
display: flex;
flex-direction: column;
}
.list-row {
display: flex;
border-bottom: 1px solid #eee;
}
.list-cell {
flex: 1;
padding: 8px;
text-align: center;
border-right: 1px solid #eee;
}
.loading {
padding: 10px;
text-align: center;
}
.end-message {
padding: 10px;
text-align: center;
color: #999;
}
</style>
现在我们可以在父组件中使用这个 ScrollableList 组件:
<template>
<div class="app">
<h1>Scrollable List</h1>
<ScrollableList :columns="columns" :fetch-data="fetchData" />
</div>
</template>
<script>
import { ref } from 'vue';
import ScrollableList from './components/ScrollableList.vue';
export default {
components: {
ScrollableList
},
setup() {
const columns = ref(['Name', 'Age', 'Location']);
const page = ref(1);
const fetchData = async () => {
const response = await fetch(`https://api.example.com/data?page=${page.value}`);
const newData = await response.json();
page.value += 1;
return newData;
};
return {
columns,
fetchData
};
}
};
</script>
<style>
.app {
display: flex;
flex-direction: column;
align-items: center;
}
</style>
在这个示例中,我们假设 fetchData 方法从服务器获取数据,并且服务器端根据 page 参数返回不同的数据。你可以根据实际需求自定义 fetchData 方法和 columns。
这个 ScrollableList 组件会在滚动到底部时加载更多数据,直到服务器返回的数据为空为止。同时,根据传入的 columns 参数,会自动根据列数来渲染列表。