Dynamisch bevöl auf eine Combobox mit Werten aus einer Karte basiert, was in einem anderen Combobox ausgewählt ist

StackOverflow https://stackoverflow.com/questions/156852

Frage

Ok, hier ist eine für die Java / JavaScript-Gurus:

In meiner app, einer der Controller übergibt ein TreeMap es JSP ist. Diese Karte hat Namen des Automobilherstellers als Schlüssel und Listen von Auto Objekten als Werte. Diese Auto-Objekte sind einfach Bohnen des Autos Namen enthält, id, Herstellungsjahr usw. So sieht die Karte so etwas wie diese (dies ist nur ein Beispiel, die Dinge ein wenig zu klären):

Key: Porsche
Wert: Liste mit drei Auto-Objekten (zum Beispiel 911, Carrera, Boxster mit ihren respektablen Produktionsjahre und ids)
Key: Fiat
Wert: Liste mit zwei Autos Objekten (zB Punto und Uno)
etc ...

Nun, in meinem JSP ich habe zwei Comboboxen. Man sollte eine Liste der Automobilhersteller (Schlüssel von der Karte - dieser Teil Ich weiß, wie zu tun ist) erhalten, und der andere sollte dynamicaly ändern die Namen der Autos angezeigt werden, wenn der Benutzer einen bestimmten Hersteller von der ersten Combobox. So zum Beispiel Benutzer wählt ein "Porsche" in der ersten Combobox und die zweiten zeigt sofort "911, Carrera, Boxster" ...

Nach ein paar Tagen damit verbringen, um herauszufinden, wie dies zu tun, ich bin bereit geschlagen zu geben. Ich habe versucht, viele verschiedene Dinge, aber jedes Mal, wenn ich eine Wand auf dem Weg getroffen somewehere. Kann jemand empfehlen, wie ich dieses nähern sollte? Ja, ich bin ein Neuling JavaScript, wenn jemand fragt mich ...

EDIT: Ich habe dies als eine Code-Herausforderung retagged. Ein großes Lob an alle, die diese löst man ohne Verwendung von JavaScript-Framework (wie JQuery).

War es hilfreich?

Lösung 2

Naja, jedenfalls wie ich schon sagte, ich es endlich geschafft, es selbst zu tun, also hier ist meine Antwort ...

Ich erhalte die Karte von meinem Controller wie folgt aus (ich bin mit Frühling, weiß nicht, wie dies mit anderen Frameworks funktioniert):

<c:set var="manufacturersAndModels" scope="page" value="${MANUFACTURERS_AND_MODELS_MAP}"/>

Das sind meine Combos:

<select id="manufacturersList" name="manufacturersList" onchange="populateModelsCombo(this.options[this.selectedIndex].index);" >
                  <c:forEach var="manufacturersItem" items="<%= manufacturers%>">
                    <option value='<c:out value="${manufacturersItem}" />'><c:out value="${manufacturersItem}" /></option>
                  </c:forEach>
                </select>


select id="modelsList" name="modelsList"
                  <c:forEach var="model" items="<%= models %>" >
                    <option value='<c:out value="${model}" />'><c:out value="${model}" /></option>
                  </c:forEach>
                </select>

Ich importiert die folgenden Klassen (einige Namen haben, natürlich geändert):

<%@ page import="org.mycompany.Car,java.util.Map,java.util.TreeMap,java.util.List,java.util.ArrayList,java.util.Set,java.util.Iterator;" %>

Und hier ist der Code, der all die harte Arbeit tut:

<script type="text/javascript">
<%  
     Map mansAndModels = new TreeMap();
     mansAndModels = (TreeMap) pageContext.getAttribute("manufacturersAndModels");
     Set manufacturers = mansAndModels.keySet(); //We'll use this one to populate the first combo
     Object[] manufacturersArray = manufacturers.toArray();

     List cars;
     List models = new ArrayList(); //We'll populate the second combo the first time the page is displayed with this list


 //initial second combo population
     cars = (List) mansAndModels.get(manufacturersArray[0]);

     for(Iterator iter = cars.iterator(); iter.hasNext();) {

       Car car = (Car) iter.next();
       models.add(car.getModel());
     }
%>


function populateModelsCombo(key) {
  var modelsArray = new Array();

  //Here goes the tricky part, we populate a two-dimensional javascript array with values from the map
<%                          
     for(int i = 0; i < manufacturersArray.length; i++) {

       cars = (List) mansAndModels.get(manufacturersArray[i]);
       Iterator carsIterator = cars.iterator();           
%>
    singleManufacturerModelsArray = new Array();
<%
    for(int j = 0; carsIterator.hasNext(); j++) {

      Car car = (Car) carsIterator.next();

 %>         
    singleManufacturerModelsArray[<%= j%>] = "<%= car.getModel()%>";
 <%
       }
 %>
  modelsArray[<%= i%>] = singleManufacturerModelsArray;
 <%
     }         
 %>   

  var modelsList = document.getElementById("modelsList");

  //Empty the second combo
  while(modelsList.hasChildNodes()) {
    modelsList.removeChild(modelsList.childNodes[0]);
  }

 //Populate the second combo with new values
  for (i = 0; i < modelsArray[key].length; i++) {

    modelsList.options[i] = new Option(modelsArray[key][i], modelsArray[key][i]);
  }      
}

Andere Tipps

Ich liebe es einfach eine Herausforderung dar.

No jQuery, einfach nur Javascript, getestet auf Safari.

Ich mag die folgenden Ausführungen im Voraus hinzuzufügen:

  • Es ist faily lange aufgrund des Fehlers Überprüfung.
  • Zwei Teile erzeugt werden; der erste Skript-Knoten mit der Karte und der Inhalt des manufacterer SELECT
  • Funktioniert auf My Machine (TM) (Safari / OS X)
  • Es gibt keine (css) Styling angewendet. Ich habe schlechten Geschmack so es ist keine Verwendung sowieso.

.

<body>
  <script>
  // DYNAMIC
  // Generate in JSP
  // You can put the script tag in the body
  var modelsPerManufacturer = {
    'porsche' : [ 'boxter', '911', 'carrera' ],
    'fiat': [ 'punto', 'uno' ]  
  };
  </script>

  <script>
  // STATIC
  function setSelectOptionsForModels(modelArray) {
    var selectBox = document.myForm.models;

    for (i = selectBox.length - 1; i>= 0; i--) {
    // Bottom-up for less flicker
    selectBox.remove(i);  
    }

    for (i = 0; i< modelArray.length; i++) {
     var text = modelArray[i];
      var opt = new Option(text,text, false, false);
      selectBox.add(opt);
    }  
  }

  function setModels() {
    var index = document.myForm.manufacturer.selectedIndex;
    if (index == -1) {
    return;
    }

    var manufacturerOption = document.myForm.manufacturer.options[index];
    if (!manufacturerOption) {
      // Strange, the form does not have an option with given index.
      return;
    }
    manufacturer = manufacturerOption.value;

    var modelsForManufacturer = modelsPerManufacturer[manufacturer];
    if (!modelsForManufacturer) {
      // This modelsForManufacturer is not in the modelsPerManufacturer map
      return; // or alert
    }   
    setSelectOptionsForModels(modelsForManufacturer);
  }

  function modelSelected() {
    var index = document.myForm.models.selectedIndex;
    if (index == -1) {
      return;
    }
    alert("You selected " + document.myForm.models.options[index].value);
  }
  </script>
  <form name="myForm">
    <select onchange="setModels()" id="manufacturer" size="5">
      <!-- Options generated by the JSP -->
      <!-- value is index of the modelsPerManufacturer map -->
      <option value="porsche">Porsche</option>
      <option value="fiat">Fiat</option>
    </select>

    <select onChange="modelSelected()" id="models" size="5">
      <!-- Filled dynamically by setModels -->
    </select>
  </form>

</body>

Sind Sie Struts?

Sie werden einige JavaScript Tricks benötigen (oder AJAX), dies zu erreichen.

Was werden Sie tun müssen, ist, irgendwo in Ihrem JavaScript-Code (abgesehen, wie Sie es für die Minute erzeugen):

var map = {
   'porsche': [ 'boxter', '911', 'carrera' ],
   'fiat': ['punto', 'uno']
};

Dies ist im Grunde eine Kopie der serverseitigen Datenstruktur, das heißt eine Karte durch Hersteller verkeilte, wobei jeder Wert eine Reihe von Fahrzeugtypen hat.

Dann in Ihrem onchange Ereignisse für die Hersteller, dann würden Sie das Array aus der Karte oben definiert erhalten müssen, und dann eine Liste der Optionen aus, dass erstellen. (Check out devguru.com - es hat viele hilfreiche Informationen zu den Standard-JavaScript-Objekte).

Je nachdem, wie groß Ihre Liste der Autos ist, aber es könnte am besten sein, den AJAX-Weg zu gehen.

Sie müssen einen neuen Controller erstellen, die die Liste der Autos Typs Hersteller gegeben nachgeschlagen, und dann nach vorne auf eine JSP, die zurückgegeben JSON (es muss nicht JSON sein, aber es funktioniert ganz gut für mich).

Verwenden Sie dann eine Bibliothek wie jQuery die Liste der Autos in Ihrem onchange Ereignisse für die Liste der zum Abrufen Hersteller. (JQuery ist ein ausgezeichneter JavaScript-Framework zu kennen -. Es macht die Entwicklung mit JavaScript viel einfacher Die Dokumentation ist sehr gut).

Ich hoffe, einige von dem macht Sinn?

Hier ist ein funktionierendes, Cut-and-Paste-Antwort in jsp ohne Tag-Bibliotheken oder externe Abhängigkeiten zu löschen. Die Karte mit den Modellen ist hartcodiert, sollte aber keine Probleme darstellen.

Ich trennte diese Antwort aus meiner früheren Antwort als die zugegebenen JSP Lesbarkeit nicht verbessert. Und in ‚wirklichen Leben‘ würde ich nicht belasten meine JSP mit allen eingebetteten Logik, sondern setzen sie irgendwo in einer Klasse. Oder Tags verwenden.

Alles, was „erstes“ Zeug ist superfluos „“ in dem generierten Code zu verhindern. ein foreach dosn't geben Sie Kenntnisse über die Anzahl der Elemente, so überprüfen Sie zum Schluss. Sie können auch die erste Element Handhabung überspringen und die letzten Streifen „“ danach durch den Generator Länge um 1 verringert wird.

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<%@page import="java.util.Map"%>
<%@page import="java.util.TreeMap"%>
<%@page import="java.util.Arrays"%>
<%@page import="java.util.Collection"%>
<%@page import="java.util.List"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Challenge</title>
</head>
<body onload="setModels()">
<% // You would get your map some other way.
    Map<String,List<String>> map = new TreeMap<String,List<String>>();
    map.put("porsche", Arrays.asList(new String[]{"911", "Carrera"}));
    map.put("mercedes", Arrays.asList(new String[]{"foo", "bar"}));
%>

<%! // You may wish to put this in a class
  public String modelsToJavascriptList(Collection<String> items) {
    StringBuilder builder = new StringBuilder();
    builder.append('[');
    boolean first = true;
    for (String item : items) {
        if (!first) {
          builder.append(',');
        } else {
          first = false;
        }
        builder.append('\'').append(item).append('\'');
    }
    builder.append(']');
    return builder.toString();
  }

  public String mfMapToString(Map<String,List<String>> mfmap) {
    StringBuilder builder = new StringBuilder();
    builder.append('{');
    boolean first = true;
    for (String mf : mfmap.keySet()) {
      if (!first) {
          builder.append(',');
      } else {
          first = false;
      }
      builder.append('\'').append(mf).append('\'');
      builder.append(" : ");
      builder.append( modelsToJavascriptList(mfmap.get(mf)) );
    }
    builder.append("};");
    return builder.toString();
  }
%>

<script>
var modelsPerManufacturer =<%= mfMapToString(map) %>
  function setSelectOptionsForModels(modelArray) {
    var selectBox = document.myForm.models;

    for (i = selectBox.length - 1; i>= 0; i--) {
    // Bottom-up for less flicker
    selectBox.remove(i);
    }

    for (i = 0; i< modelArray.length; i++) {
     var text = modelArray[i];
      var opt = new Option(text,text, false, false);
      selectBox.add(opt);
    }
  }

  function setModels() {
    var index = document.myForm.manufacturer.selectedIndex;
    if (index == -1) {
    return;
    }

    var manufacturerOption = document.myForm.manufacturer.options[index];
    if (!manufacturerOption) {
      // Strange, the form does not have an option with given index.
      return;
    }
    manufacturer = manufacturerOption.value;

    var modelsForManufacturer = modelsPerManufacturer[manufacturer];
    if (!modelsForManufacturer) {
      // This modelsForManufacturer is not in the modelsPerManufacturer map
      return; // or alert
    }
    setSelectOptionsForModels(modelsForManufacturer);
  }

  function modelSelected() {
    var index = document.myForm.models.selectedIndex;
    if (index == -1) {
      return;
    }
    alert("You selected " + document.myForm.models.options[index].value);
  }
  </script>
  <form name="myForm">
    <select onchange="setModels()" id="manufacturer" size="5">
      <% boolean first = true;
         for (String mf : map.keySet()) { %>
          <option value="<%= mf %>" <%= first ? "SELECTED" : "" %>><%= mf %></option>
      <%   first = false;
         } %>
    </select>

    <select onChange="modelSelected()" id="models" size="5">
      <!-- Filled dynamically by setModels -->
    </select>
  </form>

</body>
</html>

Wie wäre es so etwas wie dieses, mit Prototyp? Zuerst Ihre Auswahlbox von Kategorien:

<SELECT onchange="changeCategory(this.options[this.selectedIndex].value); return false;">
   <OPTION value="#categoryID#">#category#</OPTION>
   ...

Dann Sie Ausgang N verschiedene Auswahlboxen, eine für jede der Unterkategorien:

<SELECT name="myFormVar" class="categorySelect">
...                                        

Ihre change JavaScript-Funktion alle wählt mit Klasse category deaktiviert, und dann können nur die eine für die aktuelle categoryID.

// Hide all category select boxes except the new one
function changeCategory(categoryID) {

   $$("select.categorySelect").each(function (select) {
      select.hide();
      select.disable();
   });

   $(categoryID).show();
   $(categoryID).enable();
}

Wenn Sie wie dies in Prototyp verstecken / deaktivieren, ist es nicht nur versteckt es auf der Seite, aber es wird jene Form variabel halten zu veröffentlichen. Also auch wenn man N wählt mit dem gleichen Form Variablennamen (myFormVar), nur die aktiven Beiträge.

Nicht, dass vor langer Zeit dachte ich über etwas ähnliches.

Mit jQuery und die TexoTela Add-on nicht alles war so schwierig.

Zuerst müssen Sie eine Datenstruktur wie die Karte oben erwähnt:

var map = {
   'porsche': [ 'boxter', '911', 'carrera' ],
   'fiat': ['punto', 'uno']
}; 

Ihre HTML sollten vergleichbar aussehen:

<select size="4" id="manufacturers">
</select>
<select size="4" id="models">
</select>

Dann füllen Sie die erste Combo mit jQuery-Code wie:

$(document).ready(
  function() {
    $("#bronsysteem").change( manufacturerSelected() );
  } );
);

wo manufacturerSelected ist der Rückruf auf dem onChange Ereignisse registriert

function manufacturerSelected() {
  newSelection = $("#manufacturers").selectedValues();
  if (newSelection.length != 1) {
    alert("Expected a selection!");
    return; 
  }
  newSelection = newSelection[0];
  fillModels(newSelection);     
}

function fillModels(manufacterer) {
    var models = map[manufacturer];

    $("models").removeOption(/./); // Empty combo

    for(modelId in models) {
       model = models[modelId];
       $("models").addOption(model,model); // Value, Text
    }
}

Dies sollte den Trick tun.

Bitte beachten Sie, dass in Syntaxfehler sein kann; Ich habe meinen Code bearbeitet Ihren Anwendungsfall zu reflektieren und hatte abzustreifen ziemlich viel aus.

Wenn das hilft Ich habe einen Kommentar schätzen würde.

Als Add-on auf meiner vorherigen Post; Sie können ein Skript-Tag in Ihrem JSP setzen, wo Sie über Ihre Karte iterieren. Ein Beispiel über die Karten iterieren kann in

scroll top