I have a button that append's the same layout of html but I have a problem taking the select values with it. My html is:
<div id="degreePlusSign">Button</div>
<div class="padding">
<div class="col-md-5 col-xs-12">
<label for="prefix" class="sr-only">Degrees</label>
<select class="form-control marginBottom15">
#{
foreach (var degree in ViewBag.NewDegrees)
{
<option value="#degree.DegreeID" selected>#degree.DegreeName</option>
}
}
</select>
<span class="glyphicon form-control-feedback"></span>
</div>
</div>
JS:
$('#degreePlusSign').on('click', function () {
$(this).closest('.padding').append('<div class="padding mBottom"><i class="fa fa-times-circle fa-2x" aria-hidden="true"></i><div class="col-md-5 col-xs-12"><select class="form-control marginBottom15">#{foreach (var degree in ViewBag.NewDegrees){<option value="#degree.DegreeID" selected>#degree.DegreeName</option>}}</select><span class="glyphicon form-control-feedback"></span></div><div class="col-md-7 col-xs-12"><input class="form-control" placeholder="Major/Area of Study" type="text" /></div></div>');
});
Basically it recreates the html, but my problem is I'm using a foreach loop to bring in the values from the backend and it will only work with the inital container, not the duplicated containers afterwards. How do I keep the values on every duplication with the append jquery?
You have got two options:
Spit options values (formatted) from C# and keep in a JS variable:
{
var opts=new StringBuilder();
var sel="selected";
foreach(var d in ViewBag.NewDegrees)
{
opts.Append($"{d.DegreeName}");
sel="";
}
}
Then somewhere down store it into a js variable:
var optsList="#(opts)";
Now you can use append new HTML as:
$('#degreePlusSign').on('click', function () {
$(this).closest('.padding').append('<div class="padding mBottom"><i class="fa fa-times-circle fa-2x" aria-hidden="true"></i><div class="col-md-5 col-xs-12"><select class="form-control marginBottom15">'+
optsList/*THIS IS THE VALUE WE STORED FROM C# CODE*/
+'</select><span class="glyphicon form-control-feedback"></span></div><div class="col-md-7 col-xs-12"><input class="form-control" placeholder="Major/Area of Study" type="text" /></div></div>');
});
Clone the generated select element and use that:
$('#degreePlusSign').on('click', function () {
/*CLONE EXISTING SELECT ELEMENT. YOU MAY WANT TO PUT AN ID FOR SELECTION*/
var cl=$("select.form-control.marginBottom15").clone();
var d = $("").addClass("padding mBottom")
append("").addClass("fa fa-times-circle fa-2x").attr("aria-hidden",'true');
/*APPEND CLONED SELECT TO INNER DIV*/
d.append("").addClass("col-md-5 col-xs-12").append(cl);
d.append(cl);
d.append("").addClass(glyphicon form-control-feedback");
d.append("").addClass("col-md-7 col-xs-12").append("").addClass("form-control")
.attr("placeholder","Major/Area of Study").attr("type","text");
$(this).closest(".padding").append(d);
});`
Hope you will be able fix any jQuery mess. I haven't used it since long.
.clone was what I was looking for. $('.padding').clone().append('.padding');
Related
I'm trying to get two input values from a view and pass them to controller using FormCollection.
These values change is based in a double click in a table row as you can see in jquery code below.
When i tried the first time only with id_artigo it worked! Then, when i tried do the same with descricao, it return the same view because descricao isn't geting the value.
id_artigo is an integer and descricao is a string.
What am i doing wrong ?
public ActionResult Create(NebluViewModel necessidadeModel, FormCollection collection)
necessidadeModel.NecessidadesModel.id_artigo = int.Parse(collection["label"]);
necessidadeModel.NecessidadesModel.descricao = collection["labelD"];
<div class="form-group">
<span class="id-label"></span>
<input type="hidden" id="label" name="label" value="" />
</div>
<div class="form-group">
<span class="id-labelD"></span>
<input type="hidden" id="labelD" name="labelD" value="" />
</div>
<script>
$(document).ready(function () {
var table = $('#example').DataTable();
$('#example tbody').on('dblclick', 'tr', function () {
let $tr = $(this);
if ($tr.hasClass('selected')) {
$tr.removeClass('selected');
} else {
table.$('tr.selected').removeClass('selected');
$tr.addClass('selected');
$('#classModal').modal('hide');
let targetValue = $tr.find('td:first').text();
let targetDesc = $tr.find('td:nth-child(3)').text();
$('.id-label').text(targetValue);
$('#label').val(targetValue);
$('.id-labelD').text(targetDesc);
$('#labelD').val(targetDesc);
}
});
});
</script>
Update
After check using DevTools, my input values are getting the right values. Only "label" is being passed to controller. I can't understand why "labelD" isn't.
I'm currently building and application in ASP.NET Core MVC and I have ran into a problem which I cannot solve.
I have a form for something and that form should contain multiple identical fields which are added dynamically (1-10). I have managed to do that by creating a ViewComponent which contains those form fields and I make an Ajax call to invoke the view component into a tab if a user chooses to add another segment of those fields.
function CallViewComponent(num_tabs) {
var data = { id: num_tabs };
$.ajax({
type: 'POST',
url: '/Create/CreateActivityForm',
cache: false,
data: data
}).done(function (result) {
var container = "#activity-" + num_tabs;
$(container).html(result);
});
}
The problem arises because each of those fields in that view component shares a name with the other fields so each time I invoke another view component the radio buttons are shared between all identical fields.
Here is a snippet of the ViewComponent:
#model CreateActivityViewModel
<div class="activity-tab">
<div class="form-group">
<label asp-for="La.OrdinalNumber">Redni broj aktivnosti</label><br />
<select asp-for="La.OrdinalNumber" class="ordinals" style="width:50%">
#foreach (var on in Model.OrdinalNumbers)
{
<option value="#on.Value">#on.Text</option>
}
</select>
</div>
<div class="form-group">
<label asp-for="La.Latype">Tip aktivnosti</label><br />
<select asp-for="La.Latype" class="activity-type" style="width:50%">
#foreach (var lt in Model.LaTypes)
{
<option value="#lt">#lt.LatypeName</option>
}
</select>
</div>
<div class="form-group">
<label asp-for="La.Laname">Naziv aktivnosti</label>
<input asp-for="La.Laname" type="text" name="La.Laname" placeholder="Unesite naziv aktivnosti" class="f1-activity-name form-control" id="f1-activity-name">
</div>
Here is my controller which returns the ViewComponent:
[HttpPost]
public IActionResult CreateActivityForm(int id)
{
return ViewComponent("ActivityTab", id);
}
Here is the Invoke method from the ViewComponent:
public IViewComponentResult Invoke(int id)
{
var latypes = _laTypeRepository.GetAllLaType.ToList();
var ordinals = new List<SelectListItem>();
var laperformances = _laPerformanceRepository.GetAllLaPerformance.ToList();
var teachingAids = _teachingAidRepository.GetAllTeachingAid.ToList();
var strategyMethods = _strategyMethodRepository.GetAllStrategyMethod.ToList();
var laCollaboration = _laCollaborationRepository.GetAllLaCollaboration.ToList();
for (int i = 1; i <= 100; i++)
{
ordinals.Add(new SelectListItem($"{ i }. aktivnost", i.ToString()));
}
return View( new CreateActivityViewModel
{
FormId = id,
LaTypes = latypes,
OrdinalNumbers = ordinals,
LaPerformances = laperformances,
StrategyMethods = strategyMethods,
Lacollaborations = laCollaboration,
TeachingAids = teachingAids,
TeachingAidUser = new List<TeachingAid>(),
TeachingAidStudent = new List<TeachingAid>()
});
}
And finally this is where the ViewComponent gets invoked. It is inside another form because I need to submit the main form and all the ViewComponents at once:
<fieldset>
<h4>Aktivnosti</h4>
<!-- Activity Tabs -->
<div id='activity-tabs'>
<!-- Activity Links -->
<ol id="#activity-links">
<li><a href='#activity-1'>#1</a></li>
<li id="add-activity"><button type="button" id='add-activity'><i class="fa fa-plus"></i></button></li>
</ol>
<!-- Activity Content -->
<div id='activity-1'>
<h3>Aktivnost #1</h3>
#await Component.InvokeAsync("ActivityTab")
</div>
</div>
<!-- Navigation Buttons -->
<div class="f1-buttons">
<button type="button" class="btn btn-previous">Prethodna</button>
<button type="submit" class="btn btn-submit">Kreiraj scenarij</button>
</div>
</fieldset>
My question is how do I separate those identical forms and be able to submit them and store every single one of those forms into an array of objects which I can then store into a database.
I am open to all ideas and will change the entire code if necessary.
Thank you!
If you have an array of objects you need to render the components using a FOR loop rather than a FOR-EACH. I like to push common code into a shared view but you can code direct in the view. You will need to set your asp-for attributes in order to bind values to the model
#for (int index = 0; index < Model.Resources.Count; index++)
{
<partial name="_ResourceHidden" for="#Model.Resources[index]" />
Direct render
#for (int index = 0; index < Model.Resources.Count; index++)
{
<tr>
<td>
#Model.Resources[index].ResourceName
</td>
I have a simple form with three different text boxes to enter the search criteria before a resultset can be returned. Among the three fields I want to make two fields conditionally required if the other one is empty.
In the attached screenshot the search form cannot be submitted without entering either the "Title" or "Performers" fields. It is fine if both fields have values. I wanted to achieve this by making "Title" as a required field when "Performers" is empty. But my code below doesn't work. I have the necessary validation at the server side. I am looking for a client side solution.
HTML Source code:
<form id="searchWorkForm">
<div class="contourField textfield">
<label for="searchWorkTitle" class="fieldLabel">Title</label>
<div class="search-input">
<a id="searchWork" href="#" style="z-index: 2000; margin-top: 0;"><img src="/images/profile-search.png" alt="" style="z-index: 1000;" id="profileSearch" /></a>
<input type="text" name="searchWorkTitle" id="searchWorkTitle" class="text caps" value="" placeholder="Title" data-val="true" data-val-requiredif="title is mandatory" data-val-requiredif-otherpropertyname="searchWorkPerformer">
<span class="field-validation-valid" data-valmsg-for="searchWorkTitle" data-valmsg-replace="true"></span>
</div>
</div>
<div class="contourField textfield">
<label for="searchWorkWriter" class="fieldLabel">Writers</label>
<div class="wideInput">
<input type="text" name="searchWorkWriter" id="searchWorkWriter" class="text caps" value="" placeholder="Writer Name">
<span class="field-validation-valid" data-valmsg-for="searchWorkWriter" data-valmsg-replace="true"></span>
</div>
</div>
<div class="contourField textfield">
<label for="searchWorkPerformer" class="fieldLabel">Performers</label>
<div class="wideInput">
<input type="text" name="searchWorkPerformer" id="searchWorkPerformer" class="text caps" value="" placeholder="Performer Name" data-val="true">
<span class="field-validation-valid" data-valmsg-for="searchWorkPerformer" data-valmsg-replace="true"></span>
</div>
</div>
</form>
Client side validation code:
$(function() {
if ($.validator && $.validator.unobtrusive) {
$.validator.addMethod("requiredif", function (value, element, params) {
return !(value.length === 0 && $(params).val().length === 0);
});
$.validator.unobtrusive.adapters.add("requiredif", ["otherpropertyname"], function (options) {
options.rules["requiredif"] = "#" + options.params.otherpropertyname;
options.messages["requiredif"] = options.message;
});
}
$("#searchWork").click(function() {
if ($("#searchWorkForm").valid()) {
// Make an Ajax Call and get the search result
}
});
}
You first need to move the $.validator.addMethod() and $.validator.unobtrusive.adapters.add() functions outside the $(function() { .. }
But based on the description of what your wanting to validate, then the code in your $.validator.addMethod() method should first check if the 'other property' (searchWorkPerformer) has a value, and if so return true. If it does not, then check if searchWorkTitle has a value. If it has, then return true, otherwise its invalid, so return false.
// scripts for jquery, jquery.validate and jquery.validate.unobtrusive
<script>
$.validator.addMethod("requiredif", function (value, element, params) {
if ($(params).val().length === 0) {
return true;
} elseif (value.length === 0) {
return false;
}
return true;
});
$.validator.unobtrusive.adapters.add("requiredif", ["otherpropertyname"], function (options) {
options.rules["requiredif"] = "#" + options.params.otherpropertyname;
options.messages["requiredif"] = options.message;
});
$(function() {
$("#searchWork").click(function() {
if ($("#searchWorkForm").valid()) {
// Make an Ajax Call and get the search result
}
});
}
</script>
Side note: requiredif does not really describe your validation - perhaps requiredifempty would be more appropriate since you only require a value if the other property is empty.
I have a model with say 10 properties. A, B, C and so on...
Property A is an array.
For each value in array I generate one tag like this:
<div class="col-sm-10 row">
#foreach (var item in Model.A)
{
<div class="col-sm-1 right-buffer">
<i class="" aria-hidden="true">#item.Text</i>
</div>
}
</div>
When user clicks on some link I should redirect it to the same page, but with Some model property changed. For example:
Current url: my/controller/someaction?name=Alex&age=20&from=fr&CurrentA=
with model ?name=Alex&age=20&from=fr&CurrentA=
If user clicks on <a> with text foo it should be redirected on my/controller/someaction?name=Alex&age=20&from=fr&CurrentA=foo
then is clicks on <a> with text bar and it should be now redirected on my/controller/someaction?name=Alex&age=20&from=fr&CurrentA=bar
So entire query string (except one parameter) should be preserved to send current model state to server while I want to set one value and redirect it to the same page but with new value.
Eventually, it should acts like postback with one extra value setted to model
Is it possible or I should use JS and perform everything myself?
Manually i solved it like this:
First, create hidden fields for every property in model:
<form asp-controller="search" asp-action="index" method="get" role="form" id="searchForm" asp-antiforgery="false">
<input asp-for="SessionId" type="hidden" name="sessionId" value="#Model.SessionId" />
<input asp-for="Quantity" type="hidden" name="quantity" value="#Model.Quantity"/>
<input asp-for="SortField" type="hidden" name="sortField" value="#Model.SortField"/>
<input asp-for="IsAscending" type="hidden" name="IsAscending" value="#Model.IsAscending" />
<input asp-for="Offset" type="hidden" name="offset" value="0" />
...
</form>
Then, use JS to replace value in hidden field and then submit form. Values from inputs will be autimatically converter in query string, so everything works fine:
function sortDocuments(sortField) {
var sField = document.getElementById('SortField');
var isDescending = document.getElementById('IsAscending');
if (sField.value === sortField) {
if (isDescending.value.toUpperCase() === 'FALSE') {
isDescending.value = 'TRUE';
} else {
sField.value = 'rank';
isDescending.value = 'FALSE';
}
} else {
sField.value = sortField;
isDescending.value = 'FALSE';
}
document.getElementById('searchForm').submit();
}
Not very elegant, but it does its job.
I'm not sure if this can be done as the Url.Action() is trying to access a variable that's only in the foreach's scope - maybe using jQuery?
I have a strongly typed partial view that takes a view model that contains a list of search results.
I iterate through the list and each result is displayed in summarised form. Beside each item in the list there's a button that (should) when clicked launches a modal window that displays more detailed information about the list item. So if there's 10 items in the list there's going to be 10 correspdonding 'More info' buttons.
Partial View:
<div>
#foreach (var result in Model.SearchResults)
{
<div class="basic-search-result">
<div class="row-fluid">
<img class="span3" src="http://some-img.jpg" />
<div class="span3">
<div class="address">
#result.Address.TownCity<br />#result.Address.County
</div>
<div>
€#result.MonthlyRate Monthly
</div>
</div>
<div class="span3 offset1" id="basic-search-result-btns">
<button class="btn btn-primary btn-block" id="btn-show-modal-from-get-all">More info</button>
</div>
</div>
</div>
}
</div>
Just below the foreach loop I define the modal (ViewProperty is a controller action that returns a partial that contains the modal's body):
<div class="modal hide fade in" id="modal-view-property-from-get-all"
data-url="#Url.Action("ViewProperty", "ResidentialProperties", new { id = result.ResidentialPropertyId })">
<div id="view-property-from-get-all-container"></div>
</div>
When one of the buttons is clicked, it should launch a modal. Handled like this:
<script type="text/javascript">
$(document).ready(function () {
$('#btn-show-modal-from-get-all').click(function () {
var url = $('#modal-view-property-from-get-all').data('url');
$.get(url, function (data) {
$('#view-property-from-get-all-container').html(data);
$('#modal-view-property-from-get-all').modal('show');
});
});
});
I know that the Id I'm binding in the Url.Action() is outside of the scope of the foreach. Is it possible to somehow get the result.ResidentialPropertyId and pass it to the Url.Action() dynamically when a button is clicked?
To prove it's working, I placed the Url.Action() in the foreach loop, but this only launched a modal for the first item in the list. Can what I'm trying to do be achieved?
Do not include the Id in the data-url
<div class="modal hide fade in" id="modal-view-property-from-get-all"
data-url="#Url.Action("ViewProperty", "ResidentialProperties")">
<div id="view-property-from-get-all-container"></div>
</div>
With that code the data-url will have a value ResidentialProperties/ViewProperty.
Then include the Id on each row to your button or you can read it from it's container element. I also suggest you remove the id of the button and maybe just have a class to ensure that the ajax call won't get executed for all instances of your buttons
// assuming Id is the identifier per row
<button class="btn btn-primary btn-block show-modal"
data-id="#result.Id">More info</button>
and in your js do the following
$(document).ready(function () {
$('.show-modal').click(function () {
var url = $("#modal-view-property-from-get-all").attr('data-url');
var id = $(this).attr('data-id');
$.get(url + '/' + id, function (data) {
$('#view-property-from-get-all-container').html(data);
$('#modal-view-property-from-get-all').modal('show');
});
});
});