Web2py Drop down menu that filters - javascript

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

Related

Django Rest Framework - get related FK object for use in template; POST not working now?

I've got a form up and working with a Vue frontend and DRF backend. It's a form for adding (creating) a new model - and has a dropdown of the related models that are FK to the model being created.
I need to access attributes of the selected FK item.
My serializers look like this:
class SubdomainSerializer(serializers.ModelSerializer):
class Meta:
model = Subdomain
fields = [
"id",
"domain",
"short_description",
"long_description",
"character_code",
]
# def get_absolute_url(self, obj):
# return obj.get_absolute_url()
class EvidenceSerializer(serializers.ModelSerializer):
created_by = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
updated_by = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
absolute_url = serializers.SerializerMethodField()
created_by_name = serializers.SerializerMethodField()
updated_by_name = serializers.SerializerMethodField()
class Meta:
model = Evidence
fields = "__all__"
The form is to create a new 'Evidence' item, and the 'Subdomain' is a dropdown on the form that contains all related subdomains.
The models look like this:
class Subdomain(CreateUpdateMixin):
domain = models.ForeignKey(Domain, on_delete=models.PROTECT)
short_description = models.CharField(max_length=100)
long_description = models.CharField(max_length=250)
character_code = models.CharField(max_length=5)
class Evidence(CreateUpdateMixin, CreateUpdateUserMixin, SoftDeletionModel):
subdomain = models.ForeignKey(Subdomain, on_delete=models.PROTECT)
evaluation = models.ForeignKey(
Evaluation, related_name="evidences", on_delete=models.PROTECT
)
published = models.BooleanField(default=False)
comments = models.CharField(max_length=500)
In my form, I just want to include the short_description of each subdomain when the user chooses it from the dropdown - I may also want to use the long_description as well.
Here is the bit in the form where I render the dropdown:
<div class="form-group col-sm-4">
<label class="" for="subdomain">Subdomain</label>
<select name="subdomain" id="subdomain" class="form-control" v-model="element.subdomain">
<option v-for="choice in subdomains" :value="choice.id" >{{ choice.character_code }}</option>
</select>
</div>
<div class="small" v-if="element.subdomain">
<!-- THIS IS WHERE I WOULD LIKE TO DISPLAY THE SHORT DESCRIPTION FOR THE CHOICE IN THE DROPDOWN -->
{{ choice.short_description }}
</div>
The Form Data looks like this when I POST:
evaluation: 2037
subdomain: 448
comments: Test comments to add to the subdomain
published: true
csrfmiddlewaretoken: 382796ryfuasiodfgyhakljyht37yaisdfaslk3r
Things I have tried - some of which worked for display purposes but seem to have broken the form/POST:
Adding depth=1 to the Meta of the EvidenceSerializer, which worked but made the form no longer submit appropriately. I think it's because it wanted the entire subdomain instead of just the ID? I couldn't get it working - the subdomain always threw an error.
Adding the following to my EvidenceSerializer, which again seemed to break the POST operation, it would cause the subdomain dropdown to throw an error.
subdomain = SubdomainSerializer(read_only=True)
Using both of those methods above the dropdown doesn't recognize the subdomain_id being selected and both end up throwing this error behind the scenes:
Cannot insert the value NULL into column 'subdomain_id', table 'local_app.dbo.myapp_evidence'; column does not allow nulls. INSERT fails.
Any advice on how to proceed would be fantastic.
TLDR; Need to be able to access attributes on a FK relationship for a dropdown using DRF, and be able to submit that item in a form.
Thanks to #bdbd for pointing me in the right direction.
For anyone curious, I resolved it using those links - turns out I needed to change my serializers a little bit:
class SubdomainSerializer(serializers.ModelSerializer):
class Meta:
model = Subdomain
fields = [
"id",
"domain",
"short_description",
"long_description",
"character_code",
]
class EvidenceSerializer(serializers.ModelSerializer):
created_by = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
updated_by = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
absolute_url = serializers.SerializerMethodField()
created_by_name = serializers.SerializerMethodField()
updated_by_name = serializers.SerializerMethodField()
# add the 'subdomain' as read only - but with all the attributes
subdomain = SubdomainSerializer(read_only=True)
# add the 'subdomain_id' as a PrimaryKeyRelatedField with the source being the subdomain
subdomain_id = serializers.PrimaryKeyRelatedField(
queryset=Subdomain.objects.all(), source="subdomain"
)
class Meta:
model = Evidence
fields = "__all__"
Then I updated the HTML a little bit:
<div class="form-group col-sm-4">
<label class="" for="subdomain_id">Subdomain</label>
<select name="subdomain_id" id="subdomain" class="form-control" v-model="element.subdomain">
<option v-for="choice in subdomains" :value="choice" >{{ choice.character_code }}</option>
</select>
</div>
<div class="small" v-if="element.subdomain_id">
{{ element.subdomain.short_description }}
</div>
Then in the ajax call I simply assign the subdomain_id to the subdomain.id
data: {
evaluation : evaluationId,
subdomain_id : vm.element.subdomain.id,
comments : vm.element.comments,
published: vm.element.published,
csrfmiddlewaretoken: vm.sharedStore.state.csrftoken,
},

Is there a way in jquery or javascript to populate dropdown lists on page load without using an ajax request?

I would like to populate a few dropdown lists using jQuery when the page loads, rather than using ajax responding to an event, which I have currently and is working.
My Controller builds up several List objects for these dropdowns.
Currently I use ajax and my actions return the data, but this needs to be in my view when it renders.
C# code:
return Json(ddlVals1, JsonRequestBehavior.AllowGet)
return Json(ddlValAnother, JsonRequestBehavior.AllowGet)
Example of what I wish to do in my jQuery Code;
$(function ()
{
LoadUserDropDown();
LoadAnotherDropDown();
});
Thank You.
Is possible as long as you pass the list values to view. For example, you can use ViewBag to transfer temporary data to View.
// Action
public ActionResult Index()
{
ViewBag.FirstDropDownList = new List<string>() { "Item 1", "Item 2" };
ViewBag.FirstDropDownListJson = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(ViewBag.FirstDropDownList);
return View("Index");
}
// View using razor
<select>
#foreach(var item in ViewBag.FirstDropDownList)
{
<option value="#item">#item</option>
}
</select>
// View using jQuery
<select id="dropdown1"></select>
<script>
$(function(){
var dropdown1List = JSON.parse('#Html.Raw(ViewBag.FirstDropDownListJson)');
$('#dropdown1').html($.map(dropdown1List, a => `<option value="${a}">${a}</option>`));
});
</script>

In Rails, how do I query my database from JavaScript so that I can fill in a textbox?

I have a view that uses the select method in Ruby on Rails to return the following code:
<select class="form-control" name="xyz" id="food_id">
<option value="1">Peach</option>
<option value="2">Apple</option>
<option value="3">Orange</option>
</select>
To pass this information over to the JavaScript file, I am basically using a link_to method in my view, such as the following:
<%= link_to "Import text", "#/", :onclick => "fillTextBox()" %>
In the JS file, how can I basically get the name and description of whatever value is selected in the dropdown? This is as far as I've gotten before I got confused:
function fillTextBox(){
var dropdown = document.getElementById('food_id');
var foodID = dropdown[dropdown.selectedIndex].value;
}
I need to do something like:
var foodName = Food.find(foodID).name;
var foodDescription = Food.find(foodID).description;
Is this possible? The rest of the JS function would basically fill in a textbox using foodName and foodDescription, but I first need to be able to query the database for this information in the first place.
Have you ever tried Gon gem?
In your controller:
def some_method
gon.foods = Food.all
end
Then in your js
gon.foods
In your case, you can attach the complete Food collection in the controller and then find the Food by its id in javascript using somethig like this
var food = gon.foods.filter(function(food) {
return food.id === foodID;
})[0];

Populate Cascading Dropdown using json with functional Search

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"]
]
}

Options Binding overrides initial View Model value

I tried to follow the other similar questions on Stack Overflow but thus far have been unsuccessful in fixing the problem.
I am using jQuery AJAX to retrieve several items: a contact and its associated information, all available salutation types, all available email types and all available phone types.
I have successfully bound the options to the select boxes. However, it appears to overwrite the 'value' binding that holds the initial view model value.
Could any of you help me solve this? Please let me know if you have any questions for clarification.
Please see the code below:
View Model:
function contactPageViewModel() {
var self = this;
self.contact = ko.observable();
self.availableSalutations = ko.observableArray();
self.availableEmailTypes = ko.observableArray();
self.availablePhoneTypes = ko.observableArray();
self.availableAddressTypes = ko.observableArray();
}
where contact is an object coming from the server, which includes the element contact.salutation.
The json coming back for contact is:
{
//...
"createdBy":null,
"createdOn":1392848929000,
"updatedBy":null,
"updatedOn":1392848929000,
"contactId":305,
"salutation":{"salutationId":102,"salutation":"Mrs."},
"firstName":"Laura",
"middleInitial":"K",
"lastName":"Ritchey"
//...
}
the json coming back from availableSalutations (which is a property of a json object wrapper 'listObject') is:
[{"salutationId":41,"salutation":"Ms."},
{"salutationId":101,"salutation":"Mr."},
{"salutationId":66,"salutation":"CDR"},
{"salutationId":81,"salutation":"LCDR"},
{"salutationId":102,"salutation":"Mrs."},
{"salutationId":121,"salutation":"Mr."},
{"salutationId":64,"salutation":"LTC"}]
The code to map the JSON result to the knockout observables:
contactPageViewModel.contact = ko.mapping.fromJS(data.listObject[0]);
contactPageViewModel.availableEmailTypes = ko.mapping
.fromJS(data.listObject[1]);
contactPageViewModel.availableSalutations = ko.mapping
.fromJS(data.listObject[2]);
....
applyBindings();
The HTML:
<label for="rank"> Rank / Title: </label>
<select data-bind="optionsText: 'salutation',
options: availableSalutations,
value: contactPageViewModel.contact.salutation"
class="rankList"
name="Rank"
id="rankSelect">
</select>
Try value: $root.contact().salutation instead of value: contactPageViewModel.contact.salutation.
Or:
<label for="rank"> Rank / Title: </label>
<!-- ko with: contact -->
<select data-bind="options: $root.availableSalutations, optionsText: 'salutation', value: salutation" class="rankList" name="Rank" id="rankSelect">
</select>
<!-- /ko -->
Update:
You could look at this Fiddle. May be it contains a lot of excess code and you can simplify it, but the main things is to separate initial and selected salutations and add optionsCaption to select bindings:
var initialSalutation = new salutationViewModel(data.salutation);
And:
self.salutation = ko.observable();
self.displayedSalutation = ko.computed(function () {
if (self.salutation()) {
return self.salutation();
} else {
return initialSalutation;
}
})
Update 2:
Look at this Fiddle. I've added optionsValue: 'salutationId' to select bindings and move displayedSalutation to contactPageViewModel.
I think problem was with matching objects (select item and salutation from contact). When value of select is salutationId and contact salutation also salutationId (number value, not object) all working good.

Categories