Posting multiple parameters via javascript from view MVC - javascript

I want to save any changes made in the textbox field instantaneously to my database, I have the javascript to pass back the changed value to the controller, however I am not aware how pass the unique id with this to save the changed value against.
Below is my view passing the price back to the script, how can I pass the part id with this?
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.partid, new { id = "partCode"})
</td>
<td>
#Html.DisplayFor(modelIem => item.partdesc)
</td>
<td>
#Html.TextBoxFor(modelItem => item.price, new { id = "priceTextBox", #class = "mytext rightJustified" })
</td>
</tr>
}
</table>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
$(function () {
$('#priceTextBox').change(function () {
var changedText = $(this).val();
var partsJava = $("#partCode").val();
//Post the content of your Textbox to your "YourAction" action in "YourController"
$.post('#Url.Action("EnterNewSpecialPrice2", "SpecialPrices")', { "ChangedText": changedText, "PassPartCode": partsJava }, function (data) { });
});
});
</script>

Define a class to hold both pieces of information that matches the Json object you have created in your script:
public class PartInputModel{
public string ChangedText {get;set;}
public string PassPartCode {get;set;}
}
Note the properties above must match the ones you have defined here:
{ ChangedText: changedText, PassPartCode: partsJava }
Also note you must remove the double quotes around the ChangedText and PassPartCode definition when creating the JSON structure as those are not required.
Change your controller action to receive this object and let the model binding do the work:
[HttpPost]
public ActionResult EnterNewSpecialPrice2(PartInputModel inputModel) {
// inputModel will have your two pieces of data
}
A better mechanism for storing and retrieving the id is to attach it as a data tag to the markup for the Text Box by adding a data-id tag:
#Html.TextBoxFor(m => item.price, new { id = "", #class = "priceTextBox mytext rightJustified", data_id="item.partid" })
Then change your script to read this:
var partsJava = $(this).data('id');
The advantage of this approach is that it is very easy to pass additional parameters by simply expanding your JSON structure and adding a matching property to the PartInputModel class.

You generating invalid html because of duplicate id attributes and selectors such as var partsJava = $("#partCode").val(); will only ever get the value of the first element with id="partCode". In anycase, it would be undefined since DisplayFor() does not generate a element (only text) and it has id attribute or value attribute. Change your loop to
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(m => item.partid)</td>
<td>#Html.DisplayFor(m => item.partdesc)</td>
<td>
#Html.TextBoxFor(m => item.price, new { id = "", #class = "priceTextBox mytext rightJustified", data_id="item.partid" })
</td>
</tr>
}
and then modify the script to
$('.priceTextBox').change(function () {
var changedText = $(this).val();
var partsJava = $(this).data('id');
$.post('#Url.Action("EnterNewSpecialPrice2", "SpecialPrices")', { "ChangedText": changedText, "PassPartCode": partsJava }, function (data) { });
});
});
Note the new { id="" } remove the id attribute so you do not get invalid html.
This of course assumes you have a method
public ActionResult EnterNewSpecialPrice2(string ChangedText, string PassPartCode)
in SpecialPricesController (although PassPartCode may be int?

Related

How to subscribe to items in collection/foreach DOM rendering

I have a view that is used to create and also update an item. I have this problem when updating where a function is running twice due to a change event on a dropdown list. What was suggested in my previous post is that I use a subscription to the value and create logic accordingly within the subscription. I think this is a great idea, however, I have no idea how I will apply these subscriptions to my model when the bindings are being applied dynamically from a view model sent to the view from a back-end controller method.
The collection items get their functionality from data-bind values provided in a foreach element loop and using $data. It would be preferred to keep this format for the solution.
<tbody data-bind="foreach: koModel.ModelData.Lines">
<tr class="form-group">
<td>...</td>
<td>..other model properties..</td>
<td>...</td>
<!-- v v v This is the property with the change event v v v -->
<td>#Html.SearchPickerMvcKOMultipleFor(
m => m.ItemNumber,
"ItemPicker",
new { #class = "form-control" }.AddMore(
"data-bind",
"MyProgramSearchPicker: ItemNumber, value: $data.ItemNumber, event: { change: ItemNumericDetails($data) }"
))</td>
<!-- ^ ^ ^ ^ ^ -->
<td>etc</td>
</tr>
</tbody>
The model is converted to JSON and then to a JavaScript variable:
#{
Newtonsoft.Json.JsonSerializer serializer = new Newtonsoft.Json.JsonSerializer();
string data = Newtonsoft.Json.JsonConvert.SerializeObject(Model);
#Html.Raw("var modelData = " + data + ";");
}
var koModel = new KOModel(modelData);
How can I apply a subscription to the ItemNumber property, either by executing an inline function from the element, or another method that can modify a dynamically rendered collection?
Create a class that represents a "line" of the koModel.ModelData.Lines
function Line(itemNumber) {
var self = this;
self.ItemNumber = ko.observable(itemNumber);
self.ItemNumber.subscribe(...);
};
koModel.ModelData.Lines.push(new Line(123));
Or, you can create a function in your view model:
function koModel() {
...
self.ItemNumericDetails = function (line) {
//do your job
};
};
and in the HTML:
<td>
#Html.SearchPickerMvcKOMultipleFor(
m => m.ItemNumber,
"ItemPicker",
new { #class = "form-control" }.AddMore(
"data-bind",
"MyProgramSearchPicker: ItemNumber, value: $data.ItemNumber, event: { change: $root.ItemNumericDetails }"
))
</td>

For Loop in MVC 4 From JavaScript value

How can I make a repeater type in the page. In the page I have a quantity field:
<td>
#Html.DisplayNameFor(x => x.Quantity)
#Html.ValidationMessageFor(x => x.Quantity)
</td>
<td>
#Html.TextBoxFor(x => x.Quantity, new { #id = "txtQty" })
</td>
When I want to add the item, which there could be several of the same item, just different serial numbers, I need to pop open a div with repeated fields for entering serial numbers:
for (int I = 0; I < *****; I++)
{
<td>Serial Number:</td>
<td>#Html.TextboxFor(x=>x.Quantity, new { #id = "txtQty" + 1})
}
In the JS:
function AddItem() {
Qtys = parseINT($("#txtQty").val());
$("#divSerials").show();
}
How can I do this? Is there a better way?
Is this the way to do it? I try this but 'i' in the HTML model statement is not recognized.
if (parseInt($("#txtQuantity").val()) > 0) {
$("#divSerialNumbers").show();
var html = "<table>";
for (i = 1; i <= serialquantity; i++) {
html += "<tr><td>Serial Number:" + #Html.TextAreaFor(x => x.SerialNumber, new { id = "sns" + i }) + "</td></tr>";
}
html += "</table>";
$("#divSerialNumbers").html(html);
}
Razor code is parsed on the server before it is sent to the view. Javascript is client side code and is not executed until the browser receives the view. This line of code
#Html.TextAreaFor(x => x.SerialNumber, new { id = "sns" + i })
means that on the server you are trying to generate a textarea and set the id attribute to a value that includes a javascript variable which does not yet exist.
Its unclear even what the point of this would be. id attributes serve as selectors in javascript. Whats important is the name and value attributes when it comes to posting your data, and even if it could work, your generating duplicate name attributes which could not bind to you models collection property on post back.
For dynamically generating the html for collections, your name attributes need an indexer, for example <input type="text" name="[0].SerialNumber" />. Options for dynamically creating the html include using the BeginCollectionitem() helper, or a pure client side approach is shown in this answer
If all you are doing is post back an array of strings (the serial numbers) then you could use
var div = $("#divSerialNumbers"); // cache it
$('#Quantity').change(function() { // assumes you remove the pointless 'new { #id = "txtQty" }'
var quantity = parseInt($(this).val()); // use $(this) - don't traverse the DOM all over again
if (!isNan(quantity) && quantity > 0) { // must check for NAN
// note it does not seem necessary to use a table, as opposed to simply adding 4 inputs to the div, but
div.empty(); // clear existing contents
var table = $('</table>');
for (i = 1; i <= quantity; i++) {
var input = $('<input>').attr('name', 'SerialNumber');
var cell = $('</td>').append(input);
var row = $('</tr>').append(cell);
table.append(row);
}
div.append(table).show(); // add the table and display it
}
})
and your controller would need a parameter string[] SerialNumber, for example
public ActionResult Edit(string[] SerialNumber)

Is there any way to call JavaScript in an MVC4 ActionLink for one of the RouteValue parameters?

I have a drop down list (DropDownListFor) and an ActionLink on my page. Basically, the problem I'm having is I'm trying to capture the selected value from my drop down list and passing that into my ActionLink as an ID. Here's my code:
#Html.DropDownListFor(x => x.Capsules, new SelectList(Model.Capsules, "pk", "name", "pk"))
<br />
#Html.ActionLink("Submit", "Create",
new { controller = "Process", id = /*JavaScript here to get the selected ID for the DropDownList above*/ },
new { data_role = "button" })
For what I'm trying to accomplish, is there a way to embed JavaScript into my Html.ActionLink call? If there's not a way, or if it's not recommended, could you please advise of another solution to solve this problem? Thanks in advance!
You can do this via intercepting the link using javascript Darin has posted an example of this.
However, it looks like you're trying to submit some values using an ActionLink, and you're probably better off creating a viewmodel which holds all the values you want, and then posting everything using a submit button. This allows you to post more data than just the ID, prevents you from being dependent on Javascript, and keeps all of the code server side instead of mixing and matching.
Judging by the small code you've posted - you already have a model, probably some strongly typed entity, and it has a property called Capsules.
In your controller, create the view model which holds the view's data:
public class YourViewModel
{
YourModel YourModel { get; set; }
public int CapsuleId { get; set; }
}
Then your view:
#using( #Html.BeginForm( "Create", "Process" ) )
{
#Html.DropDownListFor(m=> m.CapsuleId, new SelectList(Model.YourModel.Capsules, "pk", "name", "pk"))
<input type="submit">
}
Then your controller action to handle this:
[HttpPost]
public ActionResult Create( YourViewModel model )
{
var id = model.CapsuleId;
// do what you're going to do with the id
return View();
}
You can put dummy value for the id parameter like this :
#Html.ActionLink("Submit", "Create",
new { controller = "Process", id = "dummy" },
new { data_role = "button" })
Then replace that value when the link is clicked.
// Assuming your link's id is `submit`, and the dropdown's id is `capsules`
$('#submit').click(function() {
var id = $('capsules').val();
$(this).href = $(this).href.replace('dummy', id);
});

Knockout with MVC3: Knockout Model won't post collection data?

ANSWER:
Replacing this line:
self.identifiers.push(new Identifier(self.identifierToAdd(), self.selectedIdentifierTypeValue()));
With this line:
self.identifiers.push({ Key: self.identifierToAdd(), Value: self.selectedIdentifierTypeValue()});
The post requests is now sending the collection data correctly. However that doesn't solve the fact that the MVC action is not receiving it but this question is big enough already.
I can't seem to get the data from collection property of my model in knockout into my MVC model when posting to an action. If I alert ko.toJSON my identifiers() property from below it properly shows all the data, but when I try and submit that data via a normal postback (the action just takes the EquipmentCreateModel below), it looks like this:
Identifiers is empty and when I look at the ModelState error for Identifiers, it says that it cannot convert String to Dictionary<Guid, string>. What am I doing wrong? I thought MVC3 automatically converts JSON into objects if it can, as it did with the BuildingCode and Room properties?
Also why does my string data in the above picture have escaped quotes?
EDITS:
If I look at the post data, identifiers is shown as an empty array (identifiers: [{}]). I tried jsoning identifiers in the save method like so:
self.identifiers = ko.toJSON(self.identifiers());
This causes the request data to not be empty and look like this:
identifiers:"[{\"Value\":\"sdfsd\",\"Key\":\"4554f477-5a58-4e81-a6b9-7fc24d081def\"}]"
However, the same problem occurs when I debug the action. I also tried jsoning the entire model (as outlined in knockoutjs submit with ko.utils.postJson issue):
ko.utils.postJson($("form")[0], ko.toJSON(self));
But this gives a .NET error that says Operation is not valid due to the current state of the object. Which from looking at the request data it looks like it's being JSON-ified twice because each letter or character is it's own value in the HttpCollection and this is because .NET only allows 1000 max by default ('Operation is not valid due to the current state of the object' error during postback).
Using the $.ajax method to submit the data, everything works fine:
$.ajax({
url: location.href,
type: "POST",
data: ko.toJSON(viewModel),
datatype: "json",
contentType: "application/json charset=utf-8",
success: function (data) { alert("success"); },
error: function (data) { alert("error"); }
});
But due to other reasons I cannot use the $.ajax method for this, so I need it working in the normal post. Why can I toJSON the entire viewModel in the ajax request and it works, but in the normal postback it splits it up, and when I don't, all quotes are escaped in the sent JSON.
Here is my ViewModel:
public class EquipmentCreateModel
{
//used to populate form drop downs
public ICollection<Building> Buildings { get; set; }
public ICollection<IdentifierType> IdentifierTypes { get; set; }
[Required]
[Display(Name = "Building")]
public string BuildingCode { get; set; }
[Required]
public string Room { get; set; }
[Required]
[Range(1, 100, ErrorMessage = "You must add at least one identifier.")]
public int IdentifiersCount { get; set; } //used as a hidden field to validate the list
public string IdentifierValue { get; set; } //used only for knockout viewmodel binding
public IDictionary<Guid, string> Identifiers { get; set; }
}
Then my knock-out script/ViewModel:
<script type="text/javascript">
// Class to represent an identifier
function Identifier(value, identifierType) {
var self = this;
self.Value = ko.observable(value);
self.Key = ko.observable(identifierType);
}
// Overall viewmodel for this screen, along with initial state
function AutoclaveCreateViewModel() {
var self = this;
//MVC properties
self.BuildingCode = ko.observable();
self.room = ko.observable("55");
self.identifiers = ko.observableArray();
self.identiferTypes = #Html.Raw(Json.Encode(Model.IdentifierTypes));
self.identifiersCount = ko.observable();
//ko-only properties
self.selectedIdentifierTypeValue = ko.observable();
self.identifierToAdd = ko.observable("");
//functionality
self.addIdentifier = function() {
if ((self.identifierToAdd() != "") && (self.identifiers.indexOf(self.identifierToAdd()) < 0)) // Prevent blanks and duplicates
{
self.identifiers.push(new Identifier(self.identifierToAdd(), self.selectedIdentifierTypeValue()));
alert(ko.toJSON(self.identifiers()));
}
self.identifierToAdd(""); // Clear the text box
};
self.removeIdentifier = function (identifier) {
self.identifiers.remove(identifier);
alert(JSON.stringify(self.identifiers()));
};
self.save = function(form) {
self.identifiersCount = self.identifiers().length;
ko.utils.postJson($("form")[0], self);
};
}
var viewModel = new EquipmentCreateViewModel();
ko.applyBindings(viewModel);
$.validator.unobtrusive.parse("#equipmentCreation");
$("#equipmentCreation").data("validator").settings.submitHandler = viewModel.save;
View:
#using (Html.BeginForm("Create", "Equipment", FormMethod.Post, new { id="equipmentCreation"}))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Location</legend>
<div class="editor-label">
#Html.LabelFor(model => model.BuildingCode)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.BuildingCode, new SelectList(Model.Buildings, "BuildingCode", "BuildingName", "1091"), "-- Select a Building --", new { data_bind = "value:BuildingCode"})
#Html.ValidationMessageFor(model => model.BuildingCode)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Room)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Room, new { #class = "inline width-7", data_bind="value:room"})
#Html.ValidationMessageFor(model => model.Room)
</div>
</fieldset>
<fieldset>
<legend>Identifiers</legend>
<p>Designate any unique properties for identifying this autoclave.</p>
<div class="editor-field">
Add Identifier
#Html.DropDownList("identifiers-drop-down", new SelectList(Model.IdentifierTypes, "Id", "Name"), new { data_bind = "value:selectedIdentifierTypeValue"})
#Html.TextBox("identifier-value", null, new { #class = "inline width-15", data_bind = "value:identifierToAdd, valueUpdate: 'afterkeydown'" })
<button type="submit" class="add-button" data-bind="enable: identifierToAdd().length > 0, click: addIdentifier">Add</button>
</div>
<div class="editor-field">
<table>
<thead>
<tr>
<th>Identifier Type</th>
<th>Value</th>
<th></th>
</tr>
</thead>
<!-- ko if: identifiers().length > 0 -->
<tbody data-bind="foreach: identifiers">
<tr>
<td>
<select data-bind="options: $root.identiferTypes,
optionsText: 'Name', optionsValue: 'Id', value: Key">
</select>
</td>
<td><input type="text" data-bind="value: Value"/></td>
<td>Remove</td>
</tr>
</tbody>
<!-- /ko -->
<!-- ko if: identifiers().length < 1 -->
<tbody>
<tr>
<td colspan="3"> No identifiers added.</td>
</tr>
</tbody>
<!-- /ko -->
</table>
#Html.HiddenFor(x => x.IdentifiersCount, new { data_bind = "value:identifiers().length" })<span data-bind="text:identifiers"></span>
#Html.ValidationMessageFor(x => x.IdentifiersCount)
</div>
</fieldset>
<p>
<input type="submit" value="Create" />
</p>
}
I think I've found the issue or at least narrowed down the problem. The editable grid example uses simple js objects to represent gifts. You are using Identifier objects with sub observables. It seems that if we update the grid example to use more complex types it too breaks in the same way as your example. This is either by design or a bug.
http://jsfiddle.net/SZzVW/9/
I think the only solution is to write your own mapping function to submit the form.
Hope this helps.

Dynamic created checkbox using foreach in asp.net mvc

I have list page to show all images with its name from database in asp.net mvc list action (PhotoList - get).. in that view page (PhotoList.aspx), I have created checkbox to delete multiple rows. I want scenario like following
First page shows the list with in first column checkbox and in second column PhotoName and on the down page one button for delete selected rows .
when selects checkboxes and clicks the delete button, according to selection the rows will be deleted from database and return the same list page..
I don't understand where to write code for delete and how?
<% foreach (var item in Model) { %>
<tr>
<td>
<input type="checkbox" name="deleteImage" value="<%= item.PhotoId %>"/>
</td>
<td>
<%= Html.ActionLink("Edit", "Edit", new { id=item.PhotoId }) %>
</td>
<td>
<%= Html.Encode(item.PhotoName) %>
</td>
</tr>
<% } %>
<input type="button" name="Delete" value="Delete Selected items"/>
The Code for delete will be written in the HttpPost action for delete. Something like below should work if you are using myModel
[HttpPost]
public ActionResult Delete(myModel deleteEntries) //This is the post-version of your Action that rendered the view..If it's Edit, then change the name to Edit
{
var deleteList = db.deleteEntries.where(d => d.checkBox == true).ToList();
foreach (myList my in deleteList)
{
db.myList.Remove(my); // remember db should be your DbContext instace
}
db.SaveChanges();
}
UPDATE
You will first need to make a ViewModel because otherwise you cannot recognize which entries are checked for deletion with the help of checkbox.
Make a ViewMode class like following
using pratice3.Models;
public class MyPhotoViewModel
{
public UserManagementDbEntities.tblPhoto TablePhoto { get; set; }
public bool checkBox { get; set; }
}
Return this to your view
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult PhotosList()
{
var viewModel = _datamodel.tblPhoto.Select(g => new MyPhotoViewModel
{
TablePhoto = g;
checkBox = false;
}).ToList();
return View(viewModel);
}
In View, change the using statement to reflect IEnumerable<MyPhotoViewModel> and make appropriate changes accordingly.
Then, define your Post Action like following
[HttpPost]
public ActionResult PhotosList(IEnumerable<MyPhotoViewModel> myPhotoList)
{
var deleteList = myPhotoList.where(d => d.checkBox == true).ToList();
foreach (var deletePhoto in deleteList)
{
_datamodel.tblPhoto.DeleteObject(deletePhoto.TablePhoto);
}
db.SaveChanges();
}
Using jQuery you can do this.
On button click get all the Ids of photos, something like this
var selected = new Array();
$('name="deleteImage" input:checked').each(function () {
selected.push($(this).attr('id')));
});
var selectedIds = selected.join(',');
Now on button click, make ajax call to some function on server side which will accept these ids and will delete from DB or so.
$.ajax({
url: '#Url.Action("DeleteRecord", "UserManage")',
data: 'ids=' + selectedIds + '&time=' + new Date(), //Date makes each call unique
success: function (data) {
//You can reload page
},
error: function (data) {
//You can show error
}
});
Hope this helps.

Categories