How to use JSON data in rails and provide it to javascript - javascript

I have a rails application where I have JSON data as shown below:
{"makes":[{"id":200347864,"name":"AM General","niceName":"am-general","models":[{"id":"AM_General_Hummer","name":"Hummer","niceName":"hummer","years":[{"id":3407,"year":1998},{"id":1140,"year":1999},{"id":305,"year":2000}]}]}]}
This is a very long list of car objects with multi-levels of nesting. Make, model, year, trim etc.
I want to send this JSON to javascript and populate my autofill drop down menu.
Previously, I was using a third-party API and the code looked like:
$( document ).ready(function() {
var prev ="https://xyz/makes";
var rest ="?fmt=json&api_key=";
var key = "<%= ENV['XYZ_API_KEY'] %>";
var net = prev+rest+key;
var options = [];
options.push("All");
var dictionary = {};
$.ajax({
url: net,
dataType: "json",
type: "get",
data: $(this).serialize()
}).done(function(data){
change_make(data);
change_model();
});
function change_make(data) {
for (var key in data["makes"]){
if (data["makes"].hasOwnProperty(key)){
var make = data["makes"][key];
options.push(make.name);
buffer = [];
// console.log(make.models);
for (var key2 in make.models){
// console.log(make.models[key2].name);
if (make.models.hasOwnProperty(key2)){
// console.log(make.models[key2].name);
buffer.push(make.models[key2].name);
}
}
dictionary[make.name] = buffer;
}
}
dictionary["All"] = ["All"]
// console.log(options);
$.each(options, function(key, value) {
$('#category')
.append($("<option></option>")
.attr("value", value.html_safe)
.text(value));
});
};
$('#category').on('change', function(){
console.log("change success");
change_model();
});
function change_model(){
var make = $('#category').find(":selected").text();
var models = dictionary[make];
// models.unshift("All");
$('#subcategory').empty();
$.each(models, function(key, value) {
$('#subcategory')
.append($("<option></option>")
.attr("value",value.html_safe)
.text(value));
});
}
$("#searchboxcontainer").delay(100).fadeIn(200);
});
Instead of using the ajax request, I want to use json string directly.
I wrote a helper method in application helper module as shown:
def edmunds_json
the_json_object
end
But when I am using it in javascript, its adding &gm characters and theobject comes out as:
{:makes=>[{:id=>200347864, :name=>"AM General",...
and the code is giving errors
Uncaught SyntaxError: Unexpected token :
When I am using escape in JSON, it's giving me the error on rails unexpected $undefined. expecting ').
The JSON object is using double quotes and I want to use it in my code. How should I proceed so &gt etc won't be added.

I think what you need to do is to first convert your Ruby Hash into json in your helper
def edmunds_json
the_json_object.to_json
end code here
Then in your view in a script tag do something like this
<script>
const object = <%= raw edmunds_json %>
console.log(typeof(object)) // => Object
</script>

Solved it by using:
<%= get_make_model_json.to_s.gsub("=>",":").html_safe %>; in javascript
where get_make_model_json is:
def get_make_model_json
JSON.parse(
'{
"makes": [{
"id": 200347864,
"name": "AM General",
"niceName": "am-general",
"models": [{
"id": "AM_General_Hummer",
"name": "Hummer",
"niceName": "hummer",
"years": [{
"id": 3407,
"year": 1998
}, {
"id": 1140,
"year": 1999
}, {
"id": 305,
"year": 2000
}]
}]
},...
end

Related

Trying to display a value from an array returned from external script

<script>
var url = "https://api.bridgedataoutput.com/api/v2/zestimates?access_token=3c8b83bd020d7edb2083adccb0670b37&address=";
var path = url + avmaddr;
var my_script = document.createElement('script');
my_script.setAttribute('src',path);
document.head.appendChild(my_script);
</script>
avmaddr is being pulled from a form and passed to this page. As an example, when I put in an address in the form, path = https://api.bridgedataoutput.com/api/v2/zestimates?access_token=3c8b83bd020d7edb2083adccb0670b37&address=1021%20Fortrose%20Dr%2C%20Gallatin%2C%20TN%2037066
If you click on that URL, you’ll get this back…
{
"success": true,
"status": 200,
"bundle": [{
"id": "133926112",
"zpid": "99342382",
"address": " 1021 Fortrose Dr Gallatin TN 37066",
"coordinates": [-86.526053, 36.371459],
**
"zestimate": 471752,
"upper": 508096,
"lower": 443027 ** ,
"date": "2020-02-19T00:00:00.000Z",
"rental": {
"zestimate": 2599,
"upper": 2989,
"lower": 2209,
"date": "2020-02-18T00:00:00.000Z"
},
"foreclosureZestimate": null,
"forecastStandardDeviation": 0.09985694289207458,
"zillowUrl": "https://www.zillow.com/homedetails/99342382_zpid",
"url": "api.bridgedataoutput.com/api/v2/zestimates/zestimates/133926112"
}],
"total": 1
}
I want to display the value for “zestimate”, as well as “upper” and “lower”. Any advice would be appreciated! I'm not a developer, so I really have no clue what to do next!
use jQuery would make this easier
first load jQuery in your html file, put this code in the head tag of your html file:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" </script>
then in your Javascript file. use jQuery to call the api:
$.ajax({
url: "https://api.bridgedataoutput.com/api/v2/zestimates?access_token=3c8b83bd020d7edb2083adccb0670b37&address=1021%20Fortrose%20Dr%2C%20Gallatin%2C%20TN%2037066",
dataType: "json",
success: data => {
//here you can do whatever you want to the response data:
alert(data. zestimate);
}
})
You can try to use fetch to get the response from that url:
var url = "https://api.bridgedataoutput.com/api/v2/zestimates?access_token=3c8b83bd020d7edb2083adccb0670b37&address=";
var avmaddr = "1021%20Fortrose%20Dr%2C%20Gallatin%2C%20TN%2037066";
var path = url + avmaddr;
fetch(path).then(function (response) {
return response.json();
}).then(function (data) {
var zestimate = data.bundle[0].zestimate;
var upper = data.bundle[0].upper;
var lower = data.bundle[0].lower;
console.log(zestimate);
console.log(upper);
console.log(lower);
});

How to send data from jquery to javascript

<script>
$.getJSON('chartState',{
stateCode : $(this).val(),
ajax : 'true'
},
function(data) {
alert("state data"+data);
});
</script>
I have the value in data and want to show in javascript given below.
The fields data is given want to push my state data there.
<script>
var salesChartData = {
datasets: [{
data: ["here i want my data"]
}]
};
</script>
Both are written in diffrent script
datasets is an array with an object on index 0. So to define or redeclare the data property in there the syntax is
salesChartData.datasets[0].data = data;
Use it in your callback function:
function(data) {
salesChartData.datasets[0].data = data;
});
Not sure if I understand correctly, is this what you need?
var salesChartData = {
datasets: [
{
data : {}
}
]
};
$.getJSON('chartState',{
stateCode : $(this).val(),
ajax : 'true'
},
function(data) {
salesChartData.datasets[0].data = data;
});
Just set the data after receiving it
if you need to show the ajax result in a variable salesChartData, you can try this
salesChartData.datasets[0].data[0] = "new data"
salesChartData is a JSON object with key datasets contains an array of JSON objects.
So if salesChartData is declared globally, then you can replace in the success of the ajax
Here below, it done using web storage. This is used to access from different file.
// File 1
var salesChartData = {
datasets: [{
data: ["here i want my data"]
}]
};
localStorage.setItem("salesChart", JSON.stringify(salesChartData));
//-----------------------------------------------------------------------
// File 2
var salesChartData = JSON.parse(localStorage.getItem("salesChart"));
// ajax call
$.getJSON('chartState', {
stateCode: $(this).val(),
ajax: 'true'
},
function (data) {
alert("state data" + data);
salesChartData.datasets[0].data[0] = data // "new data"
});
Hope this will work.
Thank You
I have done with my self
$.getJSON('chartState',{
stateCode : $(this).val(),
ajax : 'true'
},
function(data) {
var chr=data;
var a=chr[0];var b=chr[1];var c=chr[2];var d=chr[3];
var e=chr[4];var f=chr[5];var g=chr[6];
After that I have sended one by one data
var salesChartData = {
datasets: [
{
data : [g,f,e,d,c,b,a]
}
]
};
As you mention that both parts of the script are in different tags you can solve the problem with a global, this is not recommended. The better solution would be to refactor the structure and not have multiple script tags. But if you have no control over this then you should do something like this:
<script>
// No var used to make it global
chart_state_data = false;
$.getJSON('chartState',{
stateCode : $(this).val(),
ajax : 'true'
},
function(data) {
// the data is set to this variable on callback
chart_state_data = data
});
</script>
And:
<script>
// chart_state_data contains data retrieved from ajax call or false
var salesChartData = {
datasets: [{
data: chart_state_data
}]
};
</script>

How to filter search input to show correct autocomplete in JQUERY?

I have a search input where my autocomplete shows but for some reason it the results are not being filtered - can anyone tell or show me a way to filter results to show the correct autocomplete prior in my code below.. Below is the json format and the html code updated. Thanks for the help.
Here is my code
$( function() {
var cache = {};
$( "#searchTextField" ).autocomplete({
minLength: 2,
source: function( request, response ) {
var term = request.term;
if ( term in cache ) {
response( cache[ term ] );
return;
}
$.post( "http://localhost:8080/myApp/JobSearchItem.xhtml", request,
function( data, status, xhr ) {
cache[ term ] = data;
response( data );
});
}
});
} );
JobSearchItem Return JSON
[
{
"id": "9000",
"label": "PROGRAMMER TEST 1 (9000) ",
"value": "90000"
},
]
html
<h:body>
<f:view transient="true">
<tp:header/>
<tp:searchForm/>
<div id="results">
</div>
<h:panelGroup id="dTable" class="container">
</h:panelGroup>
</f:view>
<f:view transient="true">
<div class="jobEntity">
<div class="job-container-header">
<h4>#{testBean.jobEntity.toString()}</h4>
<c:if test="#{testBean.jobEntity.validURLConnection}">
<a href="#{testBean.jobEntity.pGradeDescriptionLink}"
class="btn btn-info-One"
target="_blank">[ Test ]</a>
</c:if>
<h4>#{testBean.jobEntity.mu} - #{testBean.jobEntity.muDescription}</h4>
<h4>#{testBean.jobEntity.specialNotes}</h4>
<h4>#{testBean.jobEntity.syRgeMnSepMsg}</h4>
</div>
<c:if test="${testBean.jobEntity.sectionToDisplay eq 'Range'}">
<table class="table">
<tbody>
<tr>
<th></th>
<c:forEach var="stepNumber" begin="1" end="#{testBean.jobEntity.stepSize}">
<th>Step #{stepNumber}</th>
</c:forEach>
</tr>
<c:forEach items="#{testBean.jobEntity.jobRows}" var="jobRow">
<tr>
<th>#{jobRow.rateType}</th>
<c:forEach items="#{jobRow.steps}" var="step">
<td>#{step.amount}</td>
</c:forEach>
</tr>
</c:forEach>
</tbody>
</table>
</c:if>
</div>
When you specify a remote URL as the datasource like this, the remote server is expected to do the filtering based on the search term given to it by the autocomplete, and return the results already filtered.
Autocomplete only carries out the filtering if you provide it with static data. See http://api.jqueryui.com/autocomplete/#option-source for more details.
N.B. If your remote server is unable to do any filtering (e.g. because it just returns a static file) then you'd have to filter the data client-side in your callback before you return it to the autocomplete. But of course this is not very efficient because you keep downloading all the data and then discarding most of it (unless the browser helpfully caches it).
Since you are calling data from a .xhtml file, it is not going to be able to filter the results, unless you can update the server side script to accept and perform activities based on data posted to it.
I would suggest you gather the static data upfront and then filter based on that. This might look something like:
$( function() {
var myData;
$.get( "http://localhost:8080/myApp/JobSearchItem.xhtml", function( data ){
myData = data;
} );
$( "#searchTextField" ).autocomplete( {
minLength: 2,
source: myData
} );
} );
This assumes that your xhtml is providing a Array of data (usually in JSON format). This can be simple:
[
"Item 1",
"Item 2",
"Item 3"
];
Or something more advanced:
[{
"label": "Item 1",
"value": 1
},{
"label": "Item 2",
"value": 2
},{
"label": "Item 3",
"value": 3
}];
If the data you get back is something else: HTML Table, XML, or text, then using a function with Source will help you. If you update your question and provide an example of the data, we could provide a more complete example or guidance.
Update 1
Given the following JSON Data:
[{
"id": "9000",
"pGrade": "0",
"label": "PROGRAMMER TEST 1"
},{
"id": "6000",
"pGrade": "0",
"label": "WEB PROGRAMMER TEST 1"
}];
This does not comply with the standard Autocomplete expected data. If you are able to POST data to JobSearchItem.xhtml, then you can have it filter first and return data. If JobSearchItem.xhtml does not accept POST, then I would perform a GET of all the data up front and then filter it later. I will include an example of both.
POST
If you are posting the data, the server-side script needs to know what data you are sending it in the form of a variable name and value. You did not supply a variable name in your example and you have not supplied the JobSearchItem.xhtml content, so it's really hard to identify how this script works.
For this example, we will use term and our example data will be we. If this was a GET command, it would look like:
JobSearchItem.xhtml?term=we
For Post we will use an Object that is submitted:
{ "term": "we" };
Here are the basics:
$(function(){
var cache = {};
$("#searchTextField").autocomplete( {
minLength: 2,
source: function(request, response){
var t = request.term;
if (t in cache){
response(cache[t]);
return;
}
var results = [];
$.ajax({
url: "http://localhost:8080/myApp/JobSearchItem.xhtml",
data: {
term: t
},
dataType: "json",
method: "POST",
success: function( data, status, xhr ) {
$.each(data, function(k, v){
results.push({
label: v.label,
value: v.label,
id: v.id,
grade: v.pGrade
});
});
cache[t] = results;
});
response(results);
});
}
});
});
So, in this case, if the user enters we, this is sent to the script, which will filter the results and will send back JSON that should look like:
[{
"id": "6000",
"pGrade": "0",
"label": "WEB PROGRAMMER TEST 1"
}];
Since Autocomplete is expecting an object containing label and value can't just be sent direct to response(). Using $.each() we can iterate the results and adjust so that it's formatted for Autocomplete.
GET
If your obSearchItem.xhtml is static and just provides a list of JSON data, using GET might be a good way to collect this data. Consider that you can get all this data up front, and then use it later. This is the most common way to use Autocomplete, but the data still has to be in the right format.
$( function() {
var myData = [];
$.get("http://localhost:8080/myApp/JobSearchItem.xhtml", function(data){
$.each(data, function(k, v){
myData.push({
label: v.label,
value: v.label,
id: v.id,
grade: v.pGrade
});
});
});
$("#searchTextField").autocomplete({
minLength: 2,
source: myData
});
});
One of these should work.

How to create complex javascript object for JSON API

Below is the structure of JSON which I use to query an API
"order_items": [
{
"menu_item_id": "VD1PIEBIIG",
"menu_item_name": "Create Your Own",
"modifiers": [
{
"modifier_id": "6HEK9TXSBQ",
"modifier_name": "Shrimp"
}
],
"quantity": "1",
"total": 15.99,
"variant_id": "TXDOR7S83E",
"variant_name": "X-Lg 18\""
}
]
Now I want to call this API from an HTML page using Javascript(Using HTML elements like forms and drop down menus etc). I want to create a Javascript object with proper structure and then convert it to JSON using "stringify" function. But I am not able to create the Javascript object. Can anyone help with this?
Like i want to have the following structure
obj.order_items[0].menu_item_id="VD1PIEBIIG";
obj.order_items[0].menu_item_name="Create Your Own";
obj.order_items[0].modifiers[0].modifier_id="6HEK9TXSBQ";
and so on.
var jsonToSend = { "order_items": [ ] };
// then for each order item
var orderItem = { "menu_item_id": <whatever>,
"menu_item_name": <whatever>,
"quantity": <whatever>,
"total": <whatever>,
"variant_id": <whatever>,
"variant_name": <whatever>,
"modifiers": []
};
// then for each modifier
var modifier = { "modifier_id": <whatever>, "modifier_name": <whatever> };
orderItem.modifiers.push(modifier);
jsonToSend.order_items.push(orderItem);
JSON.stringify(jsonToSend);
Well there are a couple of ways to do this.
Manually create the Json object to send from the HTML elements:
$.ajax({
type: "POST",
url: "some.php",
data: new {"order_items": [
{
"total": $('total').Val(),
"variant_id": $('variant_id').Val(),
"variant_name": $('variant_name').Val()
}
]})
.done(function( msg ) {
alert( "Data Saved: " + msg );
});
You could use a great framework like KnockoutJs, this will keep your JSON object up to date with your form, so that you don't have to do it manually. When you are ready you just submit your original json back to the server.
See this basic example on JsFiddle
var ClickCounterViewModel = function() {
this.numberOfClicks = ko.observable(0);
this.registerClick = function() {
this.numberOfClicks(this.numberOfClicks() + 1);
};
this.resetClicks = function() {
this.numberOfClicks(0);
};
this.hasClickedTooManyTimes = ko.computed(function() {
return this.numberOfClicks() >= 3;
}, this);
};
ko.applyBindings(new ClickCounterViewModel());
You can use any number of plugins to Serialize the form, but the problem is getting the JSON structure just right.
See SerializeArray
$( "form" ).submit(function( event ) {
console.log( $( this ).serializeArray() );
event.preventDefault();
});

twitter bootstrap typeahead (method 'toLowerCase' of undefined)

I am trying to use twitter bootstrap to get the manufacturers from my DB.
Because twitter bootstrap typeahead does not support ajax calls I am using this fork:
https://gist.github.com/1866577
In that page there is this comment that mentions how to do exactly what I want to do. The problem is when I run my code I keep on getting:
Uncaught TypeError: Cannot call method 'toLowerCase' of undefined
I googled around and came tried changing my jquery file to both using the minified and non minified as well as the one hosted on google code and I kept getting the same error.
My code currently is as follows:
$('#manufacturer').typeahead({
source: function(typeahead, query){
$.ajax({
url: window.location.origin+"/bows/get_manufacturers.json",
type: "POST",
data: "",
dataType: "JSON",
async: false,
success: function(results){
var manufacturers = new Array;
$.map(results.data.manufacturers, function(data, item){
var group;
group = {
manufacturer_id: data.Manufacturer.id,
manufacturer: data.Manufacturer.manufacturer
};
manufacturers.push(group);
});
typeahead.process(manufacturers);
}
});
},
property: 'name',
items:11,
onselect: function (obj) {
}
});
on the url field I added the
window.location.origin
to avoid any problems as already discussed on another question
Also before I was using $.each() and then decided to use $.map() as recomended Tomislav Markovski in a similar question
Anyone has any idea why I keep getting this problem?!
Thank you
Typeahead expect a list of string as source
$('#manufacturer').typeahead({
source : ["item 1", "item 2", "item 3", "item 4"]
})
In your case you want to use it with a list of objects. This way you'll have to make some changes to make it works
This should works :
$('#manufacturer').typeahead({
source: function(typeahead, query){
$.ajax({
url: window.location.origin+"/bows/get_manufacturers.json",
type: "POST",
data: "",
dataType: "JSON",
async: false,
success: function(results){
var manufacturers = new Array;
$.map(results.data.manufacturers, function(data){
var group;
group = {
manufacturer_id: data.Manufacturer.id,
manufacturer: data.Manufacturer.manufacturer,
toString: function () {
return JSON.stringify(this);
},
toLowerCase: function () {
return this.manufacturer.toLowerCase();
},
indexOf: function (string) {
return String.prototype.indexOf.apply(this.manufacturer, arguments);
},
replace: function (string) {
return String.prototype.replace.apply(this.manufacturer, arguments);
}
};
manufacturers.push(group);
});
typeahead.process(manufacturers);
}
});
},
property: 'manufacturer',
items:11,
onselect: function (obj) {
var obj = JSON.parse(obj);
// You still can use the manufacturer_id here
console.log(obj.manufacturer_id);
return obj.manufacturer;
}
});
In my case, I was getting this exact error, and it turned out that the version of Bootstrap I was using (2.0.4) did not support passing in a function to the source setting.
Upgrading to Bootstrap 2.1.1 fixed the problem.
UPDATED / REVISED LOOK AT THE ISSUE: The error you mentioned "Cannot call method 'toLowerCase' of undefined" occurred to me when I used the original Typeahead extension in bootstrap that does not support AJAX. (as you have found) Are you sure that the original typeahead extension isn't loading, instead of your revised one?
I have been sucessfully using this Gist of typeahead that is a slight variation on the one you mention. Once I switched to that Gist and confirmed that the input data was good (testing that the input was a string array in an JS object, the issue went away.
Hope this helps
Original answer:
The reason the error occurs is because the value passed into the typeahead matcher function is undefined. That is just a side effect to the real issue which occurs somewhere between your input and that matcher function. I suspect the 'manufacturers' array has a problem. Test it first to verify you have a valid array.
// It appears your array constructor is missing ()
// try the following in your binding code:
var manufacturers = new Array();
Here is what I am using to bind the input to typeahead. I confirmed that it works with your modified typeahead fork.
My HTML:
<!-- Load bootstrap styles -->
<link href="bootstrap.css" rel="stylesheet" type="text/css" />
...
<input type="text" class="typeahead" name="Category" id="Category" maxlength="100" value="" />
...
<!-- and load jQuery and bootstrap with modified typeahead AJAX code -->
<script src="jquery-1.7.2.min.js" type="text/javascript"></script>
<script src="bootstrap-modified-typeahead.js" type="text/javascript"></script>
My binding JavaScript code:
// Matches the desired text input ID in the HTML
var $TypeaheadInput = $("#Category");
// Modify this path to your JSON source URL
// this source should return a JSON string array like the following:
// string[] result = {"test", "test2", "test3", "test4"}
var JsonProviderUrl = "../CategorySearch";
// Bind the input to the typeahead extension
$TypeaheadInput.typeahead({
source: function (typeahead, query) {
return $.post(JsonProviderUrl, { query: query }, function (data) {
return typeahead.process(data);
});
}
});
In my case I had null values in my source array causing this error.
Contrived example:
source : ["item 1", "item 2", null, "item 4"]
Your manufacturer objects don't have a "name" property, you should change
property: 'name',
to
property: 'manufacturer',
Have you tried using mapping variables like example below, you need to use this when you got more then one property in your object;
source: function (query, process) {
states = [];
map = {};
var data = [
{"stateCode": "CA", "stateName": "California"},
{"stateCode": "AZ", "stateName": "Arizona"},
{"stateCode": "NY", "stateName": "New York"},
{"stateCode": "NV", "stateName": "Nevada"},
{"stateCode": "OH", "stateName": "Ohio"}
];
$.each(data, function (i, state) {
map[state.stateName] = state;
states.push(state.stateName);
});
process(states);
}
I stumbled upon this issue a while ago. I advice you to recheck configuration where you are including typeahead libraries (via main.js, app.js or config.js).
Otherwise, you can overwrite the latest Bootstrap version to your application library.
Try this code:
String.prototype.hashCode = function(){
var hash = 0;
if (this.length == 0) return hash;
for (i = 0; i < this.length; i++) {
char = this.charCodeAt(i);
hash = ((hash<<5)-hash)+char;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
};
var map_manufacter, result_manufacters;
$('#manufacturer').typeahead({
source: function(typeahead, query){
$.ajax({
url: window.location.origin+"/bows/get_manufacturers.json",
type: "POST",
data: "",
dataType: "JSON",
async: false,
success: function(results){
result_manufacter = [], map_manufacters = {};
$.each(results.data.manufacturers, function(item, data){
map_manufacters[data.Manufacturer.manufacturer.hashCode()] = data;
result_manufacters.push(data.Manufacter.manufacter);
});
typeahead.process(result_manufacters);
}
});
},
property: 'name',
items:11,
onselect: function (obj) {
var hc = obj.hashCode();
console.log(map_manufacter[hc]);
}
});
I solved this problem only updating the file bootstrap3-typeahead.min.js to the file in https://github.com/bassjobsen/Bootstrap-3-Typeahead/blob/master/bootstrap3-typeahead.min.js
I think most people can solve this way.

Categories

Resources