Writing HTML5 data- to JSON in anonymous function in jQuery - javascript

I have a click-through app using data- properties in HTML, then using jQuery, I want to save each selected option to either an array or as an object. But I'm not sure the best way to approach this - I've tried .data() and .map() with no luck.
Essentially I want to just store the data-app value that is clicked (and keep it as JSON format) through each .step iteration. But I'm not sure how to save the data either to an object or array and I can't seem to find a great solution elsewhere. Any help is greatly appreciated!
HTML:
<div class="start step" id="">
<h2>Select Property Type?</h2>
<div id="choice" data-app='{"PropertyType" : "single_family"}'>Single Family</div>
<div id="choice" data-app='{"PropertyType" : "apartment"}'>Apartment</div>
<div id="choice" data-app='{"PropertyType" : "condo"}'>Condo</div>
<div id="choice" data-app='{"PropertyType" : "hovel"}'>Hovel</div>
</div>
<div class="step" id="">
<h2>Are you looking to live in Toronto?</h2>
<div id="choice" data-app='{"LiveInToronto" : "yes"}'>Yes</div>
<div id="choice" data-app='{"LiveInToronto" : "no"}'>No</div>
</div>
jQuery
var currentStep = 1;
var num = 1;
var formType = '';
var propertyType = '';
var app = [];
$(this).find('.step').each(function(){
$(this).attr('id', 'step'+num);
num++;
$(this).hide();
});
$('.step > #choice').click(function(){
// propertyType = $(this).data('property-type');
data = $(this).data('app');
app = $.map(data, function(value, key){
return (key + ' ' + value);
});
nextStep();
});
console.log(app);
function nextStep() {
$('#step' + currentStep).hide();
currentStep++;
$('#step' + currentStep).fadeIn('fast');
return currentStep;
}
When using console.log(app) outside of the anonymous function, it returns undefined.

Some issues:
console.log(app) will execute on page load, not when the user clicks, so obviously it will not output what is happening in the anonymous function. If you want to output the result there, you must move the console.log(app) statement there.
In HTML the id attribute must have unique values, which is not the case for choice in your code. So replace with a class attribute instead.
You are not collecting data into app, but overwriting it at each step. You should use app.push to not lose the previously stored data
this in top-level code is the window object (or undefined in strict mode), so $(this).find('.step') will not return anything. Use $(document) instead.
It should not be necessary to dynamically assign id attribute values. Instead use some smarter selectors.
Here is some modified code:
var app = [];
// hide all steps, except first (don't use this, but document)
$(document).find('.step').slice(1).hide();
$('.step > .choice').click(function(){
// propertyType = $(this).data('property-type');
data = $(this).data('app');
app.push($.map(data, function(value, key){
// ^^^^^^
return (key + ' ' + value);
}));
console.log(app); // log here!
nextStep();
});
function nextStep() {
var $next = $('.step:visible').next('.step');
$('.step').hide(); // just hide all of them
$next.fadeIn('fast');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="start step" id="">
<h2>Select Property Type?</h2>
<div class="choice" data-app='{"PropertyType" : "single_family"}'>Single Family</div>
<div class="choice" data-app='{"PropertyType" : "apartment"}'>Apartment</div>
<div class="choice" data-app='{"PropertyType" : "condo"}'>Condo</div>
<div class="choice" data-app='{"PropertyType" : "hovel"}'>Hovel</div>
</div>
<div class="step" id="">
<h2>Are you looking to live in Toronto?</h2>
<div class="choice" data-app='{"LiveInToronto" : "yes"}'>Yes</div>
<div class="choice" data-app='{"LiveInToronto" : "no"}'>No</div>
</div>
NB: I doubt that the concatenation of key and value into your array is very useful. You lose the structure that you have in your JSON data attributes.

Related

Trying to extract an element attribute value that is part of an object

I have an input field coded as the following:
<input type="radio" name="03794" class=" js-sourcing-option" data-sourcingoption="{"hasBackOrder":true,"isPartial":false,"unsourcedQty":0,"warehouseSequences":[1],"shipments":{"2017-10-12T17:00:00":{"warehouseNumber":101,"deliverByTime":"2017-10-12T17:00:00","numShipments":0,"warehouseQty":0,"backorderQty":1,"qtyByWarehouseInStock":{},"qtyByWarehouseBackorder":{"101":1}}},"sourcedQty":1,"shouldDisplay":true}">
Using jQuery, I cannot figure out how to get the value of "deliverByTime" (which should equal "2017-10-12T17:00:00") buried deep within the "data-sourcingoption" attribute of this field.
Using the following outputs the "name" attribute (the "[0]" is there below because there are more of these on the page):
$('input.js-sourcing-option')[0]["name"]
// Output: 03794
But if I do a similar thing for "data-sourcingoption" to try and play with the results and extract what I need, I get this:
$('input.js-sourcing-option')[0]["data-sourcingoption"]
// Output: undefined
Any help would be greatly appreciated...thank you!
data-sourcingoption- invalid property value.
Add hidden div with json string, and get .html() value if nided.
$('input:radio[name=03794]').change(function () {
var radio_val=$(this).val();
var json_str=$('#div_for_03794_'+radio_val).html();
var jsonobj = JSON.parse(json_str);
console.log(jsonobj);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input type="radio" name="03794" class=" js-sourcing-option" value="1">
<div id="div_for_03794_1" style="display:none;">
{"hasBackOrder":true,"isPartial":false,"unsourcedQty":0,"warehouseSequences":[1],"shipments":{"2017-10-12T17:00:00":{"warehouseNumber":101,"deliverByTime":"2017-10-12T17:00:00","numShipments":0,"warehouseQty":0,"backorderQty":1,"qtyByWarehouseInStock":{},"qtyByWarehouseBackorder":{"101":1}}},"sourcedQty":1,"shouldDisplay":true}
</div>
<input type="radio" name="03794" class=" js-sourcing-option" value="2">
<div id="div_for_03794_2" style="display:none;">
{"hasBackOrder":false,"isPartial":true,"unsourcedQty":0,"warehouseSequences":[1],"shipments":{"2017-10-12T17:00:00":{"warehouseNumber":101,"deliverByTime":"2017-10-12T17:00:00","numShipments":0,"warehouseQty":0,"backorderQty":1,"qtyByWarehouseInStock":{},"qtyByWarehouseBackorder":{"101":1}}},"sourcedQty":1,"shouldDisplay":true}
</div>
The data attribute syntax seems invalid.
Can you change the outer quotes for the data attribute to a single quote in the following way?
<input type="radio" name="03794" class=" js-sourcing-option" data-sourcingoption='{"hasBackOrder":true,"isPartial":false,"unsourcedQty":0,"warehouseSequences":[1],"shipments":{"2017-10-12T17:00:00":{"warehouseNumber":101,"deliverByTime":"2017-10-12T17:00:00","numShipments":0,"warehouseQty":0,"backorderQty":1,"qtyByWarehouseInStock":{},"qtyByWarehouseBackorder":{"101":1}}},"sourcedQty":1,"shouldDisplay":true}'>
And to answer your question further, (i.e. to extract deliverByTime), you can just use this or the approach that you used to extract attributes:
console.log($('input.js-sourcing-option').data('sourcingoption').shipments['2017-10-12T17:00:00'].deliverByTime)
<input type="radio" name="03794" class="js-sourcing-option" data-sourcingoption="">
<script type="text/javascript">
var Obj = {'hasBackOrder':true,
'isPartial':false,
'unsourcedQty':0,
'warehouseSequences':[1],
'shipments': {
'SomeName': {
'warehouseNumber':101,
'deliverByTime':'2017-10-12T17:00:00',
'numShipments':0,
'warehouseQty':0,
'backorderQty':1,
'qtyByWarehouseInStock': {}
,
'qtyByWarehouseBackorder': {
'101': 1
}
}
}
,
'sourcedQty':1,
'shouldDisplay':true
}
var element = $('.js-sourcing-option').attr('data-sourcingoption', Obj);
console.log(Obj.shipments.SomeName.deliverByTime);
I'm adding the "data-sourcingoption" attribute dinamicly with javascript
The console.log is the output of deliverByTime
all -
I ended up playing around and went with this route. It worked to accommodate for potential multiple entries, etc. I think it works out:
$('.js-sourcing-option').each(function() {
var dataSourcing = $(this).data('sourcingoption');
$.each(dataSourcing.shipments, function(date, shipment) {
console.log(shipment.warehouseNumber, new Date(shipment.deliverByTime));
});
});

Bind a new function to various elements on a page

This might be a dupe, but I couldn't find exactly what I was looking for. What I want is to bind a function to a named-class element on a page, then in later script, call a function against those regions. Specifically, I want to call something like (in the example below)...
$.each( $(".saveRegion"), function( idx, ele ){
var valsToSave = $(ele).getValuesToSave();
// shove the values to save into some construct
// but only for the controls inside the two divs classed as "saveRegion"
});
My page is a set of conditional includes in the Pug file. The reason that's important, is that the individual page includes might not have the same sets of controls, but that region, or parent div's children nodes, are considered a set of information. Think of a form with address information that might be a region, and gender, eye color, and height that might be a region, all included as separate Pug files if the form in question needs that information. The parent page just wants to ask all the regions "give me your information so I can compile and save it", and each of those includes should be able to respond to that.
I'm probably overdoing it, huh?
div.sampleRegions
if user.height == "tall"
include ./userTall.pug
if user.something == "another value"
include .anotherPage.pug
if user.hair == "brown"
include ./userBrownHair.pug
The html in question might look like:
<div id="sampleRegions">
<div class="saveRegion">
<input class="form-control" id="txtUserName" type="text">
<input class="form-control" id="txtPhone" type="text">
</div>
<div>
<input class="form-control" id="favoriteColor" type="text">
</div>
<div class="saveRegion">
<input class="form-control" id="txtCountry" type="text">
<input class="form-control" id="txtLanguage" type="text">
</div>
</div>
So, I want to bind a function like getValuesToSave() to a div, then write the specifics for that div's getValuesToSave() function. I'm using Pug (formerly Jade) to draw forms based on certain user-specific settings, so the page includes I'm using can each know how to get and return the data for their specific page sections via some prototypical function. I hope this is making sense.
This would be a simple matter of an abstract class or a function override in any other language that supports it. I wrote C# server side stuff for systems processing for like 15 years, and this is trivial there. I am sure I'm just missing something super simple. Thanks!
You are able to use jQuery plugins for this purpose.
Example is available here.
You could go with a simple function which looks for all kind of input elements inside a specified elements and maps their values to objects of type { [nameByAttribute]: value }.
Like this:
function getValues(selector, keyAttr) {
var INPUTS = ['textarea', 'input', 'select'].join();
var BOOL_INPUTS = ['checkbox', 'radio'];
var NUMBER_INPUTS = ['range', 'number'];
var regions = [].slice.call(document.querySelectorAll(selector));
return regions.map(function(region) {
var values = [].slice.call(region.querySelectorAll(INPUTS));
return values.map(function(element, index) {
var type = element.getAttribute('type');
var key = element.getAttribute(keyAttr) || index;
var value = element.value;
if(BOOL_INPUTS.indexOf(type) > -1) value = element.checked;
if(NUMBER_INPUTS.indexOf(type) > -1) value = +element.value;
return { [key]: value };
});
});
}
document.querySelector('#save').addEventListener('click', function() {
console.log(getValues('.saveRegion', 'id'));
});
<div id="sampleRegions">
<div class="saveRegion">
<input class="form-control" id="txtUserName" type="radio">
<input class="form-control" id="txtPhone" type="checkbox">
<input class="form-control" id="txtPhone" type="range">
<select idd="language">
<option>EN</option>
<option>DE</option>
<option>FR</option>
</select>
</div>
<div>
<input class="form-control" id="favoriteColor" type="text">
</div>
<div class="saveRegion">
<input class="form-control" id="txtCountry" type="text" value="textInput">
<textarea class="form-control" id="txtLanguage">textArea</textarea>
</div>
<button id="save">save</button>
</div>
This could be easily extended to take a mapping as input, which defines how certain inputs should be handled.
Ok, I think what I'm going to do is just add a function to each of the include pages that knows how to return an object containing the values for that region's salient controls, and name it based on the region name or something else to ensure uniqueness. Something like:
In Include-page-1
<div class="saveRegion" saveFunc="saveNameAddressRegion()">
<input id="val1" ..../>
<input id="val2" ..../>
<input..../>
</div>
<script>
function saveNameAddressRegion(){
// stupid-simple example
return { val1: $("#val1").val(), val2 : $("#val2").val() };
}
</script>
Or in Jade/Pug template:
.saveRegion(saveFunc="saveNameAddressRegion()")
input#val1(type="text")
input#val2(type="text")
input#val3(type="text")
script.
function saveNameAddressRegion(){
// stupid-simple example
return { val1: $("#val1").val(), val2 : $("#val2").val() };
}
Then in Include-page-2
<div class="saveRegion" saveFunc="doItAgain()">
<select id="selEyeColor">
<option value="blue">Blue</option>
<option value="green">Green</option>
<option value="brown">Brown</option>
</select>
</div>
<script>
function doItAgain(){
return { eyeColor : $("#selEyeColor option:selected").val() };
}
</script>
And then the enclosing/parent page can just do something like:
include ./Include-Page-1.pug
include ./Include-Page-2.pug
script.
$.each(".saveRegion", function( idx, ele ){
var vals = [];
vals.push( eval( $(ele).attr("saveFunc") ) );
// process them or whatever here.
});
This lets me get to a point where each Include file has a way of returning its values that may or may not be in some complicated on-screen form, control layout, etc. The parent page can always expect a simplified object with values, without knowing anything at all about each of the include-page layouts, design, contents, etc.
(( shrug ))

JQuery change id attribute of elements

I have a drag and drop thing which uses clone. I am having a problem with the date clone though because of datepicker. Therefore, I need to make sure each cloned datepicker has a unique id. A cloned element looks like the following
<div data-type="date" class="form-group">
<label class="control-label col-sm-5" for="dateInput">Date Input:</label>
<div class="col-sm-3">
<input type="text" name="dateInput[]" class="form-control date_picker" id="dateInput">
</div>
</div>
So if I clone two date inputs, I will have two of the above. Now on submit, I clean all of the cloned html, doing things like removing the data-type. At this stage, if there is a cloned date input, I need to give it a unique id. At the moment I am doing this
$("#content").find(".form-group").each(function() {
var html = $(this).attr('class', 'form-group')[0].outerHTML.replace(/ data-(.+)="(.+)"/g, "");
var input = $(this).find('input');
var i = 0;
if(input.attr('id') == 'dateInput') {
alert("TEST");
input.attr("id",'dateInput' + i).datepicker();
i++;
}
console.log(html);
dataArray.push(html);
});
The TEST alert fires twice as it should do if I clone 2 date inputs. However, the id attributes do not seem to change when I output the html to the console. I have set up the following Fiddle to demonstrate that the id of the element is not changing.
Any advice on getting this to change appreciated.
Thanks
Try defining dataArray, i outside out submit event, .each() , using .map() , .get() , .attr(function() {index, attr}) , .outerHTML
$(function() {
// define `i` , `dataArray`
var i = 0, dataArray = [];
$('#content').submit(function(event) {
event.preventDefault();
$("#content").find(".form-group").each(function() {
var html = $(this).attr('class', '.form-group')[0]
.outerHTML.replace(/ data-(.+)="(.+)"/g, "");
dataArray.push($(html).map(function(_, el) {
// adjust `input` `id` here , return `input` as string
return $(el).find("input").attr("id", function(_, id) {
return id + (++i)
})[0].outerHTML
}).get()[0])
});
$("#output")[0].textContent = dataArray.join(" ");
console.log(dataArray)
});
});
jsfiddle https://jsfiddle.net/mLgrfzaL/2/

How to filter results using jQuery search string

I have a website that has a list of users profiles each in a separate div with a class of user-profile and each has a unique id equal to their name. All of them are within a #reporting container. For example
<div id="reporting">
<div class="user-profile" id="John Smith">...content...</div>
<div class="user-profile" id="Jane Smith">...content...</div>
<div class="user-profile" id="Tom Nolan">...content...</div>
</div>
Then I have an input that I'm trying filter the results with. I would like the user to enter a string, and have the user-profiles fade out if the string is not contained in the ID of element.
Using the example above, if the visitor enters the search string "Smith" both John and Jane Smith would remain visible, but the Tom Nolan div would fade out. If the visitor would to enter Tom, both Jane and John Smith would fade out, but Tom Nolan would remain visible.
I'm trying to achieve this using jQuery. I found this link: http://dinowebs.net/index.php/highlighting-matching-text-in-search-results-with-jquery/ , but it is pretty much the opposite effect I'm trying to achieve, and I couldn't figure out how to modify it to my requirements. Any help would be appreciated!
$(':input[name=filter]').on('input',function() {
//get value just typed into textbox -- see .toLowerCase()
var val = this.value.toLowerCase();
//find all .user-profile divs
$('#reporting').find('.user-profile')
//find those that should be visible
.filter(function() {
return $(this).data('id').toLowerCase().indexOf( val ) > -1;
})
//make them visible
.show()
//now go back and get only the visible ones
.end().filter(':visible')
//filter only those for which typed value 'val' does not match the `data-id` value
.filter(function() {
return $(this).data('id').toLowerCase().indexOf( val ) === -1;
})
//fade those out
.fadeOut();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="reporting">
<div class="user-profile" data-id="John Smith">...content...1</div>
<div class="user-profile" data-id="Jane Smith">...content...2</div>
<div class="user-profile" data-id="Tom Nolan">...content...3</div>
</div>
<input type="text" name="filter"/>
// build an array of values
var data = [];
jQuery('.user-profile').each(function () {
var upid = jQuery(this).attr('id'); // user profile id
data.push(upid);
});
// on change of text input get value of the text input then analyze the reporting div children ids (data array), fade out any who has an id that doesn't contain the letter(s)
jQuery('[name="filter"]').change(function (e) {
var sv = jQuery(this).val();
// check value against data array values
for (var i = 0; i < data.length; i++) {
var tupid = data[i]; // this user profile id
var re = new RegExp(sv, "gi"); // make a regex
var check = tupid.match(re); // see if there is a match
var theid = '#'+tupid; // build actual id
// if this user profile id doesn't match something in the input fade out, if it does fade in
if (Array.isArray(check) === false) {
jQuery(theid).fadeOut();
}else{
jQuery(theid).fadeIn();
}
}
});
Here is the html -- Note with this solution you need to either put underscores in between names or put them together. It works.. Its quick. I haven't used data-id yet as I primarily do php. Anyhow here is the fiddle to show it works ---> http://jsfiddle.net/eot5tfaq/
<div id="reporting">
<div class="user-profile" id="JohnSmith">...content...</div>
<div class="user-profile" id="JaneSmith">...content...</div>
<div class="user-profile" id="TomNolan">...content...</div>
</div>
<input name="filter" type="text" placeholder="Enter Your Search" />
$(document).ready(function(){
$('#filter').keyup(function(){
var value = $(this).val().toLowerCase();
$('#reporting .user-profile').filter(function(){
$(this).toggle($(this).attr('data-id').toLowerCase().indexOf(value) > -1);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="reporting">
<div class="user-profile" data-id="John Smith">...content...1</div>
<div class="user-profile" data-id="Jane Smith">...content...2</div>
<div class="user-profile" data-id="Tom Nolan">...content...3</div>
</div>
<input type="text" name="filter" id="filter"/>
$(document).ready(function(){
$('#filter').keyup(function(){
var value = $(this).val().toLowerCase();
$('#reporting .user-profile').filter(function(){
$(this).toggle($(this).attr('data-id').toLowerCase().indexOf(value) > -1);
});
});
});
And here is HTML
<div id="reporting">
<div class="user-profile" data-id="John Smith">...content...1</div>
<div class="user-profile" data-id="Jane Smith">...content...2</div>
<div class="user-profile" data-id="Tom Nolan">...content...3</div>

Knockout is generating two bindings for single foreach

I'm facing headache issue that led me to spend two days looking for a solution for it. I hope anyone would help me with it. I'm using knockout to generate bindings with json data for HTML markups. However, I'm not able to change the css of the element because I realized the element is generated twice and assigned the same id. Here's snippet of my code
<div id = 'divBinder' data-bind="foreach: Results" >
<div id='rowStyle' class='eligibilitydivContentTableRow' >
<div class='eligibilitydivContentLeftCell' style="float:left;" data-bind=" text: RequirementDescription"></div>
<div class='eligibilitydivContentMiddleCell' style="float:left;">
<span class='spanClass'></span>
<input class='inputRadio' type='radio' value:'true' data-bind="attr: { checked: IsChecked,'name': $index() }" />
<span class='spanClass'></span>
</div>
<div class='eligibilitydivContentRightCell' style="float:left;"><span class='spanClass'></span>
<input class='inputRadio2' type='radio' value:'false' data-bind="attr: { checked: IsChecked, 'name': $index(), onclick:'testFunction.apply(this);return true;'}" />
<span class='spanClass'></span>
</div>
</div>
<div data-bind=" attr: {'id': getSuffixRowID($index())}" style="display:none;" >
<div style="float:left;">
<textarea > </textarea>
</div>
<div>
<input type='text' id='dateField' onfocus='showDate()' /></div>
</div>
</div>
Here are the javascript function I'm using to generate ids
function getSuffixRowID(suffix) {
// alert(suffix);
return 'hiddenRows' + suffix;
}
Here's my binding
viewModel = [];
viewModel.Results = ko.mapping.fromJS(globalizedData.Results);
ko.cleanNode(document.getElementById("parentDivElement"));
ko.applyBindings(viewModel, document.getElementById("parentDivElement"));
Note that the RequirementDescription is binded correctly. The only problem is setting the css through testFunction being called when button is checked
function testFunction() {
// jQuery('#' + getSuffixRowID(this.attributes[6].nodeValue)).hide();
var nodeId = this.attributes['name'].nodeValue;
var stringValue = this.value;
switch (stringValue) {
case ('true'):
viewModel.Results()[nodeId].IsCompleted(true);
viewModel.Results()[nodeId].IsChecked(true);
break;
case ('false'):
viewModel.Results()[nodeId].IsCompleted(false);
viewModel.Results()[nodeId].IsChecked(false);
var idName = getSuffixRowID(nodeId);
$('#' + idName).css('display', 'block !important;');
break;
}
}
The id for checkbox elements are assigned via $index variable inside foreach. I realized the duplicate generation through taking a look at the generate html page. It has two duplicate foreach markups. Any help is really appreciated.
Thanks
This is not the way you should code with KnockoutJS :
onclick:'testFunction.apply(this);return true;'}
The Result object should have two properties (one for each checkbox).
So assuming your Result object looks like :
var Result = function() {
var self = this;
self.checkbox1 = ko.observable();
self.checkbox2 = ko.observable();
};
The binding the checkbox will be :
onclick: $parent:testFunction, value : checkbox2
You can add the id binding if you want.
And the TestFunction :
function testFunction(result/* the result item */) {
if(result.checkbox2()) {
}
[...]
};
With Knockout you souldn't modify the view directly. You have to leverage the viewModel and knockout will modify the view for you.
Please take a look at the visible binding too.
I hope it helps.

Categories