Avatar billede michael_r Nybegynder
30. oktober 2004 - 17:38 Der er 10 kommentarer og
1 løsning

Dynamiske drop-downs i PHP (teoretisk)

Jeg er desværre kørt lidt fast, og mangler et par idéer til løsning af følgende problem, helst med mest mulig genbrugelig kode.

Resultatet:
Der skal opbygges en drop-down liste, hvor brugeren kan vælge én mulighed. Så langt så godt, jeg er kommet frem til at det nok er lettest med en funktion, der klarer opbygningen af select-koden. Det går fint nok, og giver noget i stil med dette eksempel:

[ Vælg noget her ]

[ Submit ]

Problemet:
Hver valgmulighed kan også indeholde en ny underkategori, og disse har jeg valgt at fører videre med value="1234|HasChild" i select-boksen - som så splittes op igen på "|" (pipe) når værdien tjekkes. Det virker også, men - og nu kommer det komplicerede - hver af disse nye underkategorier kan også indeholde egne underkategorier (i teorien uendeligt, men i praksis nok ikke mere end 2-3 gange). Det har jeg forsøgt at vise i dette eksempel (viser select-bokse efter 2 gange submit, hvor hver af de valgte havde en underkategori):

[ Mulighed A ]
[ Mulighed B (under A) ]
[ Mulighed C (under B) ]

[ Submit ]

Altså skal de tidligere select-bokse følge med, og vises med deres respektive valg. Formen submit'er til sig selv, og skal sådan set bare behandle hver valgmulighed og afgøre hvorvidt der skal vises nuværende + en ny select-boks (underkategori) eller om værdien blot skal gemmes (såfremt den sidst valgte select-boks ikke har en underkategorier).

Det lyder måske ikke umiddelbart så svært, men problemet i det er det dynamiske. Jeg ved ikke på forhånd om der vil være 1, 3 eller ingen underkategorier, da dette skal tilpasse sig i henhold til de forskellige valg. Derfor forsøger jeg at genbruge koden, så man ikke skal skrive en hulens masse nested if-sætninger, som alligevel aldrig vil være uendelige. Jeg har overvejet både nestede funktioner, while løkke m.m., men det er ikke lykkedes mig at få det til at virke efter hensigten. Endvidere har jeg også et problem med at navngive hver enkelt select-boks. I starten tænkte jeg på at bruge et enkelt array som navn til alle select-bokse, men jeg synes ikke rigtig jeg har så meget kontrol over hver enkelt boks på den måde. Det er muligt jeg har grebet det forkert an, og det i virkeligheden er den bedste måde alligevel...

Jeg håber nogle herinde kan komme med lidt forslag til løsning af dette problem. Det må gerne være pesudo-kode, så længe jeg bare nogenlunde kan gennemskue hvilke PHP funktioner der kan bruges, og hvad deres formål er :o)

Min server har i øvrigt PHP5, og som nogle sikkert har gættet hentes indholdet til select-boksene fra en DB. Men det er knap så vigtigt, da jeg ikke har problemer med at hente og præsentere dataene som select-bokse, problemet opstår først ved at der skal laves flere sammenhængende select-bokse, og helst uden alt for meget gentagelse i koden.
Avatar billede filename Nybegynder
30. oktober 2004 - 17:41 #1
Du kan lave en if - sætning som siger at hvis den indholder | skal den udskrive det andet og ikke den?
Avatar billede bromer Nybegynder
30. oktober 2004 - 17:42 #2
Hvordan ser din databasestruktur ud?
Avatar billede michael_r Nybegynder
30. oktober 2004 - 17:53 #3
filename: Pipe klares med explode(). Jeg kan godt lave en if-sætning til X antal select-bokse, men det bliver hurtigt rodet, og er ikke videre dynamisk - eller uendeligt som jeg gerne så.

bromer: Jeg har ikke umiddelbart mulighed for at fremvise databasestrukturen, men jeg tror heller ikke det er nær så relevant i dette spørgsmål. Det drejer sig mere om behandling af value fra hver select-boks, og disse bokse indeholder value="1234" eller value="1234|HasChild". Database delen behøver jeg ikke hjælp til.
Avatar billede bromer Nybegynder
30. oktober 2004 - 18:05 #4
Så forstår jeg ikke umiddelbart hvad dit problem er. Hvis du har en streng, der har det format du skriver, kan du jo netop have en explode som:

<?php

$list = explode("|",$yourvalue);
foreach($list as $sub) {
  // Her finder du oplysningerne fra $sub, saa du kan lave endnu en dropdown
}
Avatar billede michael_r Nybegynder
30. oktober 2004 - 19:37 #5
bromer: Ja, sådan regnede jeg også med at det kunne klares til at starte med, men i praksis kan jeg ikke opnå det ønskede resultat alene på den måde. Først og fremmest skal de tidligere valgte bokse også opbygges igen, og vises med den (tidligere) valgte værdi. Dernæst skal man tage højde for at brugeren kan gå op og ændre i en af de første select-bokse, og i de tilfælde fjerne de efterfølgende bokse; hvilket er svært at identificere hvis de blot alle ligger i et og samme array.

Måske kan jeg bare ikke se skoven for træer? Jeg synes ellers jeg har prøvet en masse, og søgt vidt og bredt efter eksempler på denne form for anordning, men uden meget held :-/
Avatar billede bromer Nybegynder
30. oktober 2004 - 20:07 #6
Det kan løses ved at du altid sender alt data med over på den næste side. Du starter med en dropdown, hvor du sender den valgte værdi over på næste side. Derefter viser du den samme dd med den valgte værdi og viser den underdd. Når du vælger en værdi i denne sender du BEGGE værdier med rundt.

Generelt når du skal outputtte en underdd skal du checke om det kan passe at den netop kører til den dd der er i niveauer over hvis du forstår. Hvis man derfor vælger en ny dd f.eks. i toppen, vil dette check fejle og derfor stopper du på det niveau.

Hjalp det?
Avatar billede michael_r Nybegynder
31. oktober 2004 - 19:19 #7
bromer: Det med at sende det hele med over sker netop også, hvis man vælger at proppe det i et array, f.eks. ved at navngive ens select-bokse til "choices[]". Så vil $_POST['choices'] indeholde et array med alle drop-down bokse... Så langt så godt :o)

Men hvordan vil du så foretage det nævnte tjek, og holde styr på hvilke select-bokse der kommer under hvad? Det er sådan set det jeg har problemer med at gennemskue, endsige få til at fungere i praksis.
Avatar billede olebole Juniormester
01. november 2004 - 06:49 #8
<ole>

Kan du evt. fylde noget ekstra i navnet:
  <select name="choices['noget her']" ...>

  <select name="choices['noget andet her']" ...>

... på en måde, så det siger noget om, hvordan de hænger sammen? Så vil du jo have dem liggende som et associativt array:
  $_POST["choices"]["noget her"] og $_POST["choices"]["noget andet her"]
Sådan et kan du jo altid bladre igennem med foreach-løkker og genkende de enkelte selects ... hvis jeg altså har forstået dig ret  :)

/mvh
</bole>
Avatar billede olebole Juniormester
01. november 2004 - 06:50 #9
PS: du kan jo fortsætte i det uendelige:
  <select name="choices['noget her']['nummer to']" ...>
... osv
Avatar billede michael_r Nybegynder
01. november 2004 - 09:08 #10
olebole: Ja, det lyder som en god idé. Jeg har eksperimenteret lidt i den retning tidligere, men dengang var det ikke helt så dynamisk.

Jeg vil tro man kunne bruge tal, noget ala choice[1] (første), choice[1][1] (anden under første), choice[1][2] o.s.v. - det må jeg lige se om jeg kan få lavet mere dynamisk. Foreløbig tak.
Avatar billede michael_r Nybegynder
16. november 2004 - 00:00 #11
Jeg havde nær glemt denne tråd. Det kom til at virke efter planen, og resultatet er vidst rimeligt skudsikkert. Såfremt andre kan få glæde af dette i fremtiden, er her et forsimplet eksempel på min kode (jeg håber det vises korrekt herinde):

<?php
// Test data - i den virkelige verden langt mere dynamisk:
$result_main_choices[] = array('id'=>'5','beskrivelse'=>'Rød','hasSubs'=>0);
$result_main_choices[] = array('id'=>'10','beskrivelse'=>'Grøn','hasSubs'=>1);
$result_main_choices[] = array('id'=>'15','beskrivelse'=>'Blå','hasSubs'=>0);

$result_sub_choice[] = array('id'=>'20','beskrivelse'=>'Lys','hasSubs'=>0);
$result_sub_choice[] = array('id'=>'25','beskrivelse'=>'Mørk','hasSubs'=>0);
$result_sub_choice[] = array('id'=>'30','beskrivelse'=>'Almen','hasSubs'=>1);

function SelectBox($isSub=false,$selection='',$choice='')
{
    global $result_main_choices, $result_sub_choice;
    static $sub_count = 0;

    $list_content = ('' != $choice)
        ? $result_sub_choice
        : $result_main_choices;

    if ($isSub)
    {
        $sub_count++;
        $selectlist = '<p><select name="list[1.'.$sub_count.']" onchange="this.form.submit()">'."\n";
        $selectlist .= '<option value="">V&aelig;lg underkategori</option>'."\n";
    }
    else
    {
        $selectlist = '<p><select name="list[1.0]" onchange="this.form.submit()">'."\n";
        $selectlist .= '<option value="">V&aelig;lg hovedkategori</option>'."\n";
    }

    /* start loop */
    foreach ($list_content as $item)
    {
        if ($item['hasSubs'])
        {
            $selectlist .= '<option value="'. $item['id'] .'|hasSubs"'. (($selection == $item['id']) ? ' selected="selected"' : '') .'>'. htmlentities($item['beskrivelse'], ENT_COMPAT) .' *</option>';
        }
        else
        {
            $selectlist .= '<option value="'. $item['id'] .'"'. (($selection == $item['id']) ? ' selected="selected"' : '') .'>'. htmlentities($item['beskrivelse'], ENT_COMPAT) .'</option>';
        }
        $selectlist .= "\n";
    }
    /* end loop */

    $selectlist .= '</select> <small style="color:#ccc">[debug] selection: '. $selection .' choice: '. $choice .'</small></p>'."\n";

    return $selectlist;
} // End function SelectBox()

?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html>
<head>
<title>SelectBox test</title>
<style type="text/css">
/* <![CDATA[ */
select {    width:160px;}
/* ]]> */
</style>
</head>
<body>
<h1>SelectBox test</h1>
<?php
echo '<fieldset style="border:0">';
echo '<form action="'. $_SERVER['PHP_SELF'] .'" name="testform" method="post">'."\n";

if (isset($_POST['list']) && is_array($_POST['list']))
{
    $lists = $_POST['list'];
    $count = count($lists);
   
    for ($i=0; $i<$count; $i++)
    {
        $submitted = explode('|', $lists["1.$i"]);
        if (0 === $i)
        {
            if ($submitted[0] == '')
            {
                // Lav dropdown med hovedkategori
                echo SelectBox();
                unset($_POST['list']);
                break;
            }
            else
            {
                // Lav dropdown med hovedkategori og marking af tidligere valg
                echo SelectBox(false,$submitted[0]);
            }
        }
        else
        {
            if ($submitted[0] == '')
            {
                // Brugeren valgte ikke en værdi
                unset($_POST['list']);
                break;
            }
            else
            {
                // Lav dropdown med underkategori afhængig af tidl. valg
                $prev = ($i-1 < 0) ? 0 : $i - 1;
                if (isset($lists["1.$prev"]))
                {
                    $prev_submitted = explode('|', $lists["1.$prev"]);
                    echo SelectBox(true,$submitted[0],$prev_submitted[0]);
                }
            }
        }

        if (isset($submitted[1]) && 'hasSubs' == $submitted[1])
        {
            // Tjek om næste dropdown allerede er sat, og opret den hvis ikke
            $next = $i + 1;
            if(!isset($lists["1.$next"]))
                echo SelectBox(true,'',$submitted[0]);
        }
        else
        {
            if (isset($_POST['button']))
            {
                echo '<p>Brugeren brugte knappen, og accepterede sit valg af '. $submitted[0].'</p>';
            }
            unset($_POST['list']);
            break;
        }
    } // End for()
}
else
{
    // Lav dropdown med hovedkategori (standard)
    echo SelectBox();
}
echo '<input name="button" type="submit" value="Gem valg" />';
echo '</form>';
echo '</fieldset>';
?>
</body>
</html>
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Vi tilbyder markedets bedste kurser inden for webudvikling

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester