By some reason action does not want to be performed on this textbox when variable is used for name:
var formQty = new String("QTY." + productName).toString();
var total = document.MainForm.elements["formQty"].value;
document.MainForm.elements["formQty"].value = sum;
This is HTML:
input type="text" name="QTY.1-DAY-ACUVUE"
size="3" maxlength="8" onkeydown="javascript:QtyEnabledAddToCart();" value="1"
I only have name of texbox, not Id. var total does not return value when var formQty is used. If I hardcode the name instead of using variable var formQty, operation works. Only with variable it does not. I compared my varaible value with actual name and they are equal. Used both lengh comparison and == comparison. Both returned true.
Is there another way of defining var total?
These are quite popular
document.getElementsByName("element-name");
document.getElementById("element-id");
You can also use indices
document.forms[0].elements[0];
document.forms[0].elements[1];
Related
I have an input text field, and I want to be able to restore the initial value after the user edits the text.
So, I would say to add a data-{something} attribute, for instance data-init, that will hold the initial value, so it can be restored later.
Result should look like:
<input type="text" val="Some value..." data-init="Some value..." />
Now, I know I can achieve this by using:
#Html.TextBoxFor(m => m.InputText , new { data_init = Model.InputText })
But it's awful, and I have a lot of input fields with this behavior.
Also, I can't create a custom HtmlHelper because I have many input types with this behavior, it will be quite messy if I go this way...
So, what I think should be the practical solution is to use Data Annotations.
public class ExampleVM
{
[HoldInitialValue]
public string InputText { get; set; }
}
The [HoldInitialValue] attribute will be responsible to add the data-init="{value}" to the input tag. The {value} will be taken from the property's value, with also an option to override it.
So, how do I implement this behavior?
Thanks for the helpers.
Even if you were to create a custom attribute, which would need to implement MetadataAware in order to add the value to the AdditionalValues property of ModelMetadata, you still then need to create your own extension methods to read that value and add it to the htmlAttributes.
For an example of how to implement it, refer CustomAttribute reflects html attribute MVC5.
However, that is unnecessary since HTML inputs already store the initial value. For a <input> (other than a checkbox) or <textarea> its defaultValue. For a <input type="checkbox" /> its defaultChecked and for <select> its defaultSelected.
So in the case of your #Html.TextBoxFor(m => m.InputText), your could use for example
using javascript
var element = document.getElementById ("InputText");
var initialValue = elem.defaultValue;
// reset to initial value
element.value = initialValue;
or using jQuery
var element = $('#InputText');
var initialValue = element.prop('defaultValue');
// reset to initial value
element.val(initialValue);
try this one , using htmlAttributes as IDictionary
#Html.TextBoxFor(m => m.InputText , new Dictionary<string,object>()
{
{ "data-init", Model.InputText}
})
My model contains an array of zip code items (IEnumerable<SelectListItem>).
It also contains an array of selected zip codes (string[]).
In my HTML page, I want to render each selected zip code as a drop down with all the zip code options. My first attempt did not work:
#foreach (var zip in Model.ZipCodes) {
Html.DropDownList( "ZipCodes", Model.ZipCodeOptions )
}
I realized that although that would produce drop downs with the right "name" attribute, it wouldn't know which element of ZipCodes holds the value for that particular box, and might just default to the first one.
My second attempt is what really surprised me. I explicitly set the proper SelectListItem's Selected property to true, and it still rendered a control with nothing selected:
#foreach (var zip in Model.ZipCodes) {
Html.DropDownList( "ZipCodes", Model.ZipCodeOptions.Select( x => (x.Value == zip) ? new SelectListItem() { Value = x.Value, Text = x.Text, Selected = true } : x ) )
}
There, it's returning a new IEnumerable<SelectListitem> that contains all the original items, unless it's the selected item, in which case that element is a new SelectListItem with it's Selected property set to true. That property is not honored at all in the final output.
My last attempt was to try to use an explicit index on the string element I wanted to use as the value:
#{int zipCodeIndex = 0;}
#foreach (var zip in Model.ZipCodes) {
Html.DropDownList( "ZipCodes[" + (zipCodeIndex++) + "]", Model.ZipCodeOptions )
}
That doesn't work either, and probably because the name is no longer "ZipCodes", but "ZipCodes[x]". I also received some kind of read-only-collection error at first and had to change the type of the ZipCodes property from string[] to List<string>.
In a forth attempt, I tried the following:
#for (int zipCodeIndex = 0; zipCodeIndex < Model.ZipCodes.Count; zipCodeIndex++)
{
var zip = Model.ZipCodes[zipCodeIndex];
Html.DropDownListFor( x => x.ZipCodes[zipCodeIndex], Model.ZipCodeOptions )
}
That produces controls with id like "ZipCodes_1_" and names like "ZipCodes[1]", but does not select the right values. If I explicitly set the Selected property of the right item, then this works:
#for (int zipCodeIndex = 0; zipCodeIndex < Model.ZipCodes.Count; zipCodeIndex++)
{
var zip = Model.ZipCodes[zipCodeIndex];
Html.DropDownListFor( x => x.ZipCodes[zipCodeIndex], Model.ZipCodeOptions.Select( x => (x.Value == zip) ? new SelectListItem() { Value = x.Value, Text = x.Text, Selected = true } : x ) )
}
However, the problem with that approach is that if I add a new drop downs in JavaScript and give them all the name "ZipCodes", then those completely override all the explicitly indexed ones, which never make it to the server. It doesn't seem to like mixing the plain "ZipCodes" name with explicit array elements "ZipCodes[1]", even though they map to the same variable when either is used exclusively.
In the U.I., user's can click a button to add a new drop down and pick another zip code. They're all named ZipCodes, so they all get posted to the ZipCodes array. When rendering the fields in the loop above, I expect it to read the value of the property at the given index, but that doesn't work. I've even tried remapping the SelectListItems so that the proper option's "Selected" property is true, but it still renders the control with nothing selected. What is going wrong?
The reason you first 2 snippets do not work is that ZipCodes is a property in your model, and its the value of your property which determines what is selected (not setting the selected value in the SelectList constructor which is ignored). Since the value of ZipCodes is an array of values, not a single value that matches one of the option values, a match is not found and therefore the first option is selected (because something has to be). Note that internally, the helper method generates a new IEnumerable<SelectListItem> based on the one you provided, and sets the selected attribute based on the model value.
The reason you 3rd and 4th snippets do not work, is due to a known limitation of using the DropDownListFor() method, and to make it work, you need to use an EditorTemplate and pass the SelectList to the template using AdditionalViewData, or construct a new SelectList in each iteration of the loop (as per your last attempt). Note that all it needs to be is
for(int i = 0; i < Model.ZipCodes.Length; i++)
{
#Html.DropDownListFor(m => m.ZipCodes[i],
new SelectList(Model.ZipCodeOptions, "Value", "Text", Model.ZipCodes[i]))
}
If you want to use just a common name (without indexers) for each <select> element using the DropDownList() method, then it needs to be a name which does not match a model property, for example
foreach(var item in Model.ZipCodes)
{
#Html.DropDownList("SelectedZipCodes",
new SelectList(Model.ZipCodeOptions, "Value", "Text", item))
}
and then add an additional parameter string[] SelectedZipCodes in you POST method to bind the values.
Alternatively, use the for loop and DropDownListFor() method as above, but include a hidden input for the indexer which allows non-zero based, non consecutive collection items to be submitted to the controller and modify you script to add new items using the technique shown in this answer
Note an example of using the EditorTemplate with AdditionalViewData is shown in this answer
Running into an error during import for entities extended with a ko.observableArray() property, vs. being extended as a simple array [] type in the constructor.
var customerCtor = function () {
this.extendedProp = ko.observable(true);
//this.extendedArray = ko.observableArray(); // causes error: Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.
this.extendedArray = []; // this works just fine
};
I created a test along-side the Breeze v1.3.6 DocCode: exportImportTests.js "stash entire cache locally and restore" as my starting point, and here is the new test:
test("w/extended Customer, stash entire cache locally and restore", 3, function () {
var em1 = newEm();
var store = em1.metadataStore;
// extend Customer with observables
var customerCtor = function () {
this.extendedProp = ko.observable(true);
this.extendedArray = ko.observableArray(); // causes error: Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.
//this.extendedArray = []; // but this will work just fine?
};
store.registerEntityTypeCtor("Customer", customerCtor);
var expected = testData.primeTheCache(em1);
// grab first Customer, push value onto extendedArray prop
var custEntity = em1.getEntities(expected.customerType)[0];
custEntity.extendedArray().push('some-value'); // even when defined as [], Breeze re-writes field as ko.observable
var exportData = em1.exportEntities();
var stashName = "stash_everything";
window.localStorage.setItem(stashName, exportData);
var importData = window.localStorage.getItem(stashName);
var em2 = new EntityManager(); // virginal - so register ctor on this instance
var store2 = em2.metadataStore;
store2.registerEntityTypeCtor("Customer", customerCtor);
em2.importEntities(importData);
var entitiesInCache = em2.getEntities();
var restoreCount = entitiesInCache.length;
equal(restoreCount, expected.entityCount,
"should have restored expected number of all entities");
var restoredCustomer = em2.getEntities(expected.customerType)[0];
ok(restoredCustomer.extendedProp(), 'extended property present');
ok(restoredCustomer.extendedArray().length > 0, 'extended Array present and has data');
});
An em2.importEntities(importData); throws the error:
Error: Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.
at Error (<anonymous>)
at h [as extendedArray] (http://localhost:47595/Scripts/knockout-2.2.1.js:44:167)
at ctor.initializeEntityPrototype.proto.setProperty (http://localhost:47595/Scripts/breeze.debug.js:14634:31)
at updateTargetPropertyFromRaw (http://localhost:47595/Scripts/breeze.debug.js:13062:24)
at aspectName (http://localhost:47595/Scripts/breeze.debug.js:13025:13)
at Array.forEach (native)
at updateTargetFromRaw (http://localhost:47595/Scripts/breeze.debug.js:13023:19)
at em._inKeyFixup (http://localhost:47595/Scripts/breeze.debug.js:12601:17)
at Array.forEach (native)
at importEntityGroup (http://localhost:47595/Scripts/breeze.debug.js:12568:28)
As Breeze always rewrites constructor fields (in my case for KO), defining as [] works. But not sure why this would happen when the property is pre-defined?
Anyone run into this, or have I missed a doc note somewhere?
We'll look at it.
Yes, Breeze assumes every property added in a constructor is supposed to be rewritten per the prevailing "model library" which, in your case, is KO. Therefore, no surprise that the array becomes a ko.observableArray.
Moreover, because such a property is presumed to be under Breeze's jurisdiction, we have to tie it into Breeze observability and serialization mechanisms which means we re-write it as a Breeze-flavored observable array. Such an array is a computed.
Evidently there is some problem with the way we're doing this for a property which is "unmapped". We'll look at that.
N.B.: I am assuming (and your code confirms) that the array property, extendedArray, is an "unmapped property" in the Breeze sense. That should be fine.
You should not mention mapped collection navigation properties in your constructor. There is no valid reason to do so that I can think of. The main reason to mention a mapped property in the constructor is (a) to give it a default value or (b) make it available to a custom (unmapped) computed property. There is no reasonable alternative default value for a collection navigation property (empty is the default) and it would be rare/avoidable to include it in a computed.
I want to display a previous value on Min Miles and that should not be editable. I want like
Default value of Min Miles is 0.
When I click on Add More Range then In the new form - Min Value should be Max Value of Previous Form.
I am using semantic form for. Please Help Me. How can I do this...
Regarding your second question, and assuming that the new form appears through javascript, without page reloading, you can grab the
field value with javascript and use it as the default value for the
new field. The "add new range"
Something Like
function getvalue(){
var inputTypes_max = [],inputTypes_min = [],inputTypes_amount = [];
$('input[id$="max_miles"]').each(function(){
inputTypes_max.push($(this).prop('value'));
});
$('input[id$="amount"]').each(function(){
inputTypes_amount.push($(this).prop('value'));
});
var max_value_of_last_partition = inputTypes_max[inputTypes_max.length - 2]
var amount_of_last_partition = inputTypes_amount[inputTypes_amount.length - 2]
if (max_value_of_last_partition == "" || amount_of_last_partition == "" ){
alert("Please Fill Above Details First");
}else{
$("#add_more_range_link").click();
$('input[id$="min_miles"]').each(function(){
inputTypes_min.push($(this).prop('id'));
});
var min_id_of_last_partition=inputTypes_min[inputTypes_min.length - 2]
$("#"+min_id_of_last_partition).attr("disabled", true);
$("#"+min_id_of_last_partition).val(parseInt(max_value_of_last_partition) + 1)
}
}
I have Used Jquery's End Selector In a loop to get all value of max and amount field as per your form and get the ids of your min_miles field and then setting that value of your min_miles as per max_miles
It worked For me hope It works For You.
Default value of a field can just be passed in the form builder as a second parameter:
...
f.input :min_miles, "My default value"
Of course I do not know your model structure but you get the idea.
Regarding your second question, and assuming that the new form appears through javascript, without page reloading, you can grab the field value with javascript and use it as the default value for the new field. The "add new range" click will be the triggerer for the value capture.
Something like (with jQuery):
var temp_value = '';
$('#add_more_range').click(function(){
temp_value = $('#my_form1 #min_miles').value();
$('#my_form2 #max_miles').value(temp_value);
});
Again I am just guessing the name of the selectors, but the overall approach should work.
If you are also adding dinamically to the page the "Add new range" buttons/links, then you should delegate the function in order to be inherited also for the so new added buttons:
$('body').on('click', '#add_more_range', function(){...});
All I want to do is
Get the name-value pairs that were supplied to Request.QueryString
Populate a javascript object (aka hash) with keys from the names and values from the values
Halt the page if one of the expected hash values is the empty string
The Request.QueryString object is reminding me why I hated classic asp even before it was an abandoned technology. :/
The Request.QueryString collection has an awkward interface, particularly when it comes to iterating or cases where there are multiple params with the same name. I suggest grabbing the whole querystring using Request.QueryString.Item() or Request.ServerVariables('QUERY_STRING') and parse it using unescape/decodeURIComponent. It's a bit of effort, but gives you more control and consistency.
A simple example that lowercases keys:
var params = parseQueryString(Request.QueryString.Item());
function parseQueryString(qs) {
var parsed = {}, pairs = qs.split('&');
for (var i = 0; i < pairs.length; i ++) {
var pair = pairs[i], pos = pair.indexOf('=');
if (pos < 0) pos = pair.length;
parsed[unescape(pair.slice(0, pos)).toLowerCase()] = unescape(pair.slice(pos + 1));
}
return parsed;
}
Querystring contents are treated as string by default i believe..
But if you have to, you can always do String( request.querystring("foo") );
Are the keys known before-hand ? or you want to iterate through the pairs and retrieve both key and value ?