I have two selectors in ERB. They use the Chosen plugin:
<%= select_tag :provinces,
options_for_select(DataHelper::all_provinces_captions.zip(DataHelper::all_provinces_ids)),
{:multiple => true, class: 'chosen-select chzn-select',
:data => {:placeholder => 'Filter Provinces/States'}}
%>
<%= f.select :province_ids,
(DataHelper::all_provinces_captions.zip(DataHelper::all_provinces_ids)),
{ include_blank: true },
{multiple: true, data: {placeholder: 'Filter Provinces/States'} }
%>
I am trying to copy the options from one of the selectors to the other one, while keeping the selected options still selected, however it is not working. Here is the Javascript function I have tried:
var selectedVals = [];
$(".chzn-select").chosen().change(function() {
$("#provinces option:selected").each(function () {
console.log ("this value is " + ($(this).val()));
selectedVals.push($(this).val());
});
$("#education_plan_province_ids").empty();
for (var i = 0; i < selectedVals.length; i++) {
console.log (selectedVals[i] + " selected");
$("#education_plan_province_ids").append($("<option>" + selectedVals[i] + "</option>").attr('selected', true));
}
selectedVals = [];
});
Is there another alternative to attr('selected', true) ?
Here you go:
$(".chzn-select").chosen().change(function() {
$("#education_plan_province_ids").empty();
$("#provinces option:selected").each(function () {
$("#education_plan_province_ids").append($("<option>" + this.value + "</option>").prop('selected', true));
});
});
I am using prop here and getting rid of extra array (which I think is not needed but you can use it if you want). Also you had parenthesis in wrong place for option.
Related
I am looking to pull data from a job board API. I'd like to have headings for the departments (pulled from JSON level 1) and under each department the current open positions (JSON level 2). I have tinkered with this 50 different ways and ran through all the related articles I can find, but just can't quite get the dominoes to fall in my brain.
Update
I have gotten pretty close, but I'm obviously missing how to loop this correctly. It repeats every department and job instead of nesting all of the jobs under the department header once.
Fiddle to see where I am at - https://jsfiddle.net/swampfox/f6jv204x/#&togetherjs=GjcUL090zr
$(function() {
$.ajax({
url: 'https://boards-api.greenhouse.io/v1/boards/agilityrobotics/departments',
data: {
check: 'one'
},
dataType: 'jsonp',
success: function(result) {
$.each(result, function(key, value) {
for (var i = 0; i < value.length; i++) {
$.each(value[i].jobs, function(k, v) { // Second Level into Jobs
$('#jsonpResult').append(
$('<h3>' + value[i].name + '</h3><p class="cat"><a class="joblink" href="' + v.absolute_url + '"> ' + v.title + '</a></p>')
);
});
}
});
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="jsonpResult"></div>
The main issue is that you output the h3 for each job, but it should only be output once per iteration of the outer loop (not the inner loop).
I would also use more jQuery style for creating the elements, and I would use async/await to flatten a bit the "callback hell".
$(async function() {
let {departments} = await $.getJSON('https://boards-api.greenhouse.io/v1/boards/agilityrobotics/departments');
$("#jsonpResult").append(
departments.flatMap(({name, jobs}) => [
$("<h3>").text(name),
...jobs.map(({absolute_url: href, title}) =>
$("<p>", { "class": "cat" }).append(
$("<a>", { href, "class": "joblink" }).text(title)
)
)
])
);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="jsonpResult"></div>
To exclude departments for which there are no jobs:
$(async function() {
let {departments} = await $.getJSON('https://boards-api.greenhouse.io/v1/boards/agilityrobotics/departments');
$("#jsonpResult").append(
departments.flatMap(({name, jobs}) => jobs.length ? [
$("<h3>").text(name),
...jobs.map(({absolute_url: href, title}) =>
$("<p>", { "class": "cat" }).append(
$("<a>", { href, "class": "joblink" }).text(title)
)
)
] : [])
);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="jsonpResult"></div>
I want to create a table where rows and buttons can be added via a button (one for rows, one for columns). The columns consist of one collection_select (to select a device) as the title and text_field in each row (for test results for this device in each row).
The columns consist of several text_field and one text_area (for different test specifications).
I use this code to add a column in _form.html.erb:
<%= bootstrap_form_with(model: Testreport.new, local: true) do |f| %>
<table class="table" id="testreport_table">
...
<input type=button id='col_1_button' value="+" onclick="insertColumn();">
...
</table>
function insertColumn() {
let table = document.getElementById('testreport_table'),
columns_count = table.rows[1].cells.length,
rows_count = table.getElementsByTagName('tr').length,
i;
document.getElementById('button_row').colSpan = columns_count + 1;
<% a = f.collection_select( :devicesample_id, Devicesample.order(:name), :id, :name, include_blank: false, label: "Device Sample") %>
table.rows[1].insertCell(columns_count).innerHTML = '<%= a %>'
for (i = 2; i < (rows_count - 1); i++) {
table.rows[i].insertCell(columns_count).innerHTML = "Added Column";
}
return false;
}
However, rendering the page throws this error (Chrome): Uncaught SyntaxError: Invalid or unexpected token
The error is in this generated line:
table.rows[1].append('<select name="testreport[devicesample_id]" id="testreport_devicesample_id"><option value="4">Hisense 6486 LATAM</option>
Whole generated code:
function insertColumn() {
let table = document.getElementById('testreport_table'),
columns_count = table.rows[1].cells.length,
rows_count = table.getElementsByTagName('tr').length,
i;
document.getElementById('button_row').colSpan = columns_count + 1;
table.rows[1].insertCell(columns_count).innerHTML = '<div class="form-group"><label for="testreport_devicesample_id">Device Sample</label><select class="form-control" name="testreport[devicesample_id]" id="testreport_devicesample_id"><option value="4">Device A LATAM</option>
<option value="1">Device B </option>
<option value="3">Device C </option></select></div>';
for (i = 2; i < (rows_count - 1); i++) {
table.rows[i].insertCell(columns_count).innerHTML = "Added Column";
}
return false;
}
When I just copy the same exact collection_select to the regular body, it displays fine. What is causing this error? I have the same issue if I want to add a text_area, while text_field is working fine.
I am suspecting that it has something to do with the multi-line property of these fields, but even if that is the case I do not know how to avoid this.
I am new to Ruby, Rails and Javascript. I use:
ruby 2.6.5p114 (2019-10-01 revision 67812) [x64-mingw32]
Rails 6.0.2.1
Chrome Version 79.0.3945.130
(edit: replaced table.rows[1].append('<%= a %>');with table.rows[1].insertCell(columns_count).innerHTML = '<%= a %>'': which was the original code)
You get Uncaught SyntaxError: Invalid or unexpected token error because you have a multiline string in your code.
So change quotes from
table.rows[1].insertCell(columns_count).innerHTML = '<%= a %>'
to
table.rows[1].insertCell(columns_count).innerHTML = `<%= a %>`
You can also remove new lines:
table.rows[1].insertCell(columns_count).innerHTML = '<%= a.tr("\n","") %>'
Of course, it's better to move HTML generation to javascript side, but it's another topic :)
submitBtn.addEventListener("click",()=>{
//check to see the answer
const answer=getSelected();
if(answer){
if(answer === quizData[currentQuiz].correct){
score++;
}
currentQuiz++;
if(currentQuiz < quizData.length){
loadQuiz();
}
else{
quiz.innerHTML ='
<h2> you answered correctly at $
{score}/${quizData.length}
questions.</h2>
<button onclick="location.reload()">Reload</button>
';
}
}
});
In my ruby on rails project, I have a form with a group of sliders that look like this:
<%= range_field(:subproject, :ans1, in: 0..100, id: 'slider1', :class => 'range range--light', :'data-init' => 'auto', :step => 1, :value => $project_value_1.to_i) %>
The total values of the sliders should always be equal to 100 fo rthe form to be submittable. Otherwise, an error message is displayed. All that is handled by this function:
$sumOfProjectValues = $project_value_1.to_i + $project_value_2.to_i + $project_value_3.to_i + $project_value_4.to_i + $project_value_5.to_i
if $sumOfProjectValues == 100
message = 'Project can be saved'
btnStyle = 'confirmation'
hideDisabled = 'none'
else
message = 'Project quota must add up to 100% before saving'
btnStyle = 'alert'
hideEnabled = 'none'
end
I would like to call this form validation function everytime a value changes (sort of like the onChange method in JS).
How do I do this?
try code:
$(".range").onChange(function(){
var sumOfProjectValues = $project_value_1.to_i + $project_value_2.to_i + $project_value_3.to_i + $project_value_4.to_i + $project_value_5.to_i
if sumOfProjectValues == 100
alert('Project can be saved')
else
alert('Project quota must add up to 100% before saving')
end
})
How to titleize/captialize words (with the exception of some prepositions) in the text_area as the user types in their words in real time?
<%= f.text_area :name %>
For example, to create the same behavior as this website: http://titlecapitalization.com/
If you are using Jquery, your problem will be solved by code given below.
function makeTitle(slug) {
var words = slug.split(' ');
$.each(words, function(index, word){
words[index] = word.charAt(0).toUpperCase() + word.slice(1);
});
return words.join(' ');
}
$(element_selector).on('keyup', function(){ $(this).val( makeTitle($(this).val())) })
I want to implement the ability to dynamically add comboboxes and I have to use Telerik ComboBox for that. I put this logic into button click.
$('#add-presenter').click(function (e) {
e.preventDefault();
var combobox = '#(Html.Telerik().ComboBox()
.Name("Presenters[" + (Model.Count) + "]")
.BindTo(new SelectList(LeaderList, "ID", "Value"))
.ClientEvents(ev => ev.OnChange("onSelect"))
.DataBinding(bnd => bnd.Ajax().Select("_LoadJournalist", "MonitoringFRadio"))
.Filterable(filter => filter.FilterMode(AutoCompleteFilterMode.StartsWith))
.HtmlAttributes(new { style = "width:320px;vertical-align:middle;" }))';
combobox = combobox.split('Presenters[' + index + ']').join('Presenters[' + (index + 1) + ']');
index++;
$('#presenters-block').append(combobox);
}
This code renders in browser as this:
$('#add-presenter').click(function (e) {
e.preventDefault();
var combobox = '<div class="t-widget t-combobox t-header" style="width:320px;vertical-align:middle;"><div class="t-dropdown-wrap t-state-default"><input class="t-input" id="Presenters[0]-input" name="Presenters[0]-input" type="text" /><span class="t-select t-header"><span class="t-icon t-arrow-down">select</span></span></div><input id="Presenters[0]" name="Presenters[0]" style="display:none" type="text" /></div>';
combobox = combobox.split('Presenters[' + index + ']').join('Presenters[' + (index + 1) + ']');
index++;
$('#presenters-block').append(combobox);
combobox = $('#Presenters\\['+index+'\\]').data('tComboBox');
});
The problem is in data-binding. This code generates proper HTML, but newly added list doesn't "drop"
When I do combobox = $('#Presenters\\['+index+'\\]').data('tComboBox'); for newly added item I get undefined (it exists, but data isn't set), so combobox.dataBind(dataSource) approach doesn't work.
Ok, I tried, but failed to do this without postback. Here's rough solution to the problem: do ajax request and replace content with partial view:
The Partial view:
#model List<int>
#{
var LeaderList = ViewData["LeaderList"] as List<ListItem>;
}
<div id="presenters-ajax-wrapper">
<div id="presenters-block">
#(Html.Telerik().ComboBox()
.Name("Presenters[0]")
.BindTo(new SelectList(LeaderList, "ID", "Value"))
.ClientEvents(ev => ev.OnChange("onSelect"))
.DataBinding(bnd => bnd.Ajax().Select("_LoadJournalist", "MonitoringFRadio"))
.Filterable(filter => filter.FilterMode(AutoCompleteFilterMode.StartsWith))
.HtmlAttributes(new { style = "width:320px;vertical-align:middle;" }))
#for(int i=1; i<Model.Count; i++)
{
var item = LeaderList.FirstOrDefault(l => l.ID == Model[i]);
var value = item != null ? item.Value : "";
#(Html.Telerik().ComboBox()
.Name("Presenters[" + i + "]")
.Value(value)
.BindTo(new SelectList(LeaderList, "ID", "Value"))
.ClientEvents(ev => ev.OnChange("onSelect"))
.DataBinding(bnd => bnd.Ajax().Select("_LoadJournalist", "MonitoringFRadio"))
.Filterable(filter => filter.FilterMode(AutoCompleteFilterMode.StartsWith))
.HtmlAttributes(new { style = "width:320px;vertical-align:middle;" }))
}
</div>
<button id="add-presenter" class="t-button">+</button>
<script type="text/javascript">
var index = #(Model.Count == 0 ? 0 : Model.Count-1);
$('#add-presenter').click(function (e) {
e.preventDefault();
index++;
var msg = $('#monitForm').serialize();
$.ajax({
url: '#Url.Action("_GetPresenters","MonitoringFRadio")'+'?count='+(index+1),
data: msg,
type: 'POST',
success: function(data) {
$('#presenters-ajax-wrapper').html(data);
}
});
});
</script>
</div>
Action:
[HttpPost]
public virtual ActionResult _GetPresenters(EditableMonitoring model, int count)
{
//some logic here...
return PartialView("EditorTemplates/Presenters", model.Presenters);
}
Well, probably it would be better to create another partial view which would render a single combobox, instead of redrawing all of them...