如何在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>
问题原因与修复方案
核心问题
- 多个订阅互相覆盖DOM:三个独立的
combineLatest订阅都会更新同一个表格,初始加载时工作室筛选流的空字符串初始值导致筛选结果为空,直接清空了前两个订阅渲染的内容。 studio流初始值错误:下拉框默认选中All,但流的初始值设为空字符串,与默认选项不匹配。- 筛选逻辑分散:分开处理各个筛选条件,无法同时应用所有规则,还容易引发DOM冲突。
修复步骤
- 统一筛选流与订阅:将所有筛选流和游戏数据合并到一个
combineLatest中,统一处理筛选逻辑。 - 修正
studio流初始值:改为startWith('All'),匹配下拉框默认选项。 - 合并筛选逻辑:在同一个
map操作中同时应用搜索、派对游戏、工作室三个筛选条件。 - 修复日期显示逻辑:修正原代码中错误的日期判断逻辑,改为优先显示存在的地区日期。
修复后的代码
<!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
相关产品推荐
相关产品推荐

