最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Is there no way to create components dynamically using only Astro? - Stack Overflow

programmeradmin2浏览0评论

I’m working on an Astro project where I’m building a classic Pokédex. I have a PokemonCard.astro component that renders a Pokémon's details, and I’m trying to load more Pokémon dynamically when a "Load More" button is clicked. However, I’m currently creating the HTML elements manually in JavaScript, which feels redundant since I already have a PokemonCard component (I tried to reuse my component in the script but it doesnt work).

Is there a way to dynamically create and render Astro components (like PokemonCard) in the browser without manually creating the HTML elements? If not, what’s the best approach to achieve this?

Here’s the code:

---
import Layout from '../layouts/Layout.astro';
import PokemonCard from '../components/PokemonCard.astro';
import { getPokemons } from '../lib/controllers/pokemonController';
import type { PokemonSmall } from '../lib/models/pokemonModels';



const pokemons: PokemonSmall[] | undefined = await getPokemons(0, 12);
---

<Layout title="Pokedex">
<main class="m-auto">
    <section id="pokemon-grid">
        <div class="grid grid-cols-2 gap-7 p-2 mt-32
        md:grid-cols-4">
            {
                pokemons?.map((pokemon : PokemonSmall) => (
                        <PokemonCard {pokemon}/>
            ))
            }
        </div>
    </section>
    <section class="flex justify-center items-center">
        <button id="load-more-pkmn"
        class="p-4 bg-slate-400/20 border-gray-500 border rounded-2xl my-4 
        transition-transform transform hover:scale-105">Cargar más pokémons</button>
    </section>

</main>
</Layout>

<script>
import { getPokemons } from "../lib/controllers/pokemonController";
import { TypeColors, type PokemonSmall, type PokemonType } from "../lib/models/pokemonModels";
import { capitalizeFirstLetter, mapId } from "../lib/utils/utils";


    let offset = 12; 
    const limit = 12;

    const loadMorePkmn = document.getElementById('load-more-pkmn');
    if(loadMorePkmn) {

        loadMorePkmn.addEventListener('click', async () => {

            const pokemons : PokemonSmall [] | undefined = await getPokemons(offset, limit);
            offset += 12;

            const pokemonGrid = document.getElementById('pokemon-grid');

            const divPokemons = document.createElement('div');
            divPokemons.className = 'grid grid-cols-2 gap-7 p-2 md:grid-cols-4';
            
            pokemons?.map((pokemon : PokemonSmall) => {

                console.log(pokemon)

                const a = document.createElement('a');
                a.className = 'w-60 h-60 p-1 flex flex-col items-center bg-slate-400/10 border-gray-500 border rounded-2xl hover:bg-gray-200 cursor-pointer';
                const image = document.createElement('img');
                image.className = 'w-28';
                const h3 = document.createElement('h3');
                h3.className = 'text-2xl font-bold tracking-wide mt-1';
                const p = document.createElement('p');
                p.className = 'text-xs tracking-wide p-1';
                const divTypes = document.createElement('div');
                divTypes.className = 'flex flex-row space-x-1 mt-2 p-1 gap-2';


                a.href = `pokemon/${pokemon.id}`;   
                image.src = pokemon.image; image.alt = `Una foto de ${pokemon.name}`;
                a.appendChild(image);
                h3.innerText = capitalizeFirstLetter(pokemon.name);
                a.appendChild(h3);
                p.innerText = `No. ${mapId(pokemon.id)}`;
                a.appendChild(p);

                pokemon.types.map((types : PokemonType) => {

                    const pType = document.createElement('p');
                    pType.className = ` ${TypeColors[types.type.name]} opacity-80 rounded text-white text-center font-medium tracking-wide py-1 px-2`;
                    pType.innerText = types.type.name;
                    divTypes.appendChild(pType);
                   
                });
                a.appendChild(divTypes);
                
                
                divPokemons.appendChild(a);
            });

            pokemonGrid?.appendChild(divPokemons);
        });
    }
</script>

I thought about using a frontend framework like React or Vue, but I’d like to stick to Astro if possible.

I’m working on an Astro project where I’m building a classic Pokédex. I have a PokemonCard.astro component that renders a Pokémon's details, and I’m trying to load more Pokémon dynamically when a "Load More" button is clicked. However, I’m currently creating the HTML elements manually in JavaScript, which feels redundant since I already have a PokemonCard component (I tried to reuse my component in the script but it doesnt work).

Is there a way to dynamically create and render Astro components (like PokemonCard) in the browser without manually creating the HTML elements? If not, what’s the best approach to achieve this?

Here’s the code:

---
import Layout from '../layouts/Layout.astro';
import PokemonCard from '../components/PokemonCard.astro';
import { getPokemons } from '../lib/controllers/pokemonController';
import type { PokemonSmall } from '../lib/models/pokemonModels';



const pokemons: PokemonSmall[] | undefined = await getPokemons(0, 12);
---

<Layout title="Pokedex">
<main class="m-auto">
    <section id="pokemon-grid">
        <div class="grid grid-cols-2 gap-7 p-2 mt-32
        md:grid-cols-4">
            {
                pokemons?.map((pokemon : PokemonSmall) => (
                        <PokemonCard {pokemon}/>
            ))
            }
        </div>
    </section>
    <section class="flex justify-center items-center">
        <button id="load-more-pkmn"
        class="p-4 bg-slate-400/20 border-gray-500 border rounded-2xl my-4 
        transition-transform transform hover:scale-105">Cargar más pokémons</button>
    </section>

</main>
</Layout>

<script>
import { getPokemons } from "../lib/controllers/pokemonController";
import { TypeColors, type PokemonSmall, type PokemonType } from "../lib/models/pokemonModels";
import { capitalizeFirstLetter, mapId } from "../lib/utils/utils";


    let offset = 12; 
    const limit = 12;

    const loadMorePkmn = document.getElementById('load-more-pkmn');
    if(loadMorePkmn) {

        loadMorePkmn.addEventListener('click', async () => {

            const pokemons : PokemonSmall [] | undefined = await getPokemons(offset, limit);
            offset += 12;

            const pokemonGrid = document.getElementById('pokemon-grid');

            const divPokemons = document.createElement('div');
            divPokemons.className = 'grid grid-cols-2 gap-7 p-2 md:grid-cols-4';
            
            pokemons?.map((pokemon : PokemonSmall) => {

                console.log(pokemon)

                const a = document.createElement('a');
                a.className = 'w-60 h-60 p-1 flex flex-col items-center bg-slate-400/10 border-gray-500 border rounded-2xl hover:bg-gray-200 cursor-pointer';
                const image = document.createElement('img');
                image.className = 'w-28';
                const h3 = document.createElement('h3');
                h3.className = 'text-2xl font-bold tracking-wide mt-1';
                const p = document.createElement('p');
                p.className = 'text-xs tracking-wide p-1';
                const divTypes = document.createElement('div');
                divTypes.className = 'flex flex-row space-x-1 mt-2 p-1 gap-2';


                a.href = `pokemon/${pokemon.id}`;   
                image.src = pokemon.image; image.alt = `Una foto de ${pokemon.name}`;
                a.appendChild(image);
                h3.innerText = capitalizeFirstLetter(pokemon.name);
                a.appendChild(h3);
                p.innerText = `No. ${mapId(pokemon.id)}`;
                a.appendChild(p);

                pokemon.types.map((types : PokemonType) => {

                    const pType = document.createElement('p');
                    pType.className = ` ${TypeColors[types.type.name]} opacity-80 rounded text-white text-center font-medium tracking-wide py-1 px-2`;
                    pType.innerText = types.type.name;
                    divTypes.appendChild(pType);
                   
                });
                a.appendChild(divTypes);
                
                
                divPokemons.appendChild(a);
            });

            pokemonGrid?.appendChild(divPokemons);
        });
    }
</script>

I thought about using a frontend framework like React or Vue, but I’d like to stick to Astro if possible.

Share Improve this question asked Jan 29 at 23:18 emmyemmy 211 silver badge2 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

There is the HTML dialog element (in all modern browsers) and popover attribute (in Firefox, Chrome and Safari >= 17).

Or alternatively, you can create the HTML for your PokemonCard normally in the Astro component, but hide it with CSS. Then you just add a few lines of JavaScript to unhide. Assuming a function getPokemons(startIndex, endIndex):

import PokemonCard from '../components/PokemonCard.astro';
import { getPokemons } from '../lib/controllers/pokemonController';

---

{getPokemons(0, 10).map(pokemon =>
  <PokemonCard pokemon={pokemon}/>}

<button id="showMoreBtn">Show more</button>

<div id="pokemon" style="display: hidden;">
  {getPokemons(10, 20).map(pokemon =>
    <PokemonCard pokemon={pokemon}/>}
</div>


<script>
  document.getElementById('showMoreBtn')?.addEventListener('click', () => {
    document.getElementById('pokemon').style.display = 'block';
  })
</script>
发布评论

评论列表(0)

  1. 暂无评论