Error when attempting to dynamically change Select2 - javascript

I am developing an ASP.NET MVC web project. I am currently using select2 for several dropdowns in my project, due to its clean and appealing features. I am also having some problems with select2. I am wanting to create a dropdown on a create page that 'changes' state when another dropdown value is changed. Select2 has a drop-down that offers both the ability to select from the dropdown results OR type your own entry in and save. (see the tagging section on https://select2.github.io/examples.html). I need to be able to turn tags on/off dynamically through javascript AFTER page load. It seems that select2 will only initialize the dropdown and after that, it won't allow dynamic changes.
View Html and Javascript code used to initialize select2 on page load:
<div class="col-md-8">
#Html.ListBoxFor(model => model.modelNum, Model.modelNumList, new { #class= "js-tags" })
</div>
<script type="text/javascript">
$(".js-tags").select2({
placeholder: "Enter Model",
tags: true,
maximumSelectionLength: 1
})
</script>
The above part works great. Select2 is initialized, and follows my settings. After this, my plan is to change the settings of the select2 my destroying and reinitializing the instance, therefore editing the dropdown and turning tags back off. The below javascript method is called when another dropdown is changed.
CODE:
<script type="text/javascript" language="javascript">
function ChangeModelNum(_classID)
{
if(_classID == 1)
{
$(".js-tags").select2("destroy").select2({
placeholder: "Select Model",
tags: false,
maximumSelectionLength: 1
})
}
else
{
$(".js-tags").select2({
placeholder: "Enter Model",
tags: true,
maximumSelectionLength: 1
})
}
}
As you can see, if the user selects the classID as 1, I want to disable tagging for the dropdown. But it simply doesn't work. I also get this error in developer tools:
JavaScript runtime error: Object doesn't support property or method 'select2'
I have made sure there are no double jquery references also. It makes no sense why it has an error with Select2 right after using it perfectly.
ADDITION:
Here is the code that triggers ChangeModelNum:
<div class="col-md-2">
#Html.DropDownListFor(model => model.classnum, Model.classL, "Select Class", new { #onchange = "javascript:ChangeModelNum(this.value);", #class = "form-control" })
</div>
Thanks for any help!

I am not sure, but I think that this particular error could be caused by method chaining
$(".js-tags").select2("destroy").select2...
Try to split it to two separate calls:
$(".js-tags").select2("destroy");
$(".js-tags").select2({
placeholder: "Select Model",
tags: false,
maximumSelectionLength: 1
})

Related

Select2 Multi-select Strange Overlay

When the page loads initially the placeholder cannot be seen and there is a strange circle (I think it's related to the search feature in Select2) in the bottom right hand corner which is overlaying the placeholder.
Then, as an option is selected from the Select2 dropdown, the option shows up, but the entire area is pushed down due to that circle overlay.
If I clear the selections, the place holder shows up, but the circle is still there.
My application uses Backbone and Marionette
Here is the code I'm using in my .js file (I've played with the minimumResultsForSearch option but haven't had any luck).
this.$el.find("#categorySelect").select2({
multiple: true,
allowClear: true,
minimumResultsForSearch: -1,
placeholder: "Categories"
})
Here is the code I'm using in my .tpl file
<select id="categorySelect" name="Categories" multiple="multiple" class="form-control" style="width: 100%;">
<% for(category in categories){ %>
<option value="<%=categories[category].title%>"
<% if (categories[category].active == true) { %>
selected
<%}%>
>
<%=categories[category].title%></option>
<% }; %>
</select>
Your order of scripts is incorrect, you need to load jQuery before you use it.
Also you're trying to initialize select2 on elements that does not exist yet, i.e before actually rendering the view.
And there are somethings that doesn't make any sense like
this.$el.html(_.extend(this.template(modelJSON)));
Why would you try to extend an HTML string?
or
$(function(select2) {
App.start(select2);
});
What is passing select2 as first argument to jQuery ready event handler?
Please try to understand the code you are writing, and why you're writing it.
Fixing all such issues gives something like this http://jsfiddle.net/az5dksyz/2/.
Also it's a good idea to not use id in template strings because they could be rendered multiple times, and to not use inline style rules in them.

jQuery Validate, Select2, and Bootstrap 3 Popovers - How to Bind Popover To Select2's Parent Elements Instead of Hidden Select Element

I've implemented the excellent method to use Bootstrap 3's Popovers to show validation error messages, and for the most part it works well - except when it comes to hidden or replaced elements, like Select2 or CKEditor. The popover positions itself correctly on regular elements like input and normal selects, but not a select enhanced by Select 2.
I've created a Fiddle that Sparky fixed so it actually works: http://jsfiddle.net/jemxtthb/4/
$("#noticeSentTo").select2();
var validator = $("#documentAdmin").validate({
debug: true,
ignore: "",
showErrors: function(errorMap, errorList) {
$.each(this.successList, function(index, value) {
return $(value).popover("hide").parents(".form-group").removeClass('has-error').addClass('has-success');
});
return $.each(errorList, function(index, value) {
var _popover;
_popover = $(value.element).popover({
trigger: "manual",
placement: "auto top",
content: value.message,
container: "body",
template: "<div class=\"popover\" role=\"tooltip\"><div class=\"arrow\"></div><div class=\"popover-content\"><p></p></div></div>"
});
_popover.data("bs.popover").options.content = value.message;
return $(value.element).popover("show").parents(".form-group").addClass('has-error').removeClass('has-success');
});
}
});
});
Here's a screen capture of what I see within my application:
I've tried using the selector option on the popovers, but can't figure out how to specify the current element the popover's being applied to (if that's even the right way to do it).
As you can see from my screen capture, the popover appears up and to the far left of the element (in red) it's supposed to be bound to because Select2 is hiding the original select.
UPDATE: Based on #Sparky's suggestion, I've changed it to use errorPlacement instead of the method I used previously - the only issue now is getting the condition where if it's a hidden element (like Select2 or CKEditor) to provide the Popup to the parent object instead of the element itself (since Popovers cannot work on hidden elements, apparently) - I've put in some debug console.log code just to make sure the if statement in the jQuery is firing correctly:
var validator = $("#documentAdmin").validate({
ignore: [],
errorPlacement: function (error, element) {
var lastError = $(element).data('lastError'),
newError = $(error).text();
$(element).data('lastError', newError);
if (newError !== '' && newError !== lastError) {
$(element).popover({
trigger: "manual",
placement: "auto top",
content: newError,
container: "body",
template: "<div class=\"popover\" role=\"tooltip\"><div class=\"arrow\"></div><div class=\"popover-content\"><p></p></div></div>"
});
if (element.is(':hidden')) {
// $(element).next().parents(".form-group").addClass('has-error').removeClass('has-success').popover("show");
$(element).next('span').popover('show').addClass('has-error').removeClass('has-success');
console.log('hidden element');
} else {
$(element).popover("show").parents(".form-group").addClass('has-error').removeClass('has-success');
console.log('normal element');
}
}
},
success: function (label, element) {
$(element).popover("hide").parents(".form-group").removeClass('has-error').addClass('has-success');
}
});
And here's an updated Fiddle: http://jsfiddle.net/jemxtthb/6/
OK - really odd - the Fiddle above actually works as expected! Now I need to figure out why it works in the Fiddle and not my app...
Any help/guidance is appreciated!
Looking at the errors in the JavaScript console and fixing the following in your jsFiddle...
There is no point in having a select element with validation if it comes pre-loaded with a value. In other words, there is nothing to validate if every option already contains a value or there is an option with a value that has the selected attribute. Typically, the first option contains value="".
<option value="">Please select<option>
There was a 404 error on your Bootstrap script file. The CDN URL was mangled.
Not critical, but it should be ignore: [], not ignore: "".
You're getting a "no 'name' assigned" error in the debug console because the hidden input element created by Select2 does not contain a name attribute. It's apparently not an issue now, but it could be an issue when you have more elements on the page. The jQuery Validate plugin mandates that all elements to be validated contain a name attribute.
DEMO: http://jsfiddle.net/jemxtthb/4/
for the most part it works well - except when it comes to hidden or replaced elements, like Select2 or CKEditor. The popover positions itself correctly on regular elements like input and normal selects, but not a select enhanced by Select 2.
You should not use showErrors for tooltips because this callback function is typically used for generating a list of all messages, like for a summary at the top of the form. "Gets the map of errors as the first argument and an array of errors as the second."
Instead, you'll need to integrate your tooltips plugin with jQuery Validate by using its errorPlacement and success callback functions; which will put the single pending error message inside a single tooltip. This integration also depends on the available options of your popovers plugin... like can you dynamically change the text inside a tooltip? Can you dynamically/programmatically show/hide them, etc?
Then within errorPlacement you can conditionally target certain elements to tweak the placement when the element is hidden by Select2, etc.
Something more like this...
$("#documentAdmin").validate({
ignore: [],
errorPlacement: function (error, element) {
// put text of error into tooltip with $(error).text()
// conditional placement of the tooltip
// show the tooltip
},
success: function (label, element) {
// hide the tooltip
},
// any other options
});
I'm using the ToolTipster jQuery plugin like this.
As it turns out, the above code worked correctly from the beginning - after #Sparky fixed my Fiddle, the popover worked correctly, positioning itself in relation to the Select2's parent element.
What benefit came out of this was #Sparky's suggestion to use errorPlacement versus the method I was originally using, which gives more control over individual elements' error message placement methods.
I am currently investigating (and suspect) the culprit is an offcanvas plugin I'm using for a sidebar - I didn't include this in the Fiddle because I wanted to keep the Fiddle as concise as possible to the issue and not complicate things. I'll update this when I find out what the problem is. If anyone's interested in implementing this method for validation errors in Bootstrap 3 Popovers, I would use the code in the second Fiddle I posted.

Use #Html.ValidationMessageFor or other validation (custom messages) on pure html elements in MVC

Because I use a plugin called select2, I need to define the select lists in my application in pure Html (so without any helpers) like so:
<select name="TopicIds" id="topDrop" multiple style="width: 700px" required>
#foreach (var top in ((List<Topic>)ViewBag.TopIds).Where(top => top.MainTopic == null))
{
<option value="#top.TopicId" class="optionGroup">#top.Name</option>
foreach (var subTopic in top.SubTopics)
{
<option value="#subTopic.TopicId" class="optionChild">#subTopic.Name</option>
}
}
</select>
As you can see 1 element needs to be chosen, that's why I set the "required" attribute.
The validation happens, when the form submits it doesn't go through when nothing is selected in the list. I would like to set a custom error message though when the validation for the field happens.
Normally you would use the helper #html.ValidationMessageFor(), but that doesn't seem to be working with this pure html control. You can see the select list is binded to the property "TopicIds" in my ViewModel.
I tried this: #Html.ValidationMessageFor(model => model.TopicIds), the only message I get is the default "This field is required", strangely enough only when this helper is defined...
How can I customize this message? I've been seraching for a while and tried all kinds of solutions with JavaScript and such, but nothing seems to change the message, I always get "This field is required.".
Anyone know how to do this? I'm using all this in an ASP.NET MVC 4 web application.
Annotate the property in your ViewModel
[Required(ErrorMessage = "My Custom Message")]
public string Id {get;set;}
Then you Html.ValidationMessageFor

Multiselect Rails 3, Default Selections with Javascript

I have a Rails 3 application where I allow the user to apply tags to their listing. I've read a few stackoverflow threads with similar questions but this one is unique since I am using a frontend framework. I am using the webarch framework: http://www.revox.io/webarch/form_elements.html
If you CTRL + F "Multi Select" you can see the exact element that I am using.
Summary
I use this code to populate tags:
<%= f.collection_select :tag_ids, Tag.all, :id, :name, {:selected => #listing.tags.map(&:id).map(&:to_i)}, { :multiple => true, :style => "width:100%", :id => "multi", :class => "select2-offscreen", :placeholder => "Select Tags." } %>
This works to assign tags, but the tags that have already been saved to a listing do not appear in the dropdown on the edit view. For example, if the a user has tagged their listing as "Jim" and "Lucy", they should see this when they arrive on the edit view.
Framework's JS code
The framework is added to the site. In the JS files, it has the following code:
$(document).ready(function(){
//Dropdown menu - select2 plug-in
$("#source").select2();
//Multiselect - Select2 plug-in
$("#multi").val(["Jim","Lucy"]).select2();
This is excellent starting logic, but obviously I don't want "Jim" "Lucy" manually coded into my dropdowns. How can I override this in js to make it work correctly for the existing tags?
{:selected => #listing.tags.map(&:id).map(&:to_i)}
I just had to go into the plugin's files and change the code to:
$(document).ready(function(){
//Dropdown menu - select2 plug-in
$("#source").select2();
//Multiselect - Select2 plug-in
var multi = $("#multi");
var selected = multi.find("option[selected]").map(function() { return this.value; });
multi.val(selected).select2();

value from dropdown show/hides div and keeps its state

im relatively new to ROR, and would like some help with the following code.
I would like to show a div (it's called demographics) when a specific selection ("Customers") is made form a dropdown. If an option other than "Customers" is selected from the dropdown, I would like that the demographicsv div remain hidden.
At present, my code is partially working. It shows and hides the div; however, when I save the page and come back to it, the div is hidden, even if "Customers" is selected in the dropdown. This might be because the default setting is: display:none.
Is there a way to make sure that the field is displayed on page load when the proper selection has been made?? And I suppose a way to make the opposite true too--> a div with display:block; remains hidden if the field from the dropdown does not match the required field.
Any help would be extremely appreciated. Thank you and I sincerely appreciate your time:
my code (HAML):
= f.select :who, WHO, {}, :onChange => "showDemographics(this)", :prompt => true
#demographics{:style => "display:none;"}
%p.information
please fill out the following demographics.
%div{:class => "label_leftalign field"}
= f.label :age, "Age"
= f.select :age, AGES, :prompt => true
%div{:class => "label_leftalign field"}
= f.label :race,
= f.select :race, RACES, :prompt => true
Javascript
function showDemographics(obj)
{
if(obj[obj.selectedIndex].value == 'Customer')
{
document.getElementById('demographics').style.display = 'block';
}
else
{
document.getElementById('demographics').style.display = 'none';
}
}
EDIT::
OK, So the key issue is that on page reload a div remains hidden even if the correct value is in the dropdown box. I suppose that this can be solved by one of two ways 1. running some sort of validation on everytime the page is loaded or 2. cookies.
Unfortunately, I'm not very well versed in Javascript or jQuery... and I've never implemented cookies on a site, and would prefer to avoid using them if possible.
Could anyone help me write a script that validates the presence of Customer in the dropdown and shows/hides a div accordingly? Thank you, and thanks to Carlos Pardilla and M. Cypher for their help thus far.
You have tagged the question with jQuery although you're not using it in the code, but I'll just assume it's fine if I give you some jQuery code:
$(function() {
$('#who').change(function() {
if ($(this).val() == 'Customer') {
$('demographics').show();
} else {
$('demographics').hide();
}
}).change();
}
Note that you need to give the select an id (in this case who) for it to work.
I guess you could use a conditional statement to be checked during page load. You could establish the visibility (or lack thereof) of the div depending on the value ("Customers") of the option:selected element within your select object (the dropdown). Maybe via a script on the head of the document (provided it is a short script). Also, by doing this you wouldn't need to set the display property from the start. It would fire depending on the selected condition during page load.
This could be done to get the opposite result as well, as you said.
Hope it'll help!
The answers above where correct in their own way... the link below shows the actual code that is required for the conditional statement on page load:
show hidden divs on refresh

Categories

Resources