DropDownItem doesn't load items after update(Javascript) partial view - javascript

I have the following functionality: A user registers a series of data in a form, after sending the form and saving it in the database, it is redirected to a second form, where it must load the rest of the information, it is possible that in this second form, contains a lot of values of the first form, but this is not always the case, that is why in this second form there is a button that allows "copy" the data of the first form, to reduce data entry.
As I did?
I created a main view, where the form is loaded as a partial view with the empty model fields, so that the user can enter the information if it is not the same as the first form
Main View:
<div id="partialView">
#Html.Partial("_CreatePartialResponsable", Model)
</div>
In the form that contains the partial view, there is a dropdowlist that is filled through a web service. This dropdow is filled correctly when loading the blank form, the first time.
DropDownList in the partial view
<div class="form-group col-md-6">
#Html.LabelFor(model => model.SeguroNombre, htmlAttributes: new { #class = "titulocampo" })
<select id="SeguroNombre" name="SeguroNombre" class="form-control"><option value=""></option></select>
#Html.ValidationMessageFor(model => model.SeguroNombre, "", new { #class = "text-danger" })
</div>
With this script, the dropdowlist is loaded, I call it from the main view in the document.ready event (it's works perfectly)
function llenaDropDownSeguros(url, valor) {
var data = { nombre: valor };
$.post(url, data).done(function (data) {
if (data.isError) {
muestraError(data.Titulo, data.Mensaje);
} else {
for (var i = 0; i < data.length; i++) {
$('#SeguroNombre').append('<option value=' + data[i].codigoSeguro + '>' + data[i].descripcion + '</option > ');
}
}
});
};
If the user wants to "copy" the information of the previous form, press a button that executes the following function js to go to the server, search the data and return the same partial view, but with the corresponding fields loaded. Re render?
Script to "copy" data
function loadPartial(url, valor) {
$("#esperar").show();
var data = { cedula: valor };
$.post(url, data).done(function (data) {
if (!data.isError) {
$("#esperar").hide();
$("#partialView").html(data);
} else {
$("#esperar").hide();
muestraError(data.Titulo, data.Mensaje);
}
}).fail(manejarErrorAjax);
};
What is the problem?
After the copy button is pressed and the partial view is reloaded with the "copied" data, the dropdownlist items are no longer there. I understand that they are lost, because of course, when doing a partial view reloaded, that dropdown comes empty, that I understood. What I do not understand is because, after reloading that partial view, there is no way I can fill the dropdowlist again. By test I made a change and in the click event of the ddl, I called the loadPartial function to see if it worked, but even though in the browser console, it is seen that the function is executed and the data object takes 94 values, these they are not loaded to the dll.
Can you help me with this problem?

Related

How do I populate a list field in a model from javascript?

I have a Kendo.MVC project. The view has a model with a field of type List<>. I want to populate the List from a Javascript function. I've tried several ways, but can't get it working. Can someone explain what I'm doing wrong?
So here is my model:
public class Dashboard
{
public List<Note> ListNotes { get; set; }
}
I use the ListNotes on the view like this:
foreach (Note note in Model.ListNotes)
{
#Html.Raw(note.NoteText)
}
This works if I populate Model.ListNotes in the controller when the view starts...
public ActionResult DashBoard(string xsr, string vst)
{
var notes = rep.GetNotesByCompanyID(user.ResID, 7, 7);
List<Koorsen.Models.Note> listNotes = new List<Koorsen.Models.Note>();
Dashboard employee = new Dashboard
{
ResID = intUser,
Type = intType,
FirstName = user.FirstName,
LastName = user.LastName,
ListNotes = listNotes
};
return View(employee);
}
... but I need to populate ListNotes in a Javascript after a user action.
Here is my javascript to make an ajax call to populate ListNotes:
function getReminders(e)
{
var userID = '#ViewBag.CurrUser';
$.ajax({
url: "/api/WoApi/GetReminders/" + userID,
dataType: "json",
type: "GET",
success: function (notes)
{
// Need to assign notes to Model.ListNotes here
}
});
}
Here's the method it calls with the ajax call. I've confirmed ListNotes does have the values I want; it is not empty.
public List<Koorsen.Models.Note> GetReminders(int id)
{
var notes = rep.GetNotesByCompanyID(id, 7, 7);
List<Koorsen.Models.Note> listNotes = new List<Koorsen.Models.Note>();
foreach (Koorsen.OpenAccess.Note note in notes)
{
Koorsen.Models.Note newNote = new Koorsen.Models.Note()
{
NoteID = note.NoteID,
CompanyID = note.CompanyID,
LocationID = note.LocationID,
NoteText = note.NoteText,
NoteType = note.NoteType,
InternalNote = note.InternalNote,
NoteDate = note.NoteDate,
Active = note.Active,
AddBy = note.AddBy,
AddDate = note.AddDate,
ModBy = note.ModBy,
ModDate = note.ModDate
};
listNotes.Add(newNote);
}
return listNotes;
}
If ListNotes was a string, I would have added a hidden field and populated it in Javascript. But that didn't work for ListNotes. I didn't get an error, but the text on the screen didn't change.
#Html.HiddenFor(x => x.ListNotes)
...
...
$("#ListNotes").val(notes);
I also tried
#Model.ListNotes = notes; // This threw an unterminated template literal error
document.getElementById('ListNotes').value = notes;
I've even tried refreshing the page after assigning the value:
window.location.reload();
and refreshing the panel bar the code is in
var panelBar = $("#IntroPanelBar").data("kendoPanelBar");
panelBar.reload();
Can someone explain how to get this to work?
I don't know if this will cloud the issue, but the reason I need to populate the model in javascript with an ajax call is because Model.ListNotes is being used in a Kendo Panel Bar control and I don't want Model.ListNotes to have a value until the user expands the panel bar.
Here's the code for the panel bar:
#{
#(Html.Kendo().PanelBar().Name("IntroPanelBar")
.Items(items =>
{
items
.Add()
.Text("View Important Notes and Messages")
.Expanded(false)
.Content(
#<text>
#RenderReminders()
</text>
);
}
)
.Events(e => e
.Expand("getReminders")
)
)
}
Here's the helper than renders the contents:
#helper RenderReminders()
{
if (Model.ListNotes.Count <= 0)
{
#Html.Raw("No Current Messages");
}
else
{
foreach (Note note in Model.ListNotes)
{
#Html.Raw(note.NoteText)
<br />
}
}
}
The panel bar and the helpers work fine if I populate Model.ListNotes in the controller and pass Model to the view. I just can't get it to populate in the javascript after the user expands the panel bar.
Perhaps this will do it for you. I will provide a small working example I believe you can easily extend to meet your needs. I would recommend writing the html by hand instead of using the helper methods such as #html.raw since #html.raw is just a tool to generate html in the end anyways. You can write html manually accomplish what the helper methods do anyway and I think it will be easier for you in this situation. If you write the html correctly it should bind to the model correctly (which means it won't be empty on your post request model) So if you modify that html using javascript correctly, it will bind to your model correctly as well.
Take a look at some of these examples to get a better idea of what I am talking about:
http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
So to answer your question...
You could build a hidden container to hold your list values like this (make sure this container is inside the form):
<div id="ListValues" style="display:none">
</div>
Then put the results your ajax post into a javascript variable (not shown).
Then in javascript do something like this:
$('form').off('submit'); //i do this to prevent duplicate bindings depending on how this page may be rendered futuristically as a safety precaution.
$('form').on('submit', function (e) { //on submit, modify the form data to include the information you want inside of your ListNotes
var data = getAjaxResults(); //data represents your ajax results. You can acquire and format that how you'd like I will use the following as an example format for how you could save the results as JSON data: [{NoteID ="1",CompanyID ="2"}]
let listLength = data.length;
for (let i = 0; i < listLength; i++) {
$('#ListValues').append('<input type="text" name="ListNotes['+i+'].NoteID " value="' + data.NoteID +'" />')
$('#ListValues').append('<input type="text" name="ListNotes['+i+'].CompanyID " value="' + data.CompanyID +'" />')
//for your ajax results, do this for each field on the note object
}
})
That should do it! After you submit your form, it should automatically model bind to you ListNotes! You will be able to inpsect this in your debugger on your post controller action.

Invoking a ViewComponent within another ViewComponent

I am currently coding within a ViewComponent (ViewComponent1) view. Within this View, I have listed a few items:
As you can see, the channels 11, 12, 13 and 14 are clickable. Each channel has some additional information (OBIS, avtalsid.. etc). What I´m trying to do is to invoke ViewComponent2, within ViewComponent1, and pass along some of the data, based on the clicked item.
What I tried to do is to create another View called "Test" and within that View invoke ViewComponent2 along with its parameters, like this:
<div class="row">
<div class="col-md-2 canalstyle">
<a asp-controller="Customer" asp-action="Test" asp-route-pod="#item.STATION"
asp-route-idnr="#item.IDNR" asp-route-kanal="#item.KANAL" asp-route-start="#Model.start"
asp-route-end="#Model.end"> #Html.DisplayFor(modelItem => item.KANAL)</a>
</div>
</div>
This works, but this method redirects me away from my current View (ViewComponent 1). I don't want that. I want the current view to load the additional information from ViewComponent2.
My function that runs the ajax:
function myFunction() {
var data = JSON.stringify({
'idnr': id,
'start': this.start,
'end': this.end
});
$.ajax({
url: '#Url.Action("Test2","Customer")',
type: 'GET',
data: { idnr: id, start: this.start, end: this.end },
contentType: 'application/json',
success: handleData(data)
})
};
function handleData(data) {
alert(data);
var url = $(this).attr("href");
var $target = $(this).closest("div").find(".details");
$.get(url, function (res) {
$target.html(res);
});
//do some stuff
}
And my Test2 Action:
public async Task<IActionResult> Test2(string idnr, string start, string end)
{
ServiceClient r2s = new R2S.ServiceClient();
R2S.Konstant[] kData = r2s.GetKonstantListAsync(new string[] { "IDNR" }, new string[] { idnr}).Result; // mätarnummer in... --> alla konstanter kopplade till denna.
return ViewComponent("MeterReader2", new { k = kData[0], start = start, end = end });
}
I am trying to target the same DOM.. Any ideas?
Your current code is rendering links (a tags) and normally clicking on a link will do a new GET request, which is what you are seeing , the redirect to the new action method.
If you do not want the redirect, but want to show the result of the second view component in same view, you should use ajax.
For example, If you want to show the result of second view component just below each link, you may add another html element for that. Here i am adding an empty div.
<div class="row">
<div class="col-md-2 canalstyle">
<a class="myClass" asp-controller="Customer" asp-action="DetailsVc"
asp-route-id="#item.Id" > #item.KANAL</a>
<div class="details"></div>
</div>
</div>
Here i just removed all those route params you had in your orignal question and replaced only with on param (id) . Assuming your items will have an Id property which is the unique id for the record(primary key) and using which you can get the entity (from a database or so) in your view component to get the details.
This will generate the link with css class myClass. You can see that, i used asp-action attribute value as "DetailsVc". We cannot directly use the view component name in the link tag helper as attribute value to generate the href value. So we should create a wrapper action method which returns your view component result such as below
public IActionResult DetailsVc(int id)
{
return ViewComponent("DetailsComponent", new { id =id });
}
Assuming your second view components name is DetailsComponent and it accepts an id param. Update the parameter list of this action method and view component as needed. (but i suggest passing just the unique Id value and get details in the server code again)
Now all you have to do is have some javascript code which listen to the click event on those a tags and prevent the normal behavior (redirect) and make an ajax call instead, use the ajax call result to update the details div next to the clicked link.
You can put this code in your main view (or in an external js file without the #section part)
#section Scripts
{
<script>
$(function() {
$("a.myClass").click(function(e) {
e.preventDefault();
var url = $(this).attr("href");
var $target = $(this).closest("div").find(".details");
$.get(url,function(res) {
$target.html(res);
});
});
});
</script>
}

How to invoke my post method when I'm changing dropdown list in ASP.NET MVC

I'm very new to MVC and Javascript so please be patient with me, I'm working on small application and I came to part when I need to select something from dropdown list and based on that selection I need to redirect user to another View, I also need to determine somehow where I should redirect user, so that is reason why I tried to pass parameter also ( database ID to my post method) but unfortunatelly this is not working, in section below I will post my code:
Method which is sending data to my DropDownList :
public ActionResult ShowArticleGroup()
{
List<ArticleGroup> articlesGroups = GroupsController.GetAllGroups();
ViewBag.articlesGroups = articlesGroups;
return View(articlesGroups);
}
[HttpPost]
public ActionResult ShowArticleGroup(string id)
{
//Here I wanted to take ID of selected Group and because there will be allways 3 Groups I can do if else and Redirect by ID
if(id =="00000000-0000-0000-0000-000000000002")
{
return RedirectToAction("Create","Article");
}
return RedirectToAction("Create", "Article");
}
And my VIEW - there is only one control on the view : just one dropdown, and based on selection I should be redirected to another view, and I wanted here to take ID of selected group and by that I wanted to redirect user to appropiate view:
#model IEnumerable<Model.ArticleGroup>
#{
ViewBag.Title = "Add new article";
}
<h3 style="text-align:center">Choose article group</h3>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true)
<div class="form-group" style="text-align:center">
#Html.DropDownList("Group", new SelectList(ViewBag.articlesGroups, "GroupID", "GroupTitle.Name"), null, new { onchange = "document.location.href = '/Articles/ShowArticleGroup/' + this.options[this.selectedIndex].value;" })
</div>
</div>
}
First of all, usage of location.href on DropDownList seems wrong here:
#Html.DropDownList("Group", new SelectList(ViewBag.articlesGroups, "GroupID", "GroupTitle.Name"), null,
new { onchange = "document.location.href = '/Articles/ShowArticleGroup/' + this.options[this.selectedIndex].value;" })
AFAIK, location.href used for redirect to another page using HTTP GET, hence it will try to call first ShowArticleGroup action method without parameter, and the URL parameter simply ignored since given URL parameter only exist in POST.
To submit the form with DropDownList, you need to handle change event triggering POST into controller action method:
jQuery
<script type="text/javascript">
$(document).ready(function() {
$("#Group").change(function() {
var groupId = $("#Group").val();
$.post('#Url.Action("ShowArticleGroup", "ControllerName")', { id: groupId }, function (response, status) {
// response handling (optional)
});
});
});
</script>
DropDownList
#Html.DropDownList("Group", new SelectList(ViewBag.articlesGroups, "GroupID", "GroupTitle.Name"), null)
I recommend you using strongly-typed DropDownListFor with binding to a viewmodel approach if you want to pass viewmodel contents during form submit.
NB: $.post is shorthand version of $.ajax which uses POST submit method as default.
Related issues:
Autopost back in mvc drop down list
MVC 4 postback on Dropdownlist change

Show and hide table with filtered content after submit(jQuery)

Szenario 1
i have a view where a form and a table existing.
through my jQuery code the "table id ="divEdit"" is at the beginning invisble and should come up, when i "submit id ="btnEdit"" the form:
$(function () {
$("#divEdit").hide();
$("#btnEdit").click(function (e) {
e.preventDefault()
$("#divEdit").show();
});
});
At the beginning it is everything great, the table is invisible and i can insert something into my form, which is a search function that have two inputs which filter my content of the table.
The values of the form will be sent to my Index controller and this controller returns the content of the table.
Controller:
public ActionResult Index(string Product, string searchString2)
{
//List of all Products
List<SelectListItem> product = new List<SelectListItem>();
Productlist(product);
ViewBag.Product = new SelectList(product, "Value", "Text");
//Compares the inserted Product and S_N with the ProductName and Serial in the DB Tabel Products
var newproducts = from n in db.Products select n;
if ((!String.IsNullOrEmpty(Product)) && (!String.IsNullOrEmpty(searchString2)))
{
newproducts = newproducts.Where(n => n.ProductName.Contains(Product) && n.Serial.Contains(searchString2));
//Saves the Data in DB NewProducts so you dont have to insert it again if the product doesnt exists
db.NewProducts.Add(new NewProduct { Product = Product, S_N = searchString2 });
db.SaveChanges();
ViewBag.ID = db.NewProducts.Max(d => d.id);
}
return View(newproducts.ToList());
}
But when i submit my form the whole content of the table(unfiltered) is shown. The submit has a male funktion after the first click and wont do anything at all. The table won't update after i inserted new values to the form.
Szenario 2
I found out that the e.preventDefault() is the one who stop the updating so i earased it and now my skript looks like this:
$(function () {
$("#divEdit").hide();
$("#btnEdit").click(function () {
$("#divEdit").show();
});
});
But that wasnt the solution for my problem. Now he updates my table, but
i can see the table just one second
when i use the submit the first time in the second he show me the whole table with the whole content(what is unfiltered) and i have to click it the second time to see my filtered content in the table
Do you know how to solve the problem?
Thanks in advance.
View:
#model IEnumerable<Escalationmanagementtool.Models.Product>
#section Scripts {
<script src="~/Scripts/GetCustomers.js" type="text/javascript"></script>
}
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm())
{
<p>
Product: #Html.DropDownList("Product", "Select Product")
Serial number: #Html.TextBox("SearchString2")
<input type="submit" value="Search" id ="btnEdit" runat="server"/></p>
}
<table id ="divEdit" runat="server" style= "display:none">
<tr>
<th>
#Html.DisplayNameFor(model => model.Customer.Name)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Customer.Name)
</td>
<td>
#Html.ActionLink("Choose", "Chosen", "New", new { id=item.Id, idc = item.Customer }, null)
</td>
</tr>
}
</table>
#Html.ActionLink("Search", "Search", "Add", new { ID = ViewBag.ID }, null)
the problem to above mentioned points could be as follows-
1.(i can see the table just one second)
here the page is refreshed again which executes the line -
$("#divEdit").hide();
so it hides the table after the line is executed and you see the table just for seconds before the line is executed.
2.(when i use the submit the first time in the second he show me the whole table with the whole content(what is unfiltered) and i have to click it the second time to see my filtered content in the table)
here when you submit the first time the line that is executed is-
$("#btnEdit").click(function () {
$("#divEdit").show();
});
so only table shows up and no form is submitted yet to filter the table, it filters only when you click it again. when you click second time the form is submitted to filter the table..
hope this help in solving your problem..
i found another solution with ajax to show the table first after clicking on submit. Therefor i used the tutorial from Azim Zahir.
You can find it on: http://www.codeproject.com/Tips/886473/Implementing-AJAX-in-ASP-NET-MVC .
After i got no helpful response here, i looked for other solutions without writing my own skript. Lesson learned: Don't fokus on one solution ;).
I hope it will help you to find a solution for your Webtool.

ASP.Net MVC Razor - Javascript function routing issue

I'm experiencing some issues with a cascade dropdownlist (ASP.Net MVC Razor), i know there are many questions answered on this topic but mine is quite different and i couldn't find an answer after hours of search.
I have my PessoaController (PersonController) which is inside an area named requerente.
I have two dropdownlists one for the provinces (Província) and other for the suburbs (Município).
Belown is the code i have on my view:
<div class="editor-label">
#Html.LabelFor(model => model.ProvinciaId, "Província")
</div>
<div class="editor-field">
#Html.DropDownListFor(Model => Model.ProvinciaId, new SelectList(ViewBag.ProvinciaId as System.Collections.IEnumerable, "id", "Valor"),
"Seleccione", new { id = "ddlProvincia" })
#Html.ValidationMessageFor(model => model.ProvinciaId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.MunicipioId, "Município")
</div>
<div class="editor-field">
#Html.DropDownListFor(Model => Model.MunicipioId, new SelectList(ViewBag.MunicipioId as System.Collections.IEnumerable, "Id", "Valor"),
"Seleccione", new { id = "ddlMunicipio" })
#Html.ValidationMessageFor(model => model.MunicipioId)
</div>
and this is the javascript code that calls the function on my controller
<script type="text/javascript">
$(document).ready(function () {
$("#ddlProvincia").change(function () {
var selectedProvinceId = $(this).val();
$.getJSON("../pessoa/LoadMunicipiosByProvinceId", { provinciaId: selectedProvinceId },
function (municipioData) {
var select = $("#ddlMunicipio");
select.empty();
select.append($('<option/>', {
value: 0,
text: "Escolha o Municipio"
}));
$.each(municipioData, function (index, itemData) {
select.append($('<option/>', {
value: itemData.Value,
text: itemData.Text
}));
});
});
});
});
</script>.
This code works perfectly (on the create view), when i want to add a new person i select the province and it loads the suburbs according to the selected province, so far so good.
To avoid having to duplicate the javascript function by copying the code to the edit view, i decided to move my javascript function to a partial view and include it in both the create and edit view.
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
#Html.Partial("LoadMunicipiosScript");
}
The code still runs perfectly on the create view, but on the edit view, whenever i select a different province i get a 404 error because the function cannot be found.
From firebug i can see what the problem is, my create view uses the right path to invoke the function in the controller.
localhost:57934/requerente/pessoa/LoadMunicipiosByProvinceId?provinciaId=200003
How ever on my edit view somehow the name of my controller is added twice to the path, and thus results in a 404.
localhost:57934/requerente/pessoa/pessoa/LoadMunicipiosByProvinceId?provinciaId=200003
I know that duplicating the code and tweaking the url would get me up and running but does anyone has any idea why the inconsistent behavior, having into count that both views (create and edit) are on the same folder and are executing the same shared function.
The problem looks like you have the same relative URL ../pessoa/LoadMunicipiosByProvinceId loading from two different pages which ends up referring to a different place in each. This would happen if, for example, your two URLs are (note the different base folder):
localhost:57394/requerente/foo
localhost:57394/requerente/pessao/bar
Instead, you can use Url.Action to dynamically generate the target URL. I do not know your routing scheme, but for example:
$.getJSON("#Url.Action("LoadMunicipiosByProvinceId", "pessoa")",
{ provinciaId: selectedProvinceId },
function(data) {/*...*/});

Categories