You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在RxJS中页面加载时初始显示对象数组中的数据?

RxJS项目初始加载无数据问题修复

问题说明

我用JavaScript制作了一个简单的RxJS项目,用户可搜索游戏并按需筛选数据。所有筛选功能正常,但页面首次加载时数据无法初始显示。我尝试使用startWith()操作符来实现页面加载时的初始结果展示,但未达到预期效果,仅当用户搜索或添加筛选条件时数据才会加载。

原代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.8.0/rxjs.umd.min.js"
        integrity="sha512-v0/YVjBcbjLN6scjmmJN+h86koeB7JhY4/2YeyA5l+rTdtKLv0VbDBNJ32rxJpsaW1QGMd1Z16lsLOSGI38Rbg=="
        crossorigin="anonymous" referrerpolicy="no-referrer"></script>>
</head>


<body>

    <h1>Games</h1>
    <input type="text" name="" id="search_game" placeholder="search games">
    <label for="">
        <input type="checkbox" id="party">Party</label>
    <select id="studio">
        <option value="All">All</option>
        <option value="Lucid Sheep Games">Lucid Sheep Games</option>
        <option value="Beatshapers">Beatshapers</option>
        <option value="QubicGames">QubicGames</option>
    </select>
    <p id="test"></p>
    <table cellpadding="10">
        <thead>
            <tr>
                <th style="text-align:left;" id="game_name">Name</th>
                <th style="text-align:left;">Released</th>
                <th style="text-align:left;">Genre</th>
            </tr>
        <tbody id="table-content">

        </tbody>
        </thead>
    </table>
</body>
<script>
const { of, fromEvent, ajax: { ajax }, operators: { startWith, switchMap, map, debounce }, combineLatest } = rxjs;

const searchGame = document.getElementById('search_game');
const partyGenre = document.getElementById('party');
const studioInput = document.getElementById('studio')
const studioFlag=true
// const result=search.subscribe(val=>console.log(val))
// search.pipe((switchMap(searchStr=>ajax.getJSON('https://api.sampleapis.com/switch/games', console.log(searchStr)))))
const search = fromEvent(searchGame, 'input').pipe(map(e => e.target.value.toString().toLowerCase()), startWith(''))
// search.subscribe(e=>console.log(e)); to log the input.
const partyGame = fromEvent(partyGenre, 'input').pipe(map(e => e.target.checked), startWith(false));
const studio = fromEvent(studioInput,'change').pipe(map(e=>e.target.value),startWith(''));

const games = ajax.getJSON('https://api.sampleapis.com/switch/games');
// const lowerCaseGames = games.map(games)

//Search input 
combineLatest([games, search]).pipe(map(([games, search]) => games.filter(game => game.name.toString().toLowerCase().indexOf(search) != -1))).subscribe(gamesList => {
    let tableContent = '';
    gamesList.forEach(game => {
        tableContent += `<tr>`;
        tableContent += `<td>` + game.name + `</td>`
        tableContent += `<td>` + (!game.releaseDates.Japan + ' ' + '(Japan)' ? game.releaseDates.Europe + ' ' + `(Europe)` : game.releaseDates.Australia + ' ' + `(Australia)`) + `</td>`
        tableContent += `<td>` + game.genre + `</td>`
        // console.log(game.publishers[0]);
    })
    document.getElementById('table-content').innerHTML = tableContent
    console.log(gamesList);;
})
// Checkbox input
combineLatest([games, partyGame,studio]).pipe(map(([games, partyGame]) => games.filter(game => game.genre[0] === 'Party' ? partyGame === true : partyGame === false))).subscribe(
    gamesList => {
        let tableContent = '';
        gamesList.forEach(game => {
            tableContent += `<tr>`;
            tableContent += `<td>` + game.name + `</td>`
            tableContent += `<td>` + (!game.releaseDates.Japan + ' ' + '(Japan)' ? game.releaseDates.Europe + ' ' + `(Europe)` : game.releaseDates.Australia + ' ' + `(Australia)`) + `</td>`
            tableContent += `<td>` + game.genre + `</td>`
        })
        document.getElementById('table-content').innerHTML = tableContent
    })
    

// Select input
combineLatest([games,studio]).pipe(map(([games,studio])=> games.filter( game=>game.publishers[0] === studio ))).subscribe(
    gamesList => {
        let tableContent = '';
        gamesList.forEach(game => {
            tableContent += `<tr>`;
            tableContent += `<td>` + game.name + `</td>`
            tableContent += `<td>` + (!game.releaseDates.Japan + ' ' + '(Japan)' ? game.releaseDates.Europe + ' ' + `(Europe)` : game.releaseDates.Australia + ' ' + `(Australia)`) + `</td>`
            tableContent += `<td>` + game.genre + `</td>`
        })
        document.getElementById('table-content').innerHTML = tableContent
        console.log(gamesList);
    }
)
</script>

</html>

问题原因与修复方案

核心问题

  1. 多个订阅互相覆盖DOM:三个独立的combineLatest订阅都会更新同一个表格,初始加载时工作室筛选流的空字符串初始值导致筛选结果为空,直接清空了前两个订阅渲染的内容。
  2. studio流初始值错误:下拉框默认选中All,但流的初始值设为空字符串,与默认选项不匹配。
  3. 筛选逻辑分散:分开处理各个筛选条件,无法同时应用所有规则,还容易引发DOM冲突。

修复步骤

  1. 统一筛选流与订阅:将所有筛选流和游戏数据合并到一个combineLatest中,统一处理筛选逻辑。
  2. 修正studio流初始值:改为startWith('All'),匹配下拉框默认选项。
  3. 合并筛选逻辑:在同一个map操作中同时应用搜索、派对游戏、工作室三个筛选条件。
  4. 修复日期显示逻辑:修正原代码中错误的日期判断逻辑,改为优先显示存在的地区日期。

修复后的代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.8.0/rxjs.umd.min.js"
        integrity="sha512-v0/YVjBcbjLN6scjmmJN+h86koeB7JhY4/2YeyA5l+rTdtKLv0VbDBNJ32rxJpsaW1QGMd1Z16lsLOSGI38Rbg=="
        crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>


<body>

    <h1>Games</h1>
    <input type="text" name="" id="search_game" placeholder="search games">
    <label for="">
        <input type="checkbox" id="party">Party</label>
    <select id="studio">
        <option value="All">All</option>
        <option value="Lucid Sheep Games">Lucid Sheep Games</option>
        <option value="Beatshapers">Beatshapers</option>
        <option value="QubicGames">QubicGames</option>
    </select>
    <p id="test"></p>
    <table cellpadding="10">
        <thead>
            <tr>
                <th style="text-align:left;" id="game_name">Name</th>
                <th style="text-align:left;">Released</th>
                <th style="text-align:left;">Genre</th>
            </tr>
        <tbody id="table-content">

        </tbody>
        </thead>
    </table>
</body>
<script>
const { of, fromEvent, ajax: { ajax }, operators: { startWith, map }, combineLatest } = rxjs;

const searchGame = document.getElementById('search_game');
const partyGenre = document.getElementById('party');
const studioInput = document.getElementById('studio');

// 初始化各个筛选流,设置正确的初始值
const search$ = fromEvent(searchGame, 'input').pipe(
    map(e => e.target.value.toString().toLowerCase()),
    startWith('')
);
const partyGame$ = fromEvent(partyGenre, 'input').pipe(
    map(e => e.target.checked),
    startWith(false)
);
const studio$ = fromEvent(studioInput,'change').pipe(
    map(e => e.target.value),
    startWith('All') // 匹配下拉框默认选项
);

const games$ = ajax.getJSON('https://api.sampleapis.com/switch/games');

// 合并所有流,统一处理筛选逻辑
combineLatest([games$, search$, partyGame$, studio$]).pipe(
    map(([games, searchText, showPartyOnly, selectedStudio]) => {
        return games.filter(game => {
            // 搜索筛选:名称包含搜索文本
            const matchesSearch = game.name.toLowerCase().includes(searchText);
            // 派对游戏筛选:复选框勾选时只显示派对游戏,否则显示全部
            const matchesParty = showPartyOnly ? game.genre.includes('Party') : true;
            // 工作室筛选:选择All时显示全部,否则匹配发行商
            const matchesStudio = selectedStudio === 'All' ? true : game.publishers.includes(selectedStudio);
            
            return matchesSearch && matchesParty && matchesStudio;
        });
    })
).subscribe(gamesList => {
    let tableContent = '';
    gamesList.forEach(game => {
        tableContent += `<tr>`;
        tableContent += `<td>${game.name}</td>`;
        // 修正日期显示逻辑:优先显示日本,其次欧洲,最后澳大利亚
        const releaseDate = game.releaseDates.Japan 
            ? `${game.releaseDates.Japan} (Japan)` 
            : game.releaseDates.Europe 
                ? `${game.releaseDates.Europe} (Europe)` 
                : `${game.releaseDates.Australia} (Australia)`;
        tableContent += `<td>${releaseDate}</td>`;
        tableContent += `<td>${game.genre.join(', ')}</td>`;
        tableContent += `</tr>`;
    });
    document.getElementById('table-content').innerHTML = tableContent;
});
</script>

</html>

内容的提问来源于stack exchange,提问作者Coder_7

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 01:24:30