I have some strings and I would like to get the Index Number out from them.
Here is the example.
var x = "FundList[10].Amount";
var y = "FundList[11].Amount";
var z = "FundList[15].Amount";
I simply want to get the 10,11,15 and put them into an array of Int.
Currently, I am using a stupid way, which is to replace "FundList[", "]" and ".Amout" with "".
I am wondering if there is a better way to do so.
Update
Here are some clarifications. Here is my code.
This is my part of my PartialView.
#{
var txtIndexName = "FundList[" + Model.Index + "].Index";
var txtAmountName = "FundList[" + Model.Index + "].Amount";
var dropFiscalYearName = "FundList[" + Model.Index + "].FiscalYear";
}
Amount: <input type="text" name="#txtAmountName" id="#txtAmountName" />
Here is the JavaScript to call the PartialView. Each time when user click on a anchor link, the PartialView will be called.
function LoadContractOptionFundingPlanBlockByIndexNumber(indexNumber) {
$.ajax({
type: "POST",
url: "/Json/LoadContractOptionFundingPlanPartialView",
data: JSON.stringify({ index: indexNumber }),
contentType: "application/json; charset=utf-8"
}).success(function (result) {
$("#ContractOptionFundingPlanBlock").append(result);
});
}
function GenerateOptionFundingPlanBlock() {
$("#lnkAddFundingBlock").click(function () {
LoadContractOptionFundingPlanBlockByIndexNumber(currentIndexNumber);
currentIndexNumber++;
});
}
$(document).ready(function () {
GenerateOptionFundingPlanBlock();
});
var currentIndexNumber = 10;
Here is my View:
<form action="#" method="post" name="formCreateContracOption" id="formCreateContracOption">
#Html.AntiForgeryToken()
#Html.LabelForRequired(x=>x.ThisContractOption.OptionName)
#Html.ValidationMessageFor(x=>x.ThisContractOption.OptionName)
#Html.TextBoxFor(x=>x.ThisContractOption.OptionName) <br/><br/>
Period of Performance
#Html.TextBoxFor(x=>x.ThisContractOption.OptionStartDate)
#Html.TextBoxFor(x=>x.ThisContractOption.OptionEndDate) <br /><br />
<a id="lnkAddFundingBlock" href="#">Add Funding Plan</a> <br/><br/>
<div id="ContractOptionFundingPlanBlock"></div>
<input type="submit" id="btnCreateContractOption" name="btnCreateContractOption" value="Submit" />
</form>
After all, when user clicks on the Submit button, the whole thing will be posted to the controller.
Here is my Controller.
[HttpPost]
public ActionResult CreateContractOption(int contractId, ContractOptionCreateEditViewModel viewModel, FormCollection form)
{
var fundList = new List<OptionFundingPlanObject>();
var allOptionAmountKeyList = form.AllKeys.Where(x => x.Contains("FundList") && x.Contains("Index")).ToList();
var indexNumberList = new List<int>();
foreach(var thisKey in allOptionAmountKeyList)
{
var convertedIndex = Convert.ToInt32(Regex.Match(thisKey, #"\d+").Value);
indexNumberList.Add(convertedIndex);
}
return View();
}
The reason I am asking is because it is not simply a How to Post a List to the controller question.
When the List starts with a ZERO index, and every other index is in a sequence, it is pretty easy to handle.
In my case, user may generate a new Option, by calling my Partial View. User will have the ability to remove the generated option, and create a new one. The index then changed. In this case, I have to find another way to solve the problem.
var x = "FundList[10].Amount";
int xIndex = Convert.ToInt32(Regex.Match(x,#"\d+").Value); //10
This is a werid question though. What are you doing? :)
If you are always going to have the strings in the form you provided you can split it on the brackets and get the item from the returned string array:
var x = "FundList[10].Amount";
var num = x.Split('[', ']')[1];
int res = Convert.ToInt32(num);
As its fixed as FundList simply:
int i = int.Parse(x.Substring(9, x.IndexOf(']') - 9));
Maybe something like: Online Demo
using System;
using System.Text.RegularExpressions;
public class Program
{
public static void Main()
{
string text = "FundList[15].Amount";
var result = Convert.ToInt32(Regex.Replace(text, #"[^\d]+",""));
Console.WriteLine("Result is = {0}", result);
}
}
If you prefer LINQ syntax to Regex:
var x = "FundList[10].Amount";
int n = Int32.Parse(new string(x.Where(c => Char.IsDigit(c)).ToArray())); //10
Related
In my ASP.NET Core app I have a cshtml page where I am trying to use the Twitter Typeahead.
Here is my markup:
<div id="type-ahead">
<input class="twitter-typeahead form-control" id="typeLocation" type="text" />
</div>
Here is my Javascript for the html input:
var substringMatcher = function (strs) {
return function findMatches(q, cb) {
var matches, substringRegex;
// an array that will be populated with substring matches
matches = [];
// regex used to determine if a string contains the substring `q`
substrRegex = new RegExp(q, 'i');
// iterate through the pool of strings and for any string that
// contains the substring `q`, add it to the `matches` array
$.each(strs, function (i, str) {
if (substrRegex.test(str)) {
matches.push(str);
}
});
cb(matches);
};
};
var locations = $.get('/Home/getlocationlist', function (data) {
});
$('#type-ahead .twitter-typeahead').typeahead({
hint: true,
highlight: true,
minLength: 1
},
{
name: 'locations',
source: substringMatcher(locations)
});
My question is pertaining to getting the data source for the typeahead. Here is my jQuery get:
var locations = $.get('/Home/getlocationlist', function (data) {});
This is my controller function that the $.get retrieves:
public ActionResult GetLocationList()
{
var list = ExecuteRows("SELECT LocationName FROM Location ORDER BY LocationName");
var locations = list.SelectMany(x => x.Values);
return Ok(locations);
}
The $.get function return data and assigns it to location var. The problem is that the data comes back as an array looking like this
{["Location 1","Location 2","Location 3"]}
However, as I start typing, the typeahead starts to display multiple lines where each line shows the array noted above.
I am trying to figure out how to properly process the data from the controller method into the typeahead.
Thank you!
as I start typing, the typeahead starts to display multiple lines where each line shows the array noted above.
To fix above issue, please try to modify the code to put the code of initializing a typeahead inside Ajax success callback function, like below.
var substringMatcher = function (strs) {
return function findMatches(q, cb) {
var matches, substringRegex;
// an array that will be populated with substring matches
matches = [];
// regex used to determine if a string contains the substring `q`
substrRegex = new RegExp(q, 'i');
// iterate through the pool of strings and for any string that
// contains the substring `q`, add it to the `matches` array
$.each(strs, function (i, str) {
if (substrRegex.test(str)) {
matches.push(str);
}
});
cb(matches);
};
};
$.get('/Home/getlocationlist', function (data) {
var locations = data;
$('#type-ahead .twitter-typeahead').typeahead({
hint: true,
highlight: true,
minLength: 1
},
{
name: 'locations',
source: substringMatcher(locations)
});
});
Test Result
I used the following tutorial:
http://msdn.microsoft.com/en-us/library/gg508808%28VS.98%29.aspx
And everything seemed fine, but in my case, string Username always comes back null. After tonnes of research, I found everyone discovered BIND Prefixes. That would be great in many circumstances, but not this one. I should note all properties and names line up, however in my for loop, the EditorFor creates a [i].Username field and this doesn't map to any model property.
QUESTION: I think I want to map [i].Username to Username where i is any number from 0-infinity, so when it GETS, the value is passed to the Action properly. How do I do this? If this is wrong, what do I do validate this for a specific row in a table?
#for (var i = 0; i < Model.Count; i++)
{
BLAH BLAH BLAH CODE FOR BUILDING TABLE ROWS
<td>
#Html.EditorFor(modelItem => Model[i].Username)
</td>
}
Since I could technically have HUNDREDS if not THOUSANDS of records, I would rather not had a binding PREFIX for all 1000. Am I fundamentally missing something here? I am new to ASP.NET MVC and am used to WebForms so I feel like sometimes I am mixing concepts and mashing up something that is entirely wrong.
EDIT:
I fixed it by doing the following, but not sure if this is the best idea. I set the parameter equal to the FieldName without [i] prefix, but still retrieve the element with the [i] prefix. Javascript isn't my Forte so please let me know if it is horrible.
adapters.add("remote", ["url", "type", "additionalfields"], function (options) {
var value = {
url: options.params.url,
type: options.params.type || "GET",
data: {}
},
prefix = getModelPrefix(options.element.name);
$.each(splitAndTrim(options.params.additionalfields || options.element.name), function (i, fieldName) {
var paramName = fieldName.substr(fieldName.lastIndexOf(".") + 1);
var actualFieldName = appendModelPrefix(fieldName, prefix)
value.data[paramName] = function () {
return $(options.form).find(":input").filter("[name='" + escapeAttributeValue(actualFieldName) + "']").val();
};
});
setValidationValues(options, "remote", value);
});
You have not posted your code for the model or controller, but assuming you have a RemoteAttribute applied to property Username, for example
public class MyModel
{
[Remote("IsValidUserName", "Person")]
public string Username { get; set; }
}
with a method in PersonController
public JsonResult IsValidUserName(string Username)
{
....
}
and the view
#model List<Person>
...
#for (var i = 0; i < Model.Count; i++)
{
#Html.EditorFor(m => m[i].Username)
}
This will generate html such as
<input name="[0].UserName" ... />
<input name="[1].UserName" ... />
Unfortunately the remote method in jquery-validate posts back the name and value of the element so that the ajax call looks like
$.ajax({
url: '/Person/IsValidUserName',
data: { [0].UserName: 'someone#somewhere.com' },
...
which will not bind.
I have reported this as an issue at Codeplex with a possible solution. In the meantime you can modify the remote method in jquery-validate.js file as follows
remote: function(value, element, param) {
....
var data = {};
// data[element.name] = value;
data[element.name.substr(element.name.lastIndexOf(".") + 1)] = value; // add this
This will strip the prefix so that the posted data is
data: { UserName: 'someone#somewhere.com' },
and will correctly bind to the method.
Assuming the code is formatted in the following way:
View:
#for(var i = 0; i<Model.Count; i++) {
<div class="row">
#Html.EditorFor(modelItem => Model[i].Username)
</div>
}
<style>
.valid{
background: lime;
}
</style>
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
Model:
public class MyModel {
[Remote("IsValidUserName", "Validation", HttpMethod = "POST")]
public string Username { get; set; }
}
It is possible to use the automatic modelbinding to bind to the remote validation. If you were to use a list or array for this, the binding would fail while a Dictionary can catch this error.
Be aware however that the Key in the dictionary will be consistent with the id in the view (e.g. [5].Username will map to {Key: 5, Value: MyModel{Username:...}}) and won't be a default 0, hence the use of a Linq query.
Controller:
[HttpPost]
public JsonResult IsValidUserName(Dictionary<int,MyModel> Users) {
return Json(Users.First().Value.Username.Contains("User"));
}
I have a form which asks users for their personal info and their family members.
fields of the family members section is repeating.
my question is what is best practice to handle these kind of repeating forms?
I currently use AJAX to repeat forms but how to collect data from these repeating fields?
since some one asked for how I repeat form, I do it like this:
AJAX Call
$(document).on('click', '.btn-add-item', function (e) {
e.preventDefault();
var $results = $('#results');
$.ajax({
url: '/AJAX/AddFamilyForm',
type: 'post',
success: function (data) {
$(data).appendTo($results);
afterAJAX();
}
});
});
C# code
[HttpPost]
public PartialViewResult AddFamilyForm()
{
if (!Request.IsAjaxRequest()) return null;
return PartialView("_FamilyForm");
}
This is some skeleton code on how to get this to work with proper model-binding in MVC. You'll need to write some JS to be able to delete/add new rows.
Model
public class MyModel
{
public FamilyMembers[] FamilyMembers { get; set; }
}
View
<button id="addNewFamilyMember" type="button">Add</button>
#if (Model.FamilyMembers != null)
{
for (int i = 0; i < Model.FamilyMembers.Length; i++)
{
<tr>
<td>
<button type="button">Delete</button>
#Html.Hidden("FamilyMembers.Index", i)
</td>
<td>
#Html.TextBoxFor(m => Model.FamilyMembers[i].Relation)
</td>
<td>
#Html.TextBoxFor(m => Model.FamilyMembers[i].FullName)
</td>
</tr>
}
}
Below is the code for adding a new member. It creates html dynamically and is able to bind to the posted model because of naming conventions. time gives each added row a unique id so all the data stays together.
JS (using Jquery)
var hidden = '#Html.Hidden("FamilyMembers.Index", "{id}")';
var relationHtml = '#Html.TextBox("FamilyMembers[{id}].Relation")';
var fullNameHtml = '#Html.TextBox("FamilyMembers[{id}].FullName")';
$("#addNewFamilyMember").on("click", function () {
var time = Date.now();
var deleteHtml = "<button type='button'>Delete</button>";
$("#familyMembers-table").find("tbody")
.append($("<tr><td>" + hidden.replace("{id}", time) + deleteHtml + "</td>" +
"<td>" + relationHtml.replace("{id}", time) + "</td>" +
"<td>" + fullNameHtml.replace("{id}", time) + "</td></tr>"));
});
One of the solution could be combination of hidden field and control name.
Steps:
Use a hidden field to keep the count the number of row.
Create controls with name like text_relation_1 for first row and text_relation_2 for second row and so on
Generate other controls in same way.
Increase and decrease the hidden field value so that when values post you can know the number of rows added by the user
On your action use FormCollection and loop though hidden field number and get the values from FormCollection
Like suppose I created 3 rows then I can create a action like below
public ActionResult SomeActionMethod(FormCollection formCollection, string hid)
{
for(int i=1;i<hid;i++)
{
var relationId="text_relation_"+i;
var firstrealtion=formCollection[relationId];
...
}
}
You don't need any extra Ajax requests for this, since you can use established and standard <form> features.
Just append [] to the name of the added forms and you'll end up with an array rather than a single value in your HTTP request once the form is submitted:
<input type="text" name="relation[]" /><input type="text" name="fullname[]" />
<input type="text" name="relation[]" /><input type="text" name="fullname[]" />
<input type="text" name="relation[]" /><input type="text" name="fullname[]" />
In this example you'd end up with an array relation and an array fullname, both containing your datasets.
I have dropdown list which was created dynamically like:
#for(int i=0;i<=count;i++)
{
#Html.DropDownListFor(m => m.GetTimeSheetDetails[i].PROJ_ID, (SelectList)ViewBag.ProjectList, "-- Choose a Project --", new { #class = "ddlProjectvalue" })
}
<input type="submit" value="Add Record" name="btn"/>
in Contoller I am loading data to dropdownlist:
[HttpPost]
Public ActionResult Timesheet()
{
TimsheetModel model=new TimesheetModel();
if(btn=="Add Record")
{
var data= Session["ddlData"] as IEnumerable<SelectListItem>;
SelectList list1=new SelectList(data,"Value","Text",model.ProjID);
ViewBag.ProjectList=list1;
count++; // ADDS NEW RECORD
return View();
}
else
{
var result = (from proj in db.PROJECTs where proj.IS_DELETED == "N" select new { Value = proj.ID, Text = proj.NAME })
SelectList list = new SelectList(result, "Value", "Text", tm.PROJ_ID);
ViewBag.ProjectList = list;//Data loaded here for Dropdown list
}
return View();
}
Now My Scenario is if count=5 which means if we have five dropdown lists, when I select item in first dropdown list should not show in second dropdown list and if we have select item in second dropownlist should not show items of first and second in third dropdown list. for that I have written script like:
<script>
$(document).ready(function () {
$('.ddlProjectvalue').change(function () {
var id = $('.ddlProjectvalue').attr('id');
var selector = "#" + id;
var selectedValue = $(this).val();
$.ajax({
url: "#Url.Action("GetDDLData","Employer")",
data: { selectedValue: selectedValue, id: id },
dataType: "json",
type: "GET",
error: function () {
alert(" An error occurred.");
},
success: function (data) {
debugger;
$("" + selector + "").removeClass("ddlProjectvalue");
$('.ddlProjectvalue').empty();
var optionhtml1 = '<option value="' +
0 + '">' + "--Choose a Project--" + '</option>';
$(".ddlProjectvalue").append(optionhtml1);
$.each(data, function (i) {
var optionhtml = '<option value="' +
data[i].Value + '">' + data[i].Text + '</option>';
$(".ddlProjectvalue").append(optionhtml);
});
}
});
});
});
</script>
and when i pass selected value to controller like:
public ActionResult GetDDLData(string selectedValue, string id, string addrecord)
{
int projectid = Convert.ToInt32(selectedValue);
if (id == "GetTimeSheetDetails_0__PROJ_ID")
{
IEnumerable<SelectListItem> projectslist = (from proj in db.PROJECTs where proj.IS_DELETED == "N" && proj.ID != projectid select proj).AsEnumerable().Select(projt => new SelectListItem() { Text = projt.NAME, Value = projt.ID.ToString() });
var result = new SelectList(projectslist, "Value", "Text", tm.PROJ_ID).ToList();
Session["ddlData"] = result;
ViewBag.ProjectList = result;
return Json(result, JsonRequestBehavior.AllowGet);
}
else
{
var result = Session["ddlData"] as IEnumerable<SelectListItem>;
var query = (from data in result where data.Value != selectedValue select data) as IEnumerable<SelectListItem>;
Session["ddlData"] = query;
return Json(result, JsonRequestBehavior.AllowGet);
}
}
Now my problem is when I add new record by clciking on Add button, loading Session["ddldata"] data to total dropdown list instead it should remain selectlist item in first dropdownlist for first time, I need like when i first select a dropdownlist item in first dropdown list it should remain same when add record also. it means i should prevent server side load on first select list item and vice versa.
Note: Due to some issues i should add record on server side only
How I can prevent it, I tried like preventDefault or return false using jquery, but not working, Any Ideas? how can I fix it.
I think you are overcomplicating things here. You don't really need to request new options from server. Why not just filter the option out on the javascript side?
$(document).ready(function() {
$('.ddlProjectvalue').change(function() {
updateDDLValues();
});
updateDDLValues();
});
function updateDDLValues() {
// Display all
$('.ddlProjectvalue option').show();
// Hide all selected options from other selectlists
$('.ddlProjectvalue').each(function(i,element) {
var selectedvalue = $(element).find('option:selected').val();
$('.ddlProjectvalue').not(element).find('option[value="'+selectedvalue+'"]').hide();
});
}
Fiddle:
http://jsfiddle.net/Pt7qV/2/
Update:
As for the serverside part of your question, there are some serious flaws in your code. You increase the count property in your controller and use the variable clientside. First you'd think that's how it's done but nope it doesn't work that way.
You are returning View when Add Record is submitted but you aren't returning any model with it.
Your TimsheetModel would look something like this:
public class TimsheetModel
{
public int Count {get; set;}
}
In your controller you pass this to the view:
TimsheetModel model=new TimesheetModel();
if(btn=="Add Record")
{
var data= Session["ddlData"] as IEnumerable<SelectListItem>;
SelectList list1=new SelectList(data,"Value","Text",model.ProjID);
ViewBag.ProjectList=list1;
model.Count++; // ADDS NEW RECORD
return View(model);
}
And in your view:
#model TimsheetModel
#for(int i=0;i<=Model.Count;i++)
{
#Html.DropDownListFor(m => m.GetTimeSheetDetails[i].PROJ_ID, (SelectList)ViewBag.ProjectList, "-- Choose a Project --", new { #class = "ddlProjectvalue" })
}
<input type="submit" value="Add Record" name="btn"/>
I'd suggest you to go back to tutorials or books a bit, this is quite basic stuff after all. I won't go into how you are going to handle database side etc. since I think this answer would just escalate into explaining basic stuff.
I am trying to get the selected time from my timepicker then assign it as the value in the #Html.TextBox() value attritibute. I’ve tried using this line of code: #(timeValue) = timepicker('getTime')… but it doesn’t store a value in the timeValue variable. Then I tried timeValue = timepicker('getTime') and get an error message about datepicker not being defined. The code below works fine except for the part where I need to get the selected value from my timepicker.
How can I get the selected time from my timepicker and assign it to a TextBox value?
Thanks for any help with this one.
string date = "date" + course.Index.ToString();
string time = "time" + course.Index.ToString();
string timeValue = "";
<script type="text/javascript">
$(function () {
$('input[name= #(date) ]').datepicker();
$('input[name=#(time)]').timepicker({});
#(timeValue) = timepicker('getTime');
});
</script>
string day = DateTime.Now.ToShortDateString();
#Html.TextBox(name: time, value: timeValue, htmlAttributes: new { id = time, #class = "TextBoxCss" })
So I kept the code very much like my original post....
string date = "date" + course.Index.ToString();
string time = "time" + course.Index.ToString();
<script type="text/javascript">
$(function () {
$('input[name= #(date) ]').datepicker();
$('input[name=#(time)]').timepicker({});
});
</script>
string day = DateTime.Now.ToShortDateString();
#Html.TextBox(name: time, value: "Set Time", htmlAttributes: new { id = time, #class = "TextBoxCss" })
#Html.TextBox(name: date, value: day, htmlAttributes: new { id = date, #class = "TextBoxCss" })
Then in my controller I'm doing this and I can create a loop to iterate through all texboxes in the list:
[HttpPost]
public ActionResult Edit(int id, FormCollection formCollection)
{
String theTimes = Convert.ToString(formCollection["time0"]);
}
You can't assign javascript to razor variable. Instead use jquery to assign the value.
But first of all, i think you are not using the #Html.TextBox right.
Try,
#Html.TextBox("time", null, new {id = "time", #class = "TextBoxCss"})
Then jquery,
var timeValue = timepicker('getTime');
$("#time").val(timeValue);
Edited:
Use jquery, ajax form serialize. E.g.
$("#sendTimeButton").on("click", function () { // or use on form submit
$.ajax({
type: "POST", // or GET
url: "#Url.Action("Edit", "ControllerName")",
data: $("#myForm").serialize(), //you can use json also JSON.Stringtify
cache: false,
success: function () {
},
error: function () {
console.log("error");
}
});
});
You can also use an array:
var timeArray = new Array();
$(".TextBoxCss").each(function(){
timeArray.push($(this).val()) // loop through all text boxes with class "TextBoxCss"
});
Then the data part is as such:
data: {times: timeArray}, // "times" refer to the argument in your controller
Then in your controller:
public ActionResult Edit(int id, string[] times)