Basic API with HTML: Creating Objects - json

I have a pretty simple calculator that I am trying to setup.
<!DOCTYPE html>
<p>Crypto Currency Diversifier</p>
<div class="container">
<h1>Crypto Currency Diversifier</h1>
<form action="/action_page.php">
Dollars:<br>
<input type="text" name="dollars" value="500"><br><br>
# of Currencies:<br>
<input type="text" name="quantity" value="20"><br><br>
<input type="submit" value="Submit">
</form>
The next step is I need to pull the top 20 (quantity) cryptocurrencies, then divide the 500 (dollars) between them relative to market cap.
Here's the API website: https://coinmarketcap.com/api/
Clicking on https://api.coinmarketcap.com/v1/ticker/?limit=10 shows me the top 10 currencies.
How can I ADD up all the market caps of each individual currency on the page. Then create ratios from them?
Math part is pretty straightforward, what I don't get is how to actually pull the data from the API link and get it in a workable format.

Lookup Jquery and Ajax:
You can implement this like this:
function getCurrencies()
{
$.ajax(
{url:"https://api.coinmarketcap.com/v1/ticker/?limit=20",
success:function(data) {
//console.log(data);
var totalcap=0.0;
var u=$("<ul>");
for(var i=0;i<data.length;i++) {
var currency=data[i];
var cap=parseFloat(currency["market_cap_usd"]);
var li=$("<li />").html(currency["name"] + " " + cap.toFixed(2).replace(/./g, function(c, i, a) {
return i && c !== "." && ((a.length - i) % 3 === 0) ? ',' + c : c;
}));
u.append(li);
totalcap+=cap;
}
$('#currlist').html('').append(u);
$('#cap').html(totalcap.toFixed(2).replace(/./g, function(c, i, a) {
return i && c !== "." && ((a.length - i) % 3 === 0) ? ',' + c : c;
}));
$('#data').val(JSON.stringify(data));
},
error:function() {
alert("Error");
},
dataType:"json",
type:"get"}
);
}
textarea {width:600px;height:250px;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<p>Crypto Currency Diversifier</p>
<div class="container">
<h1>Crypto Currency Diversifier</h1>
<form action="/action_page.php">
Dollars:<br>
<input type="text" name="dollars" value="500"><br><br>
# of Currencies:<br>
<input type="text" name="quantity" value="20"><br><br>
<input type="submit" value="Submit" onclick="getCurrencies(); return false"
>
Total Market Cap:<div id="cap"></div><br />
Currencies: <div id="currlist"></div><br />
<textarea id="data"></textarea>
</form>

Related

Html: JavaScript: input: razor:minRange and maxRange

I have an input element with a minRange and maxRange set and am trying to get a custom validity message. I also have a Input TagHelper to write a custom message that I am trying to override on client-side.
I have tried this so far and it doesn't seem to be working.
<input asp-for="Amount" minRange="50.00" maxRange="100.00" oninput="check(this)" />
<script type="text/javascript">
function check(input) {
if (input.validity.rangeUnderflow || input.validity.rangeOverflow) {
input.setCustomValidity("Please enter an amount between $50.00 and $100.00.");
}
}
</script>
Rendered html:
<input oninput="check(this)" type="text" data-val="true" data-val-number="The field Amount must be a number." data-val-required="The Amount field is required." id="Model_Amount" name="Model.Amount" value="97.95" data-val-range-min="50.00" data-val-range-max="100.00" data-val-range="Please enter an Amount between 50.00 and 100.00" class="form-control valid" placeholder="Amount" aria-required="true" aria-invalid="false" aria-describedby="Model_Amount-error">
It still inputs "Please enter Amount between 50.00 and 100.00"
input taghelper:
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (MinRange != null && MaxRange != null)
{
TagHelperAttribute minAttr = new TagHelperAttribute("data-val-range-min", MinRange);
output.Attributes.Add(minAttr);
TagHelperAttribute maxAttr = new TagHelperAttribute("data-val-range-max", MaxRange);
output.Attributes.Add(maxAttr);
TagHelperAttribute rangeAttr = new TagHelperAttribute("data-val-range", string.Format("Please enter a {0} between {1} and {2}", DisplayName, MinRange, MaxRange));
output.Attributes.Add(rangeAttr);
}
}
Any help is appreciated.
Thanks
NH
Changed your code with some comments added. Hope this helps
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<!-- Change minRange/maxRange to just min/max -->
<input asp-for="Amount" min="50.00" max="100.00" oninput="check(this)" />
<span id="display"></span>
</body>
<script type="text/javascript">
function check(input) {
//Get current input field value
var num = parseInt(document.querySelector('input').value);
//Check if num is < or > than input.min/max
if(num >= input.min && num <= input.max){
document.getElementById('display').innerText = '';
}else{
document.getElementById('display').innerText = 'Number must be between 50 and 100';
}
}
</script>
</html>

Failure handler in Google Apps Script receives object different from what is thrown on server side

My app sends submitted form data to this server-side function:
function processFormData(data)
{
data = JSON.parse(data);
// validate data
var errorObject = {},
potholeErrors = createErrorObjectFor('pothole'),
intervalSizeErrors = createErrorObjectFor('intervalSize');
// make sure numbers are actual numbers and not NaN's.
if (!validateNumber(data.potholeWidth))
{
potholeErrors.messages.push(errorTypes.NOT_A_NUMBER);
errorObject.potholeWidth = potholeErrors;
}
if (!validateNumber(data.intervalSize))
{
intervalSizeErrors.messages.push(errorTypes.NOT_A_NUMBER);
errorObject.intervalSize = intervalSizeErrors;
}
// make sure numbers are within their respective bounds (handled by handleErrors())
errorObject = handleErrors(data, errorObject);
// if intervalSize doesn't divide potholeWidth, make it so
if (data.potholeWidth % data.intervalSize > 0) data.potholeWidth = nextMultiple(data.intervalSize, data.potholeWidth);
// if there is anything in errorObject, throw it
if (Object.getOwnPropertyNames(errorObject).length != 0)
{
Logger.log('errorObject == ' + JSON.stringify(errorObject, null, '\t'));
throw errorObject;
}
// createSpreadsheet
return createSpreadsheet(data.spreadsheet, data.potholeWidth, data.intervalSize);
}
which, upon success, does exactly what it's supposed to do. However, when end-user enters any invalid input, the object the server-side throws back is different than the one they end up getting. I tried entering a pothole width that was too small. When I inspected Logger on server-side, I saw this correct output:
however, in the Developer console, I see:
The code that communicates data to the server looks like:
function updateURL(url)
{
// activate button
$('#input[type="submit"]').prop('disabled', '');
// change href of #spreadsheetLink
$('#spreadsheetLink').attr('href', url);
// unhide the link's container if hidden
if ($('#spreadsheetLink').parent().hasClass('hidden')) $('#spreadsheetLink').parent().removeClass('hidden');
// hide the 'Loading...' element
if (!$('#loading').hasClass('hidden')) $('#loading').addClass('hidden');
}
function emailLink()
{
google.script.run.withSuccessHandler(function() {
$('#emailLink').next().text('E-mail message has been sent!');
$('#emailLink').prop('disabled', 'disabled');
}).emailLink($('#spreadsheetLink').attr('href'));
}
function handleFails(failData)
{
var DEBUG = true;
if (DEBUG) console.log('failData == ' + JSON.stringify(failData, null, '\t'));
// hide 'Loading...' element
if (!$('#loading').hasClass('hidden')) $('#loading').addClass('hidden');
// for now, let's ignore any Errors/TypeErrors.
if ((!failData instanceof Error) && (!failData instanceof TypeError))
{
// for now, if there were any errors with any of the fields, simply mark them as .invalid
if ((failData.potholeWidth) && (failData.potholeWidth.messages.length > 0))
{
if (!$('#potholeWidth').hasClass('invalid')) $('#potholeWidth').addClass('invalid');
}
if ((failData.intervalSize) && (failData.intervalSize.messages.length > 0))
{
if (!$('#intervalSize').hasClass('invalid')) $('#intervalSize').addClass('invalid');
}
}
}
function submitFormData()
{
// hide spreadsheetLink container if not already done, and clear its <span> element if not already clear
var spreadsheetLinkContainer = $('#spreadsheetLink').parent(),
spanElement = $('spreadsheetLinkContainer').find('span');
if (!$(spreadsheetLinkContainer).hasClass('hidden')) $(spreadsheetLinkContainer).addClass('hidden');
if ($(spanElement).text() != '') $(spanElement).text('');
// get all data
var potholeWidth = parseNumberField('potholeWidth'),
intervalSize = parseNumberField('intervalSize') || defaults.get('intervalSize'),
concaveEdges = $('input[name="concaveEdges"]').filter(function() { return $(this).prop('checked'); }).next().text() === 'Yes',
spreadsheetName = parseField('spreadsheetName') || defaults.get('spreadsheetName');
// make button inactive
if (($(this).prop('tagName')) && ($(this).prop('tagName').toLowerCase() == 'input')) $(this).prop('disabled', 'disabled');
// show "Loading..." element
if ($('#loading').hasClass('hidden')) $('#loading').removeClass('hidden');
// submit this data to the server
google.script.run.withSuccessHandler(updateURL).withFailureHandler(handleFails).processFormData(JSON.stringify({
potholeWidth: potholeWidth,
intervalSize: intervalSize,
concaveEdges: concaveEdges,
spreadsheet : spreadsheetName
}));
}
and the HTML looks something like this:
<form>
Place straightedge/yardstick along width of pothole such that it points at the corners, <a class="showImage">like this</a>
<span class="row">
<label class="firstColumn seventeenTwentieths">Pothole width (in inches): </label>
<input type="text" class="secondColumn tenth numberField" id="potholeWidth" required />
</span>
<span class="rowTight">
<label class="firstColumn seventeenTwentieths">Interval size (in inches, default 1 inch): </label>
<input type="text" class="secondColumn tenth numberField" id="intervalSize" value="1" />
</span>
<div class="rowTight">
<label class="firstColumn">Do any of the edges intersect the straightedge/yardstick other than at the corners?</label>
<div class="secondColumn">
<span>
<input type="radio" name="concaveEdges" id="yesConcaveEdges" />
<label for="yesConcaveEdges">Yes</label>
</span>
<br>
<span>
<input type="radio" name="concaveEdges" id="noConcaveEdges" checked />
<label for="noConcaveEdges">No</label>
</span>
</div>
</div>
<span class="rowTight">
<label class="firstColumn half">Spreadsheet name: </label>
<input type="text" class="secondColumn nineTwentieths" id="spreadsheetName"/>
</span>
<span class="center row">
<button class="center" id="clearForm">Clear</button>
<input type="submit" class="center action" value="Create spreadsheet" />
</span>
</form>
<span id="loading" class="row center fullWidth hidden">
Loading...
</span>
<span class="row center fullWidth hidden">
Here is your spreadsheet
<button id="emailLink">E-mail me the link</button>
<span></span>
</span>
What is that object the client actually gets and how to make sure that it's getting the object the server actually throws?
I fixed it.
What I did
In code.gs
Instead of simply throw errorObject;, I said throw JSON.stringify(errorObject);
In JavaScript.html file
in handleFails(), I converted the string the server-side threw back into object (failData = JSON.parse(failData)) to use it. It outputted it correctly, and all is well.
What I learned
Any time the server is either giving or receiving data, it MUST be in the form of a string! (Use JSON.stringify() to make that data a string!)

Why are my inputs not resetting?

The below code generates several forms depending on data returned from the server. Everything generates fine, but after clicking on an AnswerOpenQuestion button the input does not clear/reset. What's going on here?
angularJs code:
var availableInterviewController = function($scope, $http) {
// define initial model
$scope.interviews = [];
// retrieve available interviews
$http.get('/api/UserInterviewsApi/AvailableInterviews')
.success(function(data) {
// update interviews
$scope.interviews = [];
$scope.interviews = data;
});
// define open question answer selection
$scope.Answer = "";
// define multiple choice selection
$scope.selectedChoice = "";
// define answer open question button
$scope.AnswerOpenQuestion = function() {
$scope.Answer = ans;
alert(q.Question + ' and ' + $scope.Answer);
$scope.Answer = ''; // <---This is not clearing/resetting the HTML form inputs
};
// define answer multiple choice button
$scope.AnswerMultipleChoice = function() {
//
};
};
// assign the new controller to the main angular app
myAngApp.controller('availableInterviewCtrl', availableInterviewController);
Html code:
<form class="form-group" ng-repeat="q in inter.Questions">
<fieldset style="display: inline-block;">
<legend>Question {{$index + 1}}</legend>
<!--Open Ended-->
<div class="form-group" ng-show="q.MultipleChoices.length === 0">
<label for="{{'quest-' + $index}}">
<strong class="text-info">{{q.Question}}</strong><br />
</label>
<input name="openQuestion" id="{{'quest-' + $index}}" type="text"
class="form-control" ng-model="Answer" />
<button ng-click="AnswerOpenQuestion()">Answer</button><br />
<span class="text-info">
asked by {{q.AskedByUserName ==
'Administrator' ? 'staff' : q.AskedByUserName}}
</span>
</div>
<!--Multiple Choice Question-->
<div class="form-group" ng-show="q.MultipleChoices.length > 0">
<label for="{{'quest-' + $index}}">
<strong class="text-info">{{q.Question}}</strong>
</label>
<div>
Select an answer:
<label ng-repeat="x in q.MultipleChoices">
<input name="currentChoice" type="radio" value="{{x.Id}}"
ng-model="selectedChoice" />
{{x.Choice}}
</label>
<button ng-click="AnswerMultipleChoice()">Answer</button><br />
<span class="text-info">
asked by {{q.AskedByUserName ==
'Administrator' ? 'staff' : q.AskedByUserName}}
</span>
</div>
</div>
</fieldset>
</form>
UPDATE - Solution
AngularJs:
// define open question answer selection
$scope.OpenAnswer = { Answer: '' };
// define answer open question button
$scope.AnswerOpenQuestion = function (q, ans) {
$scope.OpenAnswer.Answer = ans;
alert(q.Question + ' and ' + $scope.OpenAnswer.Answer);
// clear the input
$scope.OpenAnswer.Answer = '';
};
Html:
<input id="{{'quest-' + $index}}" type="text"
class="form-control" ng-model="OpenAnswer.Answer" />
Don't use the scope as a model instead make an object that wraps the data model and assign it to a property of the scope.
$scope.myModel = {Answer:''}
Also don't use value in most cases ngmodel is all you need for two way binding.
In js strings are immutable so the original reference is not being updated instead a new string is being made, the digest cycle won't see this as a change to the original string.

integer output returning NaN in javascript

I'm trying to write this extremely simple javascript app where the user can input two numbers and the sum of these two is automatically outputted to screen. I'm very new to javascript so I'm not sure where I went wrong. The following code will print NaN for a second after I press the button. Any idea as to how I handle this input better?
<!DOCTYPE html>
<html>
<head>
<script>
function myFunction()
{
var x = parseInt(first_num);
var y = parseInt(second_num);
var z = x + y;
var a = z.toString();
document.getElementById("demo").innerHTML = a;
}
</script>
</head>
<body>
<p>Enter two numbers to be added together.</p>
<form>
<input id="first_num" name="first_num" > first num<br>
<input id="second_num" name="second_num" > second num
<button onclick="myFunction()">calculate</button>
<p id="demo"></p>
</body>
</html>
var x = parseInt(first_num);
var y = parseInt(second_num);
Instead of first_num and second_num, you need to retrieve the value from the input boxes, like this
var x = parseInt(document.getElementById("first_num").value);
var y = parseInt(document.getElementById("second_num").value);
You have to use a selector to get the value of the two input elements:
var x = parseInt(document.getElementById('first_num').value);
var y = parseInt(document.getElementById('second_num').value);
...
And you have to close your form tag in your HTML use and use a <input type="button"> instead of <button> so your form doesn't post unnecessarily.
<form>
<input id="first_num" name="first_num" > first num <br />
<input id="second_num" name="second_num" > second num <br />
<input type="button" onclick="myFunction()" value="calculate">
</form>
Here is working demo.
The reason of NaN is you trying to parseInt with not initialized variable. beside this input tag is not closed.
function myFunction() {
document.getElementById("demo").innerHTML = parseInt(document.getElementById('first_num').value) + parseInt(document.getElementById('second_num').value);
}
and html:
<p>Enter two numbers to be added together.</p>
<input id="first_num" name="first_num" />first num
<br>
<input id="second_num" name="second_num" />second num
<button onclick="myFunction()">calculate</button>
<p id="demo"></p>
Here is a working sample:
<!DOCTYPE html>
<html>
<head>
<title>Page</title>
</head>
<body>
<p>Enter two numbers to be added together.</p>
<input id="first_num" name="first_num">first num<br />
<input id="second_num" name="second_num">second num<br />
<button onclick="myFunction()">calculate</button>
<p id="demo"></p>
<script>
function myFunction() {
var x = document.getElementById("first_num").value;
var y = document.getElementById("second_num").value;
var z = ((x && x != "") ? parseInt(x) : 0) + ((y && y != "") ? parseInt(y) : 0);
document.getElementById("demo").innerHTML = z;
}
</script>
</body>
</html>
Do the following:
<p>Enter two numbers to be added together.</p>
<form>
<input id="first_num" name="first_num" > first num<br>
<input id="second_num" name="second_num" > second num
<button onclick="myFunction(parseInt(document.getElementById("first_num").value),parseInt(document. getElementById("second_num").value))">calculate</button>
<p id="demo"></p>
function myFunction(first_num,second_num)
{
var x = parseInt(first_num);
var y = parseInt(second_num);
var z = x + y;
var a = z.toString();
document.getElementById("demo").innerHTML = a;
}

How do preserve leading and trailing whitespace when using an input tag?

I am playing with Angular and writing a Regex tester.
Problem is leading whitespace is trimmed when I enter data. See example jsfiddle here:
So when the page loads I have the RegEx "^\d+$".test(" 123 ") which results in "No Match", But if you enter an extra leading or trailing space in the Candidate box:
The leading and trailing spaces are removed from my variable
The result changes "Match"
Here is my HTML:
<div id='ng:app' class='ng-app: myApp' ng-app='myApp'>
<div ng-controller="Controller">{{addTodo2()}}
<form novalidate class="simple-form">Pattern:
<input type="text" ng-model="pattern" />Candidate:
<input type="text" ng-model="candidate" />
<br />.{{candidate}}.
<br>.{{candidate2}}.</form>
</div>
</div>
And here is the associated JavaScript:
function Controller($scope) {
$scope.pattern = "^\\d+$";
$scope.candidate = " 123 ";
$scope.candidate2 = " 123 ";
$scope.addTodo2 = function () {
var str = "Javascript is an interesting scripting language";
var re = new RegExp($scope.pattern, "g");
var result = re.test($scope.candidate);
if (result) {
return "Match22";
} else {
return "No Match22";
};
};
}
var myapp = angular.module('myApp', []);
Updated the fiddle, added ng-trim="false" to the input tags
http://jsfiddle.net/T2zuV/12/
<div id='ng:app' class='ng-app: myApp' ng-app='myApp'>
<div ng-controller="Controller">{{addTodo2()}}
<form novalidate class="simple-form">Pattern:
<input type="text" ng-model="pattern" ng-trim="false"/>Candidate:
<input type="text" ng-model="candidate" ng-trim="false"/>
<br />.{{candidate}}.
<br>.{{candidate2}}.</form>
</div>
</div>