I am having a Controller Calculations where I have a property called Formula. The main idea behind the Calculation is to perform operations using objects from other tables in the Database , here for instance SetValues.
What I am trying to achieve
I have a textbox for entering the Formula. I want to make sure that the variables used in the Formula are existing in the database. I can display the names of all the available rows in the table SetValues. I would like to make the values being displayed from the Setvalues click-able so that the value being clicked would be displayed on the textbox. I will keep a table with the mathematical operators as well which would also work in the same way. How can I make the TextBox for Formula the way I want? And how can I make the Names to be entered into the TextBox when clicked.
When the formula has been saved I want to use the formula to perform calculations based on the values of the SetValues(Assume SetValue has properties Name and Values(int)). I understand that I need to parse the Formula to perform the calculation which I have not yet tried, but could find other examples where the same has been performed. I have done something similar but with a Calculation string with numbers alone. I have not done something that would parse a formula and then assign values to it. Is it possible to achieve the parsing?
Calculation View
#foreach(var item in ViewBag.SetValues
{
<p>#item.Name</p>
}
#Html.LabelFor(model => model.CalculationName, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.EditorFor(model => model.CalculationName, new { htmlAttributes = new { #class = "form-control" } })
#Html.LabelFor(model => model.CalculationFormula, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.EditorFor(model => model.CalculationFormula, new { htmlAttributes = new { #class = "form-control" } })
<input type="submit" value="Create" class="btn btn-default" />
In a ViewBag I have passed the SetValue and the elements are displayed.
The Model classes and Controller are not relevant for the output I guess. But if it is required comment. Further details can be added.
Update : adding Wireframe sample
To solve your question regarding to adding the formula to the formula textbox when clicked. Here is some sample code. I use jQuery with a double click event.
HTML
<b>Formulas:</b><br/>
<i>Double click to append the formula.</i></br>
<select id="formulas" size="5">
<option>d_0</option>
<option>v_fix</option>
<option>f_Fc</option>
<option>a_sc</option>
</select>
<select id="operands" size="5">
<option>+</option>
<option>-</option>
<option>*</option>
<option>/</option>
</select>
<br/>
<span id="message" style="color: red"></span>
<hr/>
<b>Formula:</b><br/>
<textarea id="formula"></textarea>
JavaScript
$(document.ready( function() {
var formulas = $("#formulas");
var formula = $("#formula");
var operands = $("#operands");
var message = $("#message");
var expectFormula = true;
formulas.dblclick(function() {
if(expectFormula == true) {
var currentFormula = formula.val();
var appendFormula = formulas.val();
var newFormula = (currentFormula + " " + appendFormula).trim();
formula.val(newFormula);
expectFormula = false;
return;
}
message.html("Operand expected.");
});
operands.dblclick(function() {
if(expectFormula == false) {
var currentFormula = formula.val();
var appendFormula = operands.val();
var newFormula = (currentFormula + " " + appendFormula).trim();
formula.val(newFormula);
expectFormula = true;
return;
}
message.html("Formula expected.");
});
});
JSFiddle
https://jsfiddle.net/L1r3xdvq/1/
I am by no means a JavaScript expert, but this should demonstrate on how to do this.
Do note that you have to double check the user input on the server, even though it is created with JavaScript, it is possible to enter whatever you want and send this as formula.
Related
I have a ViewModel that includes 4 different classes with a lot of properties.
Now, if I make a submit in my view, only the properties that are bound to an input field are posted back, which is bad, since I have a table based on one of those classes (which are gone after the submit).
I know I could handle it like this:
#Html.HiddenFor(m => m.class.property)
But with the amount of properties, this seems like a very inconvenient approach.
I could also just get my model via query again, but this also seems like an approach that's not right.
Is there a better approach than those I mentioned above?
Edit:
And I tried it this way too:
#foreach (var property in Model.Mandant.GetType().GetProperties())
{
#Html.HiddenFor(p => property)
}
But for some reasons this doesn't work, sadly.
Edit2 for clarification:
I have an viewModel like this:
public class ValidationViewModel
{
public M_IV_INVOICE Invoice { get; set; }
public List<M_IP_INVOICE_POS> Positions { get;}
public S_KR_KREDITOR_ Kreditor { get; set; }
public S_MD_MANDANTEN Mandant { get; set; }
public ValidationViewModel() { }
public ValidationViewModel(int invoiceId)
{
Invoice = CRUD.GetFirstOrDefault(new M_IV_INVOICE(), string.Format(#"WHERE M_IV_ID IN ({0})", invoiceId));
Positions = Invoice != null ? CRUD.GetList(new M_IP_INVOICE_POS(), string.Format(#"WHERE M_IP_INVOICEID IN ({0})", Invoice.M_IV_ID)) : null;
Kreditor = Invoice?.M_IV_KREDITOR != null ? CRUD.GetFirstOrDefault(new S_KR_KREDITOR_(), string.Format(#"WHERE S_KR_KREDITOR IN ({0})", Invoice.M_IV_KREDITOR), "S_KR_KREDITOR") : null;
Mandant = Invoice?.M_IV_MANDANT != null ? CRUD.GetFirstOrDefault(new S_MD_MANDANTEN(), string.Format(#"WHERE S_MD_FIR IN ({0})", Invoice.M_IV_MANDANT)) : null;
}
}
I have a view which looks like this:
#using (Html.BeginForm("Update", "Home"))
{
#Html.ValidationSummary();
<div class="container">
<div class="row">
<div class="form-group col-2">
#*#Html.LabelFor(m => m.Mandant.S_MD_FIR)
#Html.TextBoxFor(m => m.Mandant.S_MD_FIR, string.Format("{0} - {1}", Model.Mandant?.S_MD_FIR, Model.Mandant.S_MD_BEZEICHNUNG), new { #class = "form-control", placeholder = "0" })
#Html.ValidationMessageFor(m => m.Mandant.S_MD_FIR)*#
#Html.Label("Mandant")
<input readonly value="#string.Format("{0} - {1}", Model.Mandant?.S_MD_FIR, Model.Mandant?.S_MD_BEZEICHNUNG)" class="form-control" placeholder="" id="Mandant" />
</div>
<div class="form-group col-2">
#Html.LabelFor(m => m.Invoice.M_IV_KREDITOR)
#Html.TextBoxFor(m => m.Invoice.M_IV_KREDITOR, new { #class = "form-control", placeholder = "0" })
#Html.ValidationMessageFor(m => m.Invoice.M_IV_KREDITOR)
</div>
<div class="form-group col-2">
#Html.LabelFor(m => m.Invoice.M_IV_INVOICEDATE)
#Html.TextBoxFor(m => m.Invoice.M_IV_INVOICEDATE, new { #class = "form-control", placeholder = "0" })
#Html.ValidationMessageFor(m => m.Invoice.M_IV_INVOICEDATE)
</div>
</div>
.....
<button type="submit" class="btn btn-primary">Update</button>
}
Now, if I press the button and call my update method:
[HttpPost]
public IActionResult Update(ValidationViewModel validationViewModel)
{
if (ModelState.IsValid)
{
validationViewModel.Invoice.Update();
// TODO additional logic
}
return View("Index", validationViewModel);
}
Everything that's not bound to an input field or listed with:
#Html.HiddenFor(m => m.Invoice.M_IV_ID)
is nulled.
And now I'm locking for a more convenient way to circumvent this, if even possible.
ASP.NET does not provide the behavior that you're looking for, as far as I know, there is no #Html.HiddenForModel(model => model.child). I guess it's because if you have that level of data complexity on your view, you might want to simplify it and to present only what's necessary for the view. But you can try to "hack" it with #Html.EditorForModel(), wrapped within an invisible container on your view, something like the snippet below:
<div style="display: none">
#Html.EditorForModel()
#Html.EditorFor(model => model.child1)
#Html.EditorFor(model => model.child2)
#Html.EditorFor(model => model.child3)
</div>
To further elaborate on my problem with the List object:
If I do it the way Dorin Raba showed in the answer:
#Html.EditorFor(model => model.child1)
it won't work because, all the textfields are named without an index, so it's not possible to match the values back.
But I can simply do it this way:
#for (int i = 0; i < Model.M_IP_INVOICE_POS.Count; i++)
{
#Html.EditorFor(model => model.M_IP_INVOICE_POS[i])
}
and now every editor field has the correct name and I get the positions back to the controller.
So no need to query my Positions every time I want to Update something.
I have an HTML.dropdown multiple select2 working perfect. When I save values,
BUT: On update page I have to show the pre selected values in the dropdown
here is the code:
<div class="col-md-6 mb-3" id="categorylist">
<p class="mb-1 font-weight-bold text-muted mt-3 mt-md-0">Category*</p>
#Html.DropDownList("pCategory[]", new SelectList(new admin.Models.CategoryModel().getMultipleCategoryBySP(), "cat_id", "cat_name", --placeToProvideSingleIntValue--),
new { #class = " form-control select2-multiple ", #data_toggle = "select2", #multiple = "multiple", #style = "width: 100%;" })
</div>
in above code, there is a place holder --placeToProvideSingleIntValue-- where I can place single integer value it shows as preSelected.
Solution/HELP Required for: i want to pass an array to it or multiple values anyother way. so it would show multiple pre selected values.
You'll have to use a MultiSelectList instead of a SelectList. Something like
#Html.DropDownList("pCategory[]", new MultiSelectList(new admin.Models.CategoryModel().getMultipleCategoryBySP(), "cat_id", "cat_name", --placeToProvideMultipleIntValue--),
new { #class = " form-control select2-multiple ", #data_toggle = "select2", #multiple = "multiple", #style = "width: 100%;" })
Razor-pages(near the Blazor thingy :D) is one of the newest and most modern frameworks from Microsoft for now, so I think you should try to use tag helpers wherever is possible as you can get so many benefits from using it!
You can have a look at the sample here
So the tag component on your .cshtml page should look quite simple, like:
<select asp-for="pCategory" asp-items="items" multiple class="form-control select2-multiple" style="width: 100%;" data_toggle = "select2"></select>
where an item is a MultiSelectList object
MultiSelectList items = new MultiSelectList(Categories, "cat_id", "cat_name",
selectedValues);
and selectedValues array of ints.
Hope it will help :)
And just one small thing out of the scope. I don't think it is nice to do something like this
new admin.Models.CategoryModel().getMultipleCategoryBySP()
on your client-side. I believe that the right thing that should be done is to pass just a flat object to the client-side and keep the whole business logic/conversions/mapping stuff in the back-end.
Cheers
Class CreateActivityViewModelwill be passed to View.
public class CreateActivityViewModel
{
public List<List<int>> SelectedDepartmentIds { get; set; }
...
}
In the View, using these code to generate html code:
<div class="form-group">
#Html.Label("报名范围", htmlAttributes: new {#class = "col-xs-12"})
<div>
<button id="repeat" type="button">增加单位范围</button>
</div>
#for (var i = 0; i < Model.MaxDepartmentLevel; i++)
{
<div class="col-xs-6">
#if (i == 0)
{
#Html.Label((i + 1) + "级单位", htmlAttributes: new {#class = "control-label col-md-2"})
#Html.DropDownListFor(x => x.SelectedDepartmentIds[0][i], Model.Departments,
"请选择单位", new {#class = "form-control department"})
}
else
{
#Html.Label((1 + i) + "级单位", htmlAttributes: new {#class = "control-label col-md-2"})
#Html.DropDownListFor(x => x.SelectedDepartmentIds[0][i], Enumerable.Empty<SelectListItem>(),
"所有单位", new {#class = "form-control department"})
}
</div>
}
</div>
Here x => x.SelectedDepartmentIds[0][i] is the two dimention List that cannot be passed back to server. When debuging, createActivityViewModel.SelectedDepartmentIds=null. However, Using Chrome Dev Tools to see what is passing to server, I saw SelectedDepartmentIds do submit:
In fact, I have tried almost the same work in other place, the only difference I think is that the success work is passing one dimension List and now two dimension. Does ASP.NET not support binding two dimension List or what?
This should work as expected. From the comments it seems that you have forgotten to include the property to the [Bind(Include=...)] attribute that your Create action is using. This being said, since you are using a view model you don't need any Bind attributes. The properties that are part of the view model will simply be bound. It is much less error prone and explicit.
I'm trying to create an Item edit screen where the user can set a property of the Item, the ItemType. Ideally, when the user returns to the screen, the dropdown would display the ItemType already associated with the Item.
As it is, regardless of what the item.ItemType is, the dropdown will not reflect that in the dropdown. Is there a way around this?
For reference, my code at the moment is:
<div class="form-group">
#Html.LabelFor(model => model.ItemType, new { #class = "control-label col-xs-4" })
<div class="col-xs-8">
#Html.DropDownListFor(model => model.ItemType, (SelectList)ViewBag.ItemType, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.ItemType, String.Empty, new { #class = "text-danger" })
</div>
</div>
The ViewBag is set with the following:
var ItemType = Enum.GetValues(typeof(ItemType));
ViewBag.ItemType = new SelectList(ItemType);
If you're using ASP.NET MVC 5, try just using the EnumHelper.GetSelectList method. Then you don't need ViewBag.ItemType.
#Html.DropDownListFor(model => model.ItemType, EnumHelper.GetSelectList(typeof(ItemType)), new { #class = "form-control" })
If not, you might need to specify the data value and data text fields of the select list.
var itemTypes = (from ItemType i in Enum.GetValues(typeof(ItemType))
select new SelectListItem { Text = i.ToString(), Value = i.ToString() }).ToList();
ViewBag.ItemType = itemTypes;
Then since it's an IEnumerable<SelectListItem> you'll need to change your cast.
#Html.DropDownListFor(model => model.ItemType, (IEnumerable<SelectListItem>)ViewBag.ItemType, new { #class = "form-control" })
Eventually I found a fix - manual creation of the list.
<select class="form-control valid" data-val="true"
data-val-required="The Item Type field is required." id="ItemType" name="ItemType"
aria-required="true" aria-invalid="false" aria-describedby="ItemType-error">
#foreach(var item in (IEnumerable<SelectListItem>)ViewBag.ItemType)
{
<option value="#item.Value" #(item.Selected ? "selected" : "")>#item.Text</option>
}
</select>
Try to keep as much of the logic outside of the View and in the Controller.
I saw in your self answer that it looks like you have an enum selected from wihin your controller.
I have a DropDownList in one of my apps that contains a list of Enums. It also has a default value selected, but also has specific enums available to the user. The default selection can be set from within the controller.
This example is based on what my needs were, so you'll need to adapt to your case.
In the controller:
public ActionResult Index()
{
ViewBag.NominationStatuses = GetStatusSelectListForProcessView(status)
}
private SelectList GetStatusSelectListForProcessView(string status)
{
var statuses = new List<NominationStatus>(); //NominationStatus is Enum
statuses.Add(NominationStatus.NotQualified);
statuses.Add(NominationStatus.Sanitized);
statuses.Add(NominationStatus.Eligible);
statuses.Add(NominationStatus.Awarded);
var statusesSelectList = statuses
.Select(s => new SelectListItem
{
Value = s.ToString(),
Text = s.ToString()
});
return new SelectList(statusesSelectList, "Value", "Text", status);
}
In the view:
#Html.DropDownList("Status", (SelectList)ViewBag.NominationStatuses)
This approach will automatically set the default item to the enum that was selected in the controller.
I am developing a ASP.NET MVC 5 website. I have the Employee model which looks something like this:
public class Employee
{
public EmployeeID {get;set;}
//... other properties such as FirstName, Salary which are not important right now
public DateTime? EndingDate {get; set;} //this is important
}
The corresponding part of the view is like this:
<div class="form-group">
#Html.LabelFor(model => model.EndingDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input name="endingDate" type="date" class="form-control datepicker" />
#Html.ValidationMessageFor(model => model.EndingDate, "", new { #class = "text-danger" })
</div>
</div>
As you can see the EndingDate is nullable because I do not necessarily fill the EndingDate field. If I want to create an employee without filling the EndingDate I get a validation error, which makes sense because on the datepicker the value is exactly the following dd.mm.yyy. This happens on Google Chrome. On Mozilla it is null by default but is behaving like a textbox not like a datepicker. I am using bootstrapper css classes, I didn't wrote a single line of front-end development code; if you accept the language. I tried to add to model the DisplayFormat attribute but no success so far.
Any ideas? Or it is a matter of browsers?
At this point a don't know any method. You can try a text box where you will fill the data and validate it in a dynamic way (using Angular by chance). Think about filing a data from a dumb phone. You enter the digits and it feels the data.
dateTimePicker1.CustomFormat= " ";