Populate Cascading Dropdown using json with functional Search - javascript

This has been one of the most frustrating online searches-for-an-answer-or-solution EVER! And I still haven't found anything out there that can perform this basic task - with that being said, this IS a search facility that's used very often all over the net ... so it comes as a surprise as to why there aren't many (any) demos / scripts for sale that can do this.
I want to have a search facility on my website whereby the user can select a country > province > town (3 drop downs).
Obviously if the user selects the USA (for example), the next dropdown populates / updates the provinces (states) relevant to USA and so forth with the next drop down.
I see a lot of people using ASP.net and AngularJS to perform this funtion but I am using neither languages and don't want to use them.
This guy here has developed a great solution for people who'd like their results to dynamically load up as they select items in the dropdowns - however this isn't what I want.
The Javascript and Json approach is where I like to go.
Now this guy here made a great / simple solution for populating dropdowns (I am going to post the code for this script later on).
But with ALL of these demos and scripts online, they are ALL missing one thing - the SEARCH facility. It's great populating a dropdown to select correct items but that's half the job done and that's all it does.
I want the user to be able to click a SEARCH button AFTER the last item in a dropdown is selected and go to it's respective page (because isn't that what is supposed to be for? - Of course that also depends on the project at hand).
So lets take the code of the populated dropdown created by the guy in the second link:
HTML:
<head>
<meta charset="utf-8">
<title></title>
<meta name="description" content="">
<script src="js/jquery-1.8.1.min.js"></script>
<script src="js/outils.js"></script>
</head>
<body>
<div>
<select id="Marque">
<option value="0">Choix du marque</option>
<option value="bmw">bmw</option>
<option value="mercedes">mercedes</option>
<option value="audi">audi</option>
<option value="volswagen">volswagen</option>
</select>
<select id="Serie"></select>
</div>
</body>
Javascript:
jQuery().ready(function(){
var tabMarque=[];
$.getJSON('file.json', function(data) {
$.each(data, function(index, val) {
tabMarque[index]=val;
});
});
$('#Marque').change(function(event) {
$marque=$(this).val();
$htmlOption='<option value="0">Choix du serie</option>';
if($marque!=0)
{
$.each(tabMarque[$marque], function(key, value) {
$htmlOption+='<option
value="'+value[0]+'">'+value[1]+'</option>';
});
}
$('#Serie').html($htmlOption);
});
});
JSON:
{
"bmw":[
["1","serie 1"],
["2","serie 3"],
["3","serie 5"],
["4","serie 7"]
],
"mercedes":[
["6","class A"],
["7","class B"],
["8","class C"],
["9","class E"]
],
"audi":[
["10","a3"],
["11","a4"],
["12","a5"],
["13","a6"]
],
"volswagen":[
["14","polo"],
["15","golf"],
["16","cady"]
]
}
(Sorry, I'd like to put this on JSfiddle but there's json involved and I don't know where to put the json code).
So after the 3 dropdown boxes, I'd like to have a button saying "Go" or "Search" and once it's clicked it takes the user to the page of the last selected item.
EG (using the example code above - understand there's only 2 dropdowns in his code):
I select:
BMW
1 Series
... and then when I click "GO" - it take me to bmw-1-series.htm
How can this be done?
Surely one could add urls to the items in the json file eg:
"bmw":[
["1","serie 1","http://www.example.com/bmw-1-series.htm"],
["2","serie 3","http://www.example.com/bmw-2-series.htm"],
["3","serie 5","http://www.example.com/bmw-3-series.htm"],
["4","serie 7","http://www.example.com/bmw-4-series.htm"]
],
and then when you click "GO", it will take you to the respective url. Obviously this needs extra code I can imagine to be able to grab the url of the selected item and take the user to that page (something I wouldn't know how to do)?
Is there a better way to do this?
UPDATE TO MAKE THIS CLEARER:
Go to CSS TRICKS DEMO
This is what I want (to be able to populate the dropdowns - this is the only thing the demo does) however if the user wants to search for Coffee's ... they would click on BEVERAGES > then choose COFFEE and then I'd like them to be able to click a button (just below the 2 dropdowns) saying SEARCH ... which will take them to a page with all the coffees listed on

As you requested, I looked at the code and have corrected it. It can be found at:
http://plnkr.co/edit/RnCdfnrX0jy3RvmXjF56
Link-only answers are frowned upon in StackOverflow, so I will write a little more:
As I wrote in my original comment:
If you are generally following along with the video you posted, you
should be able to use that as a foundation. You can do one of two
things with your links, either maintain your own datastructure (even a
basic Object) to map between the value of the leaf node select items
and then navigation destination, or add them directly as a data
attribute when adding them to the menu. Then your button code can
lookup the URL in whichever place you put it. In his line 15 (in the
'marque' handler) you could have:
$htmlOption+='<option value="'+val[0]+'" data-theurl="'+val[2]+'"...'
if URL is in spot 2
Although C Fasolin took the code you pointed to and my advice and attempted to convert it to an answer, you are correct that the code provided in the answer has errors in it.
Since SO requires code for Plunkr links, I now also have to paste some code in here as well. This is the corrected JS code minus the silly number of alerts injected into the code (which I only bothered to comment out in the Plunkr, but removed here to be tidier). The HTML and JSON were fine and are also on the Plunkr. Note that the JSON data only contains URLs for the BMW examples so you will get a 404 when clicking "go" for other makes, ou marques, en français.
$(document).ready(function() {
var tabMarque = [];
$.getJSON('data.json', function(data) {
$.each(data, function(index, val) {
tabMarque[index] = val;
});
});
$('#Marque').change(function(event) {
var marque = $(this).val();
var htmlOption = '<option value="0">Choix du serie</option>';
if (marque !== '0') {
var itemsMarque = tabMarque[marque];
$.each(itemsMarque, function(key, value) {
htmlOption += '<option value="' + value[0] + '" data-href="' + value[2] + '">' + value[1] + '</option>';
});
}
$('#Serie').html(htmlOption);
});
$('#go').click(function() {
var selected = $('#Serie').find('option:selected');
var href = selected.data('href');
window.location = href;
});
});
Happy menuing!

this solution work for me:
JAVASCRIPT:
$(document).ready(function(){
var tabMarque=[];
$.getJSON('file.data.txt', function (data) {
alert(data);
$.each(data, function(index, val) {
tabMarque[index]=val;
});
});
$('#Marque').change(function(event) {
alert('Marque_change');
var marque=$(this).val();
alert(marque);
var htmlOption='<option value="0">Choix du serie</option>';
if(marque!="0")
{
var itemsMarque = tabMarque[marque];
alert(JSON.stringify(itemsMarque));
$.each(itemsMarque, function (key, value) {
//alert("k=" + key + " v=" + JSON.stringify(value));
htmlOption+='<option value="'+value[0]+'" data-href="'+value[2]+'">'+value[1]+'</option>';
});
}
$('#Serie').html(htmlOption);
});
$('#go').click(function () {
alert('go_click');
var selected = $('#Serie').find('option:selected');
var href = selected.data('href');
alert('goto:'+href);
});
});
HTML
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8">
<title></title>
<meta name="description" content="">
<script src="js/jquery-1.8.1.min.js"></script>
<script src="js/outils.js"></script>
</head>
<body>
<div>
<select id="Marque">
<option value="0">Choix du marque</option>
<option value="bmw">bmw</option>
<option value="mercedes">mercedes</option>
<option value="audi">audi</option>
<option value="volswagen">volswagen</option>
</select>
<select id="Serie"></select>
<input type="button" id="go" value="Go!" />
</div>
</body>
</html>
JSON:
{
"bmw":[
["1","serie 1","http://www.example.com/bmw-1-series.htm"],
["2","serie 3","http://www.example.com/bmw-2-series.htm"],
["3","serie 5","http://www.example.com/bmw-3-series.htm"],
["4","serie 7","http://www.example.com/bmw-4-series.htm"]
],
"mercedes":[
["6","class A"],
["7","class B"],
["8","class C"],
["9","class E"]
],
"audi":[
["10","a3"],
["11","a4"],
["12","a5"],
["13","a6"]
],
"volswagen":[
["14","polo"],
["15","golf"],
["16","cady"]
]
}

Related

Web2py Drop down menu that filters

New to html/javascript/web2py, but I been reading a lot and saw many different ways into sorting and some ways to filtering. However, I haven't found something that is similar to mine.
Right now, I'm creating a website that is similar to Craigslist where you can post items and I'm attempting to make a drop down menu that can filter. For an example, if I click car, it will only show posts that has the keyword Car in the category.
Right now, you can create a post and (IS_IN_SET) will already have the categories there for you. However, this is where I'm getting lost. I'm not sure how to get the keywords from (IS_IN_SET) so I am able to use those words to filter.
This is in db.py
db.define_table('posts',
Field('title', requires=IS_NOT_EMPTY()),
Field('interests'),
Field('category', requires=IS_IN_SET(['Computer', 'Electronics', 'Cars', 'Video Games'])),
In my default/index, I created
<select>
<option value='0'>(Select Category)</option><!--added-->
<option value="Computer">Computer</option>
<option value="Electronics">Electronics</option>
<option value="Cars">Cars</option>
<option value="Video Games">Video Games</option>
</select>
But I don't know where to go from here.
I read that you can use IS_IN_DB to create a drop down filter list.
I tried using, but I'm pretty sure this is wrong....
db.category.requires=IS_IN_DB(db,'category.id','%(Tcategory)s')
Looking for any advice/tips in trying to solve this problem.
Thank you.
Here is a solution that shows the power and simplicity of Web2py. I also added a form to enter new posts. Tested.
IN MODEL (models/posts.py):
post_categories = ['Computer', 'Electronics', 'Cars', 'Video Games']
db.define_table('posts',
Field('title', requires=IS_NOT_EMPTY()),
Field('interests'),
Field('category', requires=IS_IN_SET(post_categories)),
Field('body', "text")
)
IN VIEW (views/default/posts.html):
<script src="//code.jquery.com/jquery-1.12.0.min.js"></script>
{{include 'web2py_ajax.html'}}
<div>
<select id="post-select">
<option value='0'>(Select Category)</option>
{{for item in post_categories:}}
<option value="{{=item}}">{{=item}}</option>
{{pass}}
</select>
</div>
{{=form}}
{{=LOAD('default','posts_load', vars=dict(category=item_selected), target='posts-ajax', ajax=True)}}
<script>
$(document).ready(function() {
// pre-sets the option
$('#post-select option[value="{{=item_selected}}"]').attr('selected', 'selected');
$('#post-select').change(function(event) {
var itemSelected = $(this).val();
var urlOfLoadedComponent = "{{=URL(c='default', f='posts_load')}}";
/* either one of these works but the first is better because it uses ajax */
// refreshes the component with the new subset of records
web2py_component( urlOfLoadedComponent + '?category=' + itemSelected, target='posts-ajax');
//window.location.replace('http:/localhost/posts?category=' + itemSelected);
});
});
</script>
IN VIEW (views/default/posts_load.html):
{{for post in post_rows:}}
<div class="post">
Title: <div class="post_title">{{=post.title}}</div>
Post: <blockquote class="post_body">{{=post.body}}</blockquote>
</div>
{{pass}}
IN CONTROLLER (controllers/default.py):
"""
Usage:
http:/localhost/default/posts
-- or --
http:/localhost/default/posts?category=Computer
"""
def posts():
# if category is specified in the url vars, use it otherwise use 'Computer'
item_selected = request.vars.get('category', 'Computer')
# or you could use the first one in the list:
#item_selected = request.vars.get('category', post_categories[0])
"""
creates the form
processes new posts; posts() function called again on form submit via ajax
the response.js refreshes form after ajax post
"""
form=SQLFORM(db.posts).process()
response.js = "jQuery('#posts-ajax').get(0).reload();"
"""
you don't need to pass "post_categories = post_categories" in the dict
because the view can see the variables defined in the models it runs
"""
return dict(
form = form,
item_selected = item_selected
)
def posts_load():
category = request.vars.get('category', '')
if category:
post_rows = db(db.posts.category==category).select()
else:
post_rows = db(db.posts).select()
return post_rows

Best way to bind data to an onchange of a select element using rivets

I have a page with an array with some books titles and prices, and I populated a select with the books titles. I am using rivets to select items and show the selected items below.
My dropdown select list is working. But it does not show the selected item below the dropdown list OR said in other words I want to bind the onchange event of the select to the current item on the array.
Is that possible? If so how?
I searched the internet but there is almost no resources for learning rivets. This is my code:
<!DOCTYPE HTML>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<title>Rivets test</title>
<script src="../js/rivets.js"></script>
</head>
<body>
<h3>Livros no saraiva.com.br</h3>
<select id="select">
<option rv-each-book="books">{book.title}</option>
</select>
<br><br>
<div id="contents">
<strong>Título:</strong> <span>{book.title}</span><br>
<strong>Valor:</strong> <span>{book.price}</span>
</div>
<script>
var s = document.getElementById('select'),
books = [
{
"title": "Como treinar o seu dragão",
"price": 29.90
},
{
"title": "Livro de colorir mangá",
"price": 17.40
},
{
"title": "Heróis para colorir",
"price": 27.90
},
{
"title": "O pequeno príncipe",
"price": 17.70
},
{
"title": "Guerra civil",
"price": 21.70
}
];
rivets.bind(s, {"books": books});
</script>
</body>
</html>
What would be the best way to bind the onchange to the view using rivets?
you can add rv-value to the select tag and to the options tag , because the literals only displays the value from the model property ,as below :
<select rv-value="data.selectedBook">
<option rv-each-book="books" rv-value="book.title">{ book.title }</option>
</select>
//and display the binded value as following:
<strong>Título:</strong> <span>{data.selectedBook}</span>
I hope it helps!
you have to use some of the Rivets events handlers, for example:
<button rv-on-click="clickHandler">Remove</button>
Then you need to have a method in your model called clickHandler, like this:
var model = {
items: [1,2,3,4,5],
clickHandler: function(e){
//handler code
}
};
Here's a blog post using different Rivets features (in spanish but you will understand the code samples).
http://www.leomicheloni.com/post/2014/04/26/Automatic-html-binding-con-Rivetsjs.aspx
Hope it helps.

use jquery variable in # block razor

I'm strugling with a jquery script inside a cshtml page. For short my question is how to use a var inside a # statement in a cshtml page?
below an example of what I'm trying:
<select id="DefaultText">
<option value="-1">-- select --</option>
#foreach( var d in Model.DefaultTexts )
{
<option value="#d.Id" >#d.Name</option>
}
</select>
<script type="text/javascript">
$('#DefaultText').change(function () {
var id = parseInt($('#DefaultText :selected').val());
var text = #Model.DefaultTexts.First( t => t.Id == id );
$('#CustomProductText').val(text);
});
</script>
I can't reach the var id. It's out of scope. I've also tryed it with a for loop and a if statement. But in the if statement I get the same error: out of scope.
The full story is this:
On my page I've a dropdown list. The items to select are short names for default text parts. Based on the id or name, I want to show the default text part in a textbox.
#CustomProductText is my textbox where the content should be placed (code not posted).
I've also tryed it with #: and statement but that did not work.
What am I doing wrong or maybe its not even possible what I'm trying to do.
As an alternative I've added a action to my controller to get the text form there. Below the code:
<script type="text/javascript">
$('#DefaultText').change(function () {
var id = parseInt($('#DefaultText :selected').val());
$.post("Categories/GetDefaultText", { Id: id }, function (data) {
alert(data);
});
//$('#CustomProductText').val(text);
});
</script>
controller code:
[HttpPost]
public ActionResult GetDefaultText(int id)
{
using( var context = new MyContext() )
{
var text = context.DefaultText.First( d => d.Id == id ).Text;
return this.Content( text );
}
}
This doesn't work. The action doesn't get hit in debug mode.
regards,
Daniel
The $.post that is not working for you, you should prefix the url with / sign and it will be hit as expected:
$.post("/Categories/GetDefaultText", { Id: id }, function (data) {
alert(data);
});
As for the razor solution, you can't use javascript variables in the razor code as it's not a scripting language. What razor does is simply rendering the strings (be it html or javascript or anything) into the page.
To do what you want you either need to request the server to pass the text to your page or render all the texts you have in the page and then access this rendered content in your javascript.

AngularJS --> Select requires to clicks to visually change

I'm using the following HTML and am having issues when a user selects from the dropdown box. The issue is that when you click the first time, you can see the variable change through a console log, but visually the item remains blank. If I select the item again, it will then visually show.
Here's my HTML>
<div class="section">
<select ng-model="companyInfo" ng-change='getCompany(companyInfo)'
ng-options="company.name for company in list">
</select><br>
</div>
I have a feeling its a real rookie mistake, but I can't figure it out!
Any help would be greatly appreciated.
EDIT --
Thanks for the quick replies! Should have given getCompaniesList
$scope.getCompany = function(company) {
console.log("companyInfo passed", company);
internalClientService.getCompany(company.name)
.then(function(companyRecord){
console.log("returning record from getCompany service", companyRecord);
$scope.currentCompany = companyRecord[0].name;
$scope.currentCompanyId = companyRecord[0].companyid;
getFarmList(companyRecord[0].farms);
});
};
var getFarmList = function(farmIdArray){
$scope.farmsList = [];
internalClientService.getFarms(farmIdArray)
.then(function(farmNames){
console.log('the farmNames returning', farmNames);
for (var i = 0; i<farmNames.length; i++){
$scope.farmsList.push(farmNames[i].name);
}
});
};
The internalCLientService is a simple HTTP request.
What do you think?
Thanks,
B

Mixing JavaScript and Scala in a Play template

I'm not sure how this is done. I could hard code the route I'm trying to use, but I'd like to do this the right way.
I have a dropdown that needs to load a new page on change. Here's basically how I'm trying to do it (I've tried a few variations of this):
#getRoute(value: String) = #{
routes.Accounts.transactions(Long.valueOf(value))
}
<script type="text/javascript">
$(function() {
$("select[name='product']").change(function() {
location.href = #getRoute($(this).val());
}).focus();
$('a.view.summary').attr('href', "#routes.Accounts.index()" + "?selectedAccountKey=" + $('select[name=product]').val());
});
</script>
This produces a identifier expected but 'val' found exception. I also tried surrounding it in quotes, but that causes a [NumberFormatException: For input string: "$(this).val()"]
So how the heck do I insert a value from JavaScript into a Scala function?
Edit
Here's my solution, inspired by the accepted answer. This dropdown is defined in a tag that's made for reuse by different components, and the base URL is different for each component. The way to achieve this was to pass a function that generates a URL based on an account key into the dropdown:
#(accountList: List[models.MemberAccount],
selectedAccountKey: Long,
urlGenerator: (Long) => Html
)
<select name="product">
#for(account <- accountList) {
#if(account.accountKey == selectedAccountKey) {
<option selected="selected" value="#urlGenerator(account.accountKey)">#account.description (#account.startDate)</option>
} else {
<option value="#urlGenerator(account.accountKey)">#account.description (#account.startDate)</option>
}
}
</select>
<script type="text/javascript">
$(function() {
$('select[name=product]').change(function() {
location.href = $(this).val();
});
});
</script>
Then you can define a function like this to pass in:
#transactionsUrl(memberAccountKey: Long) = {
#routes.Accounts.transactions(memberAccountKey)
}
#accountsDropdown(transactionDetails.getMemberAccounts(), transactionDetails.getMemberAccountKey(), transactionsUrl)
You need a way of storing all URLs in the page, e.g.
<option value="#routes.Accounts.transactions(id)">Display</option>
Then onChange, you can:
$("select[name='product']").change(function() {
location.href = $(this).val();
});

Categories