knockout js valueupdate 'afterkeydown' with event change not working - javascript

I'm using Knockout JS on a form that's used to calculate pricing for a product.
I have a function that is called on form submit and calculates pricing. For the text input fields, I'd like for the pricing function to be called as soon as user starts changing values. At the moment this only works if I click outside of the text input field, so not immediately on keypress/keyup, which is what I'd like to achieve.
I'm pretty sure it's something in my code that needs fixing.
HTML for the input field:
<div class="form-group">
<label>Number of Pages</label>
<input data-bind="value : pages, valueUpdate: ['afterkeydown'], event: { change: onSubmit }" >
</div>
Javascript:
self.pages = ko.observable("10");
self.onSubmit = function () {
self.response("<div class='priceLoading'>Loading</div>");
var servURL = "http://prices.mysite.com/getpricing/"
+ ko.utils.unwrapObservable(self.selectedCategoryValue) + "/"
+ self.pages() + "/"
+ self.copies()
$.get(servURL, function (response) {
self.response(response.PriceFormatted);
})
}
The onSubmit function that is called on the event change grabs data xml data from a service and writes it on the form.
Any help with this greatly appreciated!

You can use
<input data-bind="textInput : pages, event: { keyup: onSubmit }" >

It makes a little more sense, if you want to submit on every change to your variable, to subscribe your onsubmit function to the variable rather than having a separate event.
var self = {};
self.pages = ko.observable("10");
self.response = ko.observable();
self.copies = ko.observable("5");
self.pages.subscribe(function(newValue) {
self.response("<div class='priceLoading'>Loading</div>");
var servURL = "http://prices.mysite.com/getpricing/" + ko.utils.unwrapObservable(self.selectedCategoryValue) + "/" + self.pages() + "/" + self.copies()
setTimeout(function() {
self.response("Fetched for " + newValue);
}, 500);
});
ko.applyBindings(self);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class="form-group">
<label>Number of Pages</label>
<input data-bind="value : pages, valueUpdate:'afterkeydown'">
<div data-bind="text:response"></div>
</div>

Related

User input to build URL

I have a bit of experience with HTML but am very new to JavaScript. In essence, I would like for a user input to be part of a URL. For example, we could have something simple such as:
<script>
function get_cityname() {
var cityname = document.getElementById("cn").value;
alert(cityname);
}
</script>
<form>
Enter city name:
<input type = "text" size = "12" id = "cn">
<input type = "submit" onclick = "get_cityname();">
</form>
This will create a textbox where a user inputs their text (city name) and then click the 'submit' button next to it, and an alert should pop up based on the information they provided, just to make sure this works. However, this code only would seem to work (because of the 'onclick' command) to work for one user input. Therefore, I have 2 questions:
How could the above variable be included in a URL string? If it were something simple as:
URLstring = "https://sampleurl" + cityname + "moretext.html"
How could this be expanded if I want to include two or possibly even n number of inputs? For example, if I create more user prompt boxes and want to have the user also be able to input their zipcode, or state abbreviation, for example:
URLstring = "https://sampleurl" + cityname + "moretext" + zipcode + "moretext" + "stateabbreviation.html"
You could do something along these lines (it would be the same for one or more fields):
// Ensures the DOM (html) is loaded before trying to use the elements
window.addEventListener('DOMContentLoaded', function() {
var cnInput = document.getElementById("cn"),
zipInput = document.getElementById("zip"),
form = document.getElementById("myForm");
form.addEventListener('submit', getUrl); // On submit btn click, or pressing Enter
function getUrl(e) {
var cityname = cnInput.value,
zipcode = zipInput.value,
url = "https://sample.com/" + cityname + "/" + zipcode + ".html";
alert(url);
e.preventDefault(); // Prevent the form from redirecting?
}
});
<form id="myForm">
<label>Enter city name: <input type="text" size="12" id="cn"></label>
<label>Enter zip code: <input type="text" size="12" id="zip"></label>
<input type="submit">
</form>
First specify an action attribute for your form. This is where your form will be submitted. Then set your form's method attribute to GET. Finally, add as many fields as you like (I am assuming you are after a GET query such as https:url.com?key1=val1&key2=val2...):
<form method="GET" action="https://sampleurl">
Enter city name:
<input type="text" size="12" id="cn">
Enter zip code:
<input type="text" pattern="[0-9]{5}"
<input type="submit" ">
</form>

How to make one input type disable in HTML

I have a webapp, that implements CRUD functionality. The goal I'm trying to reach is to make Add editRow different from Edit. Here what I mean. When I invoke the Add function it makes a user capable(even mandatory) to provide an ID, whereas Edit disables a user of providing ID(or it would be saving a new entity, not updating). The simplest way to do that is to add "disable" for the input type. I've tried this:
<div class="form-group">
<label for="userId" class="control-label col-xs-3">ID</label>
<div class="col-xs-9">
<input type="number" class="form-control" id="userId" name="userId"
<c:choose>
<c:when test="${user.userId== null}"> placeholder="ID"
</c:when>
<c:otherwise>
placeholder = "${user.userId}" disabled </c:otherwise>
</c:choose>>
</div>
</div>
But this doesn't work.
My js scripts:
function add() {
form.find(":input").val("");
$("#editRow").modal();
}
function updateRow(id) {
$.get(ajaxUrl + id, function (data) {
$.each(data, function (key, value) {
form.find("input[name='" + key + "']").val(value);
});
$('#editRow').modal();
});
}
I don't find cool creating 2 editRow's with 1 difference
You just need to add the disabled property to the input you want to disable. The easiest way is to select that input outside of the each cycle and assign the property.
form.find("input[name='ID']").prop("disabled", true);
If you want to keep it all inside the cycle you can do something like this:
function updateRow(id) {
$.get(ajaxUrl + id, function (data) {
$.each(data, function (key, value) {
var input = form.find("input[name='" + key + "']");
input.val(value);
if(key=="ID")
input.prop("disabled", true);
});
$('#editRow').modal();
});
}
Change ID for the actual name of your input

DataPicker not getting binded to textbox ? fiddle provided

Well in other cases i will get datepicker binded to my textbox which will be straight forward but not in this case .
Fiddle link : http://jsfiddle.net/JL26Z/1/ .. while to setup perfect seanrio i tried but unable to bind datepicker to textboxes . except that everything is in place
My code :
**<script id="Customisation" type="text/html">** // here i need to have text/html
<table style="width:1100px;height:40px;" align="center" >
<tr>
<input style="width:125px;height:auto;" class="txtBoxEffectiveDate" type="text" id="txtEffective" data-bind="" />
</tr>
</script>
The above code is used for my dynamic generation of same thing n no of time when i click each time on a button . So above thing is a TEMPLATE sort of thing .
My knockout code :
<div data-bind="template:{name:'Customisation', foreach:CustomisationList},visible:isVisible"></div>
<button data-bind="click:$root.CustomisatioAdd" >add </button>
I tried same old way to bind it with datepicker
$('#txtEffective').datepicker(); // in document.ready i placed
Actually to test this i created a textbox with some id outside script with text/html and binded datepicker to it and It is working fine sadly its not working for the textbox inside text/html and i want to work at any cost.
PS: well i haven't posted my view model as it is not required in this issue based senario
View model added with Js
var paymentsModel = function ()
{
function Customisation()
{
var self = this;
}
var self = this;
self.isVisible = ko.observable(false);
self.CustomisationList = ko.observableArray([new Customisation()]);
self.CustomisationRemove = function () {
self.CustomisationList.remove(this);
};
self.CustomisatioAdd = function () {
if (self.isVisible() === false)
{
self.isVisible(true);
}
else
{
self.CustomisationList.push(new Customisation());
}
};
}
$(document).ready(function()
{
$('#txtEffective').datepicker();
ko.applyBindings(new paymentsModel());
});
Any possible work around is appreciated
Regards
The best way I've found to do this is create a simple bindingHandler.
This is adapted from code I have locally, you may need to tweak it...
** code removed, see below **
Then update your template:
** code removed, see below **
By using a bindingHandler you don't need to try to hook this up later, it's done by knockout when it databinds.
Hope this is helpful.
EDIT
I created a fiddle, because I did indeed need to tweak the date picker binding quite a lot. Here's a link to the Fiddle, and here's the code with some notes. First up, the HTML:
<form id="employeeForm" name="employeeForm" method="POST">
<script id="PhoneTemplate" type="text/html">
<div>
<span>
<label>Country Code:</label>
<input type="text" data-bind="value: countryCode" />
</span>
<span><br/>
<label>Date:</label>
<input type="text" data-bind="datepicker: date" />
</span>
<span>
<label>Phone Number:</label>
<input type="text" data-bind="value: phoneNumber" />
</span>
<input type="button" value="Remove" data-bind="click: $parent.remove" />
</div>
</script>
<div>
<h2>Employee Phone Number</h2>
<div data-bind="template:{name:'PhoneTemplate', foreach:PhoneList}">
</div>
<div>
<input type="button" value="Add Another" data-bind="click: add" />
</div>
</div>
</form>
Note I removed the id=... from in your template; because your template repeats per phone number, and ids must be unique to be meaningful. Also, I removed the datepicker: binding from the country code and phone number elements, and added it only to the date field. Also - the syntax changed to "datepicker: ". If you need to specify date picker options, you would do it like this:
<input type="text" data-bind="datepicker: myObservable, datepickerOptions: { optionName: optionValue }" />
Where optionName and optionValue would come from the jQueryUI documentation for datepicker.
Now for the code and some notes:
// Adapted from this answer:
// https://stackoverflow.com/a/6613255/1634810
ko.bindingHandlers.datepicker = {
init: function(element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {},
observable = valueAccessor(),
$el = $(element);
// Adapted from this answer:
// https://stackoverflow.com/a/8147201/1634810
options.onSelect = function () {
if (ko.isObservable(observable)) {
observable($el.datepicker('getDate'));
}
};
$el.datepicker(options);
// set the initial value
var value = ko.unwrap(valueAccessor());
if (value) {
$el.datepicker("setDate", value);
}
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$el.datepicker("destroy");
});
},
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
$el = $(element);
//handle date data coming via json from Microsoft
if (String(value).indexOf('/Date(') === 0) {
value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
}
var current = $el.datepicker("getDate");
if (value - current !== 0) {
$el.datepicker("setDate", value);
}
}
};
function Phone() {
var self = this;
self.countryCode = ko.observable('');
self.date = ko.observable('');
self.phoneNumber = ko.observable('');
}
function PhoneViewModel() {
var self = this;
self.PhoneList = ko.observableArray([new Phone()]);
self.remove = function () {
self.PhoneList.remove(this);
};
self.add = function () {
self.PhoneList.push(new Phone());
};
}
var phoneModel = new PhoneViewModel();
ko.applyBindings(phoneModel);
Note the very updated binding handler which was adapted from this answer for the binding, and this answer for handling onSelect.
I also included countryCode, date, and phoneNumber observables inside your Phone() object, and turned your model into a global variable phoneModel. From a debugger window (F12 in Chrome) you can type something like:
phoneModel.PhoneList()[0].date()
This will show you the current value of the date.
I notice that your form is set up to post somewhere. I would recommend instead that you add a click handler to a "Submit" button and post the values from your phoneModel using ajax.
Hope this edit helps.
Dynamic entities need to have datepicker applied after they are created. To do this I'd use an on-click function somewhere along the lines of
HTML
<!-- Note the id added here -->
<button data-bind="click:$root.CustomisatioAdd" id="addForm" >add </button>
<script>
$(document).on('click', '#addForm', function(){
$('[id$="txtEffective"]').datepicker();
});
</script>

Trying to make a pricing list with jQuery in JavaScript

I've typed the whole calculation. I have a submission button which by clicking needs to retrieve the sum. It doesn't work. I'm pretty new at JavaScript so I can't really tell where is the problem. Here is the code:
$('#home_price').submit(function(){
var shutter_price, lights_price, socket_price, screen10_price, screen7_price, dimmer_price, x
var total_price = 3000;
shutter_price = ($('#shutter').val())*200;
light_price = ($('#lights').val())*200;
socket_price = ($('#socket').val())*200;
screen10_price = ($('#screen10').val())*700;
screen7_price = ($('#screen7').val())*200;
dimmer_price = ($('#dimmer').val())*400;
if($('#boiler').is(":checked")==true){
total_price+=600;
x+=1;
}
x+=($('#shutter').val())*2+($('#lights').val())+($('#socket').val());
Math.floor(x);
x+=1;
total_price = total_price + shutter_price + light_price + socket_price + screen10_price + screen7_price + dimmer_price + x*400;
$('#home_pricing').val()=total_price;
if($('#home_pricing').val() < 6000)
alert('the solution invalid');
else
alert(" total: " + $('#home_pricing').val());
});
});
and a piece of the html code:
<label for="screen7"> 7inch screen </label>
<input style="margin-right:70px" name="screen7" type="number" id="screen7"> <br><br>
<label for="dimmer"> dimmer</label>
<input style="margin-right:174px" name="dimmer" type="number" id="dimmer"> <br><br>
<label for="boiler"> bolier </label>
<input style="margin-right:148px" type="checkbox" name="boiler" id="boiler" > <br><br>
<div>
<input type="submit" name=" home_pricing " id="home_pricing" value=" calculate " >
</div>
</form>
I tried doing the same with document.getelementbyid(' one_of the id's').value
but still once I pick values in each input line and then click the submission button it just jumps to the window again with no values and doesn't print anything.
Change the submit button's type from 'submit' to 'button'. That way, clicking submit won't redirect you. What you have to do to set the values though, is make an onclick event for the new button.
Instead of using $('#home_price').submit(), use calculate() and it will work.
The default behavior when a form is submitted to is to to go to a new page. If no new page is specified, the current page is just reloaded. You can prevent that default behavior in the submit event handler method by invoking the event's preventDefault method. To do that, specify an event parameter to the submit method definition, then call preventDefault, like so:
$('#home_price').submit(function(event) {
event.preventDefault();
// Rest of your code here
// ...
Update
There are also a couple of obvious errors in your code. The Math.floor method does not modify the variable passed to it, it returns a integer value. Change this line:
Math.floor(x);
to this
x = Math.floor(x);
Putting the jQuery val() method on the left side of an assignment does do anything. However, you can pass a value to val() and it will set the new value. Change this line:
$('#home_pricing').val()=total_price;
to this:
$('#home_pricing').val(total_price);

Javascript: convert form to static

I'm working on an online application that uses a lot of forms like this:
<form action="..." id=".." method="post">
<label for="i0">something</label>
<input type="text" id="i0" .... />
<label for="i1">something</label>
<select id="i1"..> <option>a</option> <option>b</option> </select>
.....
<input type="submit" ...>
</form>
I use some JQuery plugins to serialize and deserialize data so I can save all data to database in one JQuery line and fill form with another instruction. My problem now is that in some context I need to show only the data, not editable form.
The question is: is there any JQuery plugin or some code that converts a <form> into a textual data preserving the form structure?
Note: a simple option is to show the form and disable all form fields, but this is not a good option if the user wants to print the data.
var html = '';
var $form = $('#form');
$form.find('label').each(function() {
var $label = $(this);
html += '<p>' + $label.text() + ': ' + $('#' + $label.attr('for')).val() + '</p>';
})
$form.replaceWith('<div>' + html + '</div>');
If you're doing that on submit, you could use jQuery form plugin and the formSerialize() function or beforeSubmit() callback.
var queryString = $('#myFormId').formSerialize();
or
$("#myFormId").ajaxForm({
beforeSubmit: function(arr, $form, options) {
// The array of form data takes the following form:
// [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
// return false to cancel submit
return true;
},
success: function() {
//success (your actions here)
}
});
(Use $form).
Here is non-jquery crossbrowser workaround:
var getOuterHtml = function(node){
var wrapper = document.createElement('div');
document.body.appendChild(wrapper);
var clone = node.cloneNode(true);
wrapper.appendChild(clone);
var result = wrapper.innerHTML;
document.body.removeChild(wrapper);
return result;
};
Here is working example.
P.S. By the way in ie you can use node.outerHTML.
EDIT: little bit modified example so that it wouldn't remove original form
I would propose a little different, but seems more appopriate solution - jQuery Templates
You keep you common code as template, depending on actual needs, you wrap those templates either to <forms> or <div>. This is more clear and easy to support.

Categories