How to update innerhtml of multiple divs from C# code behind? - javascript

I managed to execute C# functions which returns a serialized string for a div's InnerHtml using unobtrusive approach.
<script>
$("#btnSubmit").click(function(){
bal.innerHTML = <%=ToInternalHTML()%>;
</scrtipt>
But there are multiple divs that needs innerhtml to be written based on the database data from code behind C#. So I want to use the same server-side function ToInternalHTML(). It's currently returning a string. But what I need is to write innerHtml to the specific divs from server-side in this function and execute this function from javascript.
foreach (DataRow row in table.Rows)
{
string rType = row[0].ToString();
sbhtml.Append(#"<div><a href=""#""class=""item""><p>");
sbhtml.Append(row[1].ToString());
sbhtml.Append("</p></a></div>");
switch (rType)
{
case "Clinical":
bal.InnerHtml = (new JavaScriptSerializer()).Serialize(sbhtml);
break;
case "Rehab":
rom.InnerHtml = (new JavaScriptSerializer()).Serialize(sbhtml);
break;
}
}
Can this be done? If so how to do so?

You cannot use server-side function to affect your page when it is already complete. Simply said, ASP.NET generates a page, sends it to your browsers and forgets about it.
If you want to fill a data in ASP.NET view's divs, you can do this in several ways.
During ASP.NET View generation
You don't need a StringBuilder or something else to form HTML elements. Use the following approach:
foreach (DataRow row in table.Rows)
{
<div>
<a href="#" class="item">
<p>row[1].ToString()</p>
</a>
</div>
/* Generate any divs here. Add any data from your table.Rows etc. */
}
Here is one, already generated. If you run this page you will as many divs with data as you have rows in table.Rows
Using Ajax.
// C# controller
public class MyController : Controller
{
public ActionResult Index()
{
return View();
}
public JsonResult GetData()
{
YourEntity[] data = GetFromSomewhere();
return new JsonResult(Data = data, JsonRequestBehaviour = JsonRequestBehaviour.AllowGet);
}
}
// JS script
$(document).ready(function()
{
$.ajax({
url: '#Url.Action("GetData")',
type: 'GET'
}).done(function(data){
$.each(data, function()
{
var yourEntity = this;
// Manipulate with DOM here. Change inner HTML of div or append it
var div = $("<div></div>");
$(div).text(yourEntity.Id);
$("body").append(div);
});
});

Related

Reload page based on a selection from drop down list MVC

I have a working solution, but I don't know how to reload the page after a certain ID is selected from the drop down list. My list is being populated from the DB. When I select it, I can see the ID and the corresponding data for it. However, there is no change on the screen.
Model class:
public List<Hello> getID()
{
var que = (from rel in db.Table1
select new Hello
{
ID = rel.R_ID
}).ToList();
return que;
}
public List<Hello> getStuff()
{
var que = (from wre in db.View
select new Hello
{
ID = wre.R_ID,
Summary = wre.Summary,
Description = wre.Description
}
}
getHello() is the same exact method as the getStuff(), just accepts a string ID parameter.
Controller class:
public ActionResult Index()
{
var model = test.getStuff();
ViewBag.IDs = new SelectList(test.getID(), "", "ID");
return View(model);
}
[HttpPost]
public JsonResult getDataBySelectedID(string selectedId)
{
var que = test.getHello(selectedId);
return Json(que, JsonRequestBehavior.AllowGet);
}
Partial_View Class:
<div class="container">
<table id="myTable" align="left">
<tr>
<th>#Html.DisplayNameFor(model => model.R_ID)</th>
<th>#Html.DisplayNameFor(model => model.Summary)</th>
<th>#Html.DisplayNameFor(model => model.Description)</th>
</tr>
#foreach (var item in Model)
{
<tr id="Home">
<td>#Html.DisplayFor(x => item.R_ID)</td>
<td>#Html.DisplayFor(x => item.Summary)</td>
<td>#Html.DisplayFor(x => item.Description)</td>
</tr>
}
</table>
</div>
View Class:
#Html.DropDownList("ID", ViewBag.IDs as SelectList)
<script>
$(document).ready(function () {
$("#ID").on("change", function () {
var selectedId = this.value;
var url = "/Sample/getDataBySelectedID";
$.ajax({
method: "POST",
dataType: "json",
url: url,
data: {
selectedId: selectedId
}
});
});
});
</script>
#foreach (var item in Model)
{
<tr>
<td>
#{Html.RenderPartial("Partial_Index", item);}
</td>
</tr>
}
How would I be able to reload the page with the selected value and its corresponding data?
Any help would be appreciated!
Thank you.
As described in the comments, you'll have to load your data into your page somehow. I recommend you do this through partial views.
1) Create a reusable Partial View
First, create a partial view that references your model. To do this, create a view as you normally would for your controller, and tick the "Partial View" option. Make sure to select your model in the model dropdown.
Then, move your .cshtml that references your model from your current view to your partial view. For example, if you have a table that lists out the fields of your model, you would cut the entire table into your partial view. You want to include the minimal amount of code needed in the partial view (aka, don't copy your entire view into it).
2) Reference the Partial View in your Current View
Now that you have your partial view set up, you should use it in your existing view to load the table. You should make sure this works first before continuing. You can use the #Html.RenderPartial(string ViewName, object Model) helper method to render it. Read more. So, you might place this line where your now-cut-code was: #RenderPartial("MyPartialView", model), where "MyPartialView" is the name of your partial view, and model is the model object that you want to pass into the partial view.
3) Add Methods to Render Partial View on Controller
At this point, you just need to be able to update your partial view after using AJAX. First, you need to add the ability to render the Partial View as a string so that you can easily inject it into your view. I recommend you do this by implementing a controller interface and letting your controller inherit the needed methods from that. In my programs, I have the following controller interface that my controllers inherit from:
public class IBaseController : Controller
{
internal string PartialViewToString(string partialViewName, object model = null)
{
ControllerContext controllerContext = new ControllerContext(Request.RequestContext, this);
return ViewToString(
controllerContext,
ViewEngines.Engines.FindPartialView(controllerContext, partialViewName) ?? throw new FileNotFoundException("Partial view cannot be found."),
model
);
}
protected string ViewToString(string viewName, object model = null)
{
ControllerContext controllerContext = new ControllerContext(Request.RequestContext, this);
return ViewToString(
controllerContext,
ViewEngines.Engines.FindView(controllerContext, viewName, null) ?? throw new FileNotFoundException("View cannot be found."),
model
);
}
protected string ViewToString(string viewName, string controllerName, string areaName, object model = null)
{
RouteData routeData = new RouteData();
routeData.Values.Add("controller", controllerName);
if (areaName != null)
{
routeData.Values.Add("Area", areaName);
routeData.DataTokens["area"] = areaName;
}
ControllerContext controllerContext = new ControllerContext(HttpContext, routeData, this);
return ViewToString(
controllerContext,
ViewEngines.Engines.FindView(controllerContext, viewName, null) ?? throw new FileNotFoundException("View cannot be found."),
model
);
}
private string ViewToString(ControllerContext controllerContext, ViewEngineResult viewEngineResult, object model)
{
using (StringWriter writer = new StringWriter())
{
ViewContext viewContext = new ViewContext(
ControllerContext,
viewEngineResult.View,
new ViewDataDictionary(model),
new TempDataDictionary(),
writer
);
viewEngineResult.View.Render(viewContext, writer);
return writer.ToString();
}
}
}
Then, on your controller you can inherit from this interface like so:
public class ExampleController : IBaseController
{
}
Now, you can use the new methods to easily render your partial view to a string.
In your getDataBySelectedID action, this is what you'll want to do.
[HttpPost]
public JsonResult getDataBySelectedID(string selectedId)
{
var que = test.getHello(selectedId);
string partialViewString = PartialViewToString("MyPartialView", que);
return Json(partialViewString, JsonRequestBehavior.AllowGet);
}
You may need to modify the above statement to fit your uses, but it should get you close.
4) Inject Partial View into Page on AJAX Success
Now, we've setup a Partial View to handle the model data that we want to update. We've updated our view to load from the Partial View by default. We've implemented a controller interface that will let use render that Partial View to a string so that we can inject it into our page. Now, we just need to do that injection.
First, wrap the previous setup #Html.RenderPartial() statement in a div. Let's give it the ID partialViewDiv. This will let us easily target it with jQuery.
$(document).ready(function () {
$("#ID").on("change", function () {
var selectedId = this.value;
var url = "/Sample/getDataBySelectedID";
var $partialViewDiv = $('#partialViewDiv');
$.ajax({
method: "POST",
dataType: "json",
url: url,
data: {
selectedId: selectedId
}
})
.done(function (response, status, jqxhr) {
$partialViewDiv.html(response);
// Do any other updates here.
})
.fail(function (reponse, status, error) {
$partialViewDiv.html('');
// Handle your error here.
});
});
});
Again, these is mostly pseudo-code so you may have to make some modifications. But, at this point, you should be roughly where you need to be. The AJAX call should update your view by reloading the partial view with your new model data.
Tips
Loading a partial view like this may break some jQuery event handlers, depending on your application. If that happens, take a look at this.
You can return any string from the Controller with your AJAX call. You could, if needed, return different partial views than what you originally loaded. Just use the methods from the interface and you can render whatever you need.
The above code is only some general guidelines. Without knowing your full implementation, I can't provide 100% working, bug free code. But, post here if you have any issues and I'll try to help.
A solution i see is with php
location.reload();
That is how you reload but if you want to reload with data you could use something like
window.location.replace("PathToThePage.php?YourDataName=YourData");

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>
}

Problems saving data into database SQL SERVER

I'm quite new here so if I do something wrong let me know, ok?
I'm quite new in web development as well.
I'm having a problem here with a post method in ASP.NET.
Please, don't mind the name of the buttons and methods, ok? I'm Brazilian and their names are all in portuguese.
I have a submit button that calls a ng-click (Angularjs) method called AdicionarCliente().
View
<div>
<input type="submit" class="btn btn-info" value="Salvar" ng-click="AdicionarCliente()"/>
</div>
JavaScript
myApp.controller('AdicionarClientesController', function ($scope, $http) {
$scope.NomeCliente = "";
$scope.Telefone1Cliente = "";
$scope.AdicionarCliente = function () {
var promisse = $http.post("/app/AdicionarCliente/", { NomeCliente: $scope.NomeCliente, Telefone1Cliente: $scope.Telefone1Cliente })
promisse.then(function () {
window.location.href = "CadastroPet";
return false;
});
};
It works well until this part. All the times that I hit the submit button, it comes here and enter the function in the variable "promisse".
Now - the problem is here:
Controller
[HttpPost]
public JsonResult AdicionarCliente(string NomeCliente, string Telefone1Cliente)
{
var db = new RexsoftEntities();
db.CLIENTES.Add(new CLIENTES() { NOME = NomeCliente,
TELEFONE1 = Telefone1Cliente});
db.SaveChanges();
var Clientes = db.CLIENTES.ToList();
return Json(Clientes, JsonRequestBehavior.AllowGet);
}
The first time that I hit the submit button, the code here goes until the db.CLIENTES.Add part of the code - then it doesn't run the DB.SAVECHANGES() nor the rest of the code here. The second time it works like a charm. The problems just happen on the first submit hit.
As the return of the controller doesn't happens properly, the final part of the Javascript code does not run as well. This part:
window.location.href = "CadastroPet";
return false;
Can anyone help me?
(All the view is inside this div
<div ng-controller="AdicionarClientesController">
)
UPDATE
I removed the TYPE of the submit button and put the simple button type. It seems to be working now. How can I submit my form then?
First,as per EF best practice, try to wrap the db operation in using() { } block. Thus your controller lokks like
[HttpPost]
public JsonResult AdicionarCliente(string NomeCliente, string Telefone1Cliente)
{
var Clientes = new CLIENTES();
using(var db = new RexsoftEntities())
{
var _Clientes = new CLIENTES()
{
NOME = NomeCliente,
TELEFONE1 = Telefone1Cliente
};
db.CLIENTES.Add(_Clientes);
db.SaveChanges();
Clientes = db.CLIENTES.ToList();
}
return Json(Clientes, JsonRequestBehavior.AllowGet);
}
Secondly, in javascript side, you are using angularjs. window.location.href will not work in angular(see this and this). You have to use $window service (source: using angularjs $window service) or $location service (source: using angularjs $location service). Also avoid using return false;.
In your case the below will work.
promisse.then(function () {
$location.path('/CadastroPet');
});
I removed the TYPE of the submit button and put the simple button type. It seems to be working now.
I created another way to validate my form using the same js script that I mentioned. If the criterias wasn't met, i would return a message and a return false statement.

use jquery variable in # block razor

I'm strugling with a jquery script inside a cshtml page. For short my question is how to use a var inside a # statement in a cshtml page?
below an example of what I'm trying:
<select id="DefaultText">
<option value="-1">-- select --</option>
#foreach( var d in Model.DefaultTexts )
{
<option value="#d.Id" >#d.Name</option>
}
</select>
<script type="text/javascript">
$('#DefaultText').change(function () {
var id = parseInt($('#DefaultText :selected').val());
var text = #Model.DefaultTexts.First( t => t.Id == id );
$('#CustomProductText').val(text);
});
</script>
I can't reach the var id. It's out of scope. I've also tryed it with a for loop and a if statement. But in the if statement I get the same error: out of scope.
The full story is this:
On my page I've a dropdown list. The items to select are short names for default text parts. Based on the id or name, I want to show the default text part in a textbox.
#CustomProductText is my textbox where the content should be placed (code not posted).
I've also tryed it with #: and statement but that did not work.
What am I doing wrong or maybe its not even possible what I'm trying to do.
As an alternative I've added a action to my controller to get the text form there. Below the code:
<script type="text/javascript">
$('#DefaultText').change(function () {
var id = parseInt($('#DefaultText :selected').val());
$.post("Categories/GetDefaultText", { Id: id }, function (data) {
alert(data);
});
//$('#CustomProductText').val(text);
});
</script>
controller code:
[HttpPost]
public ActionResult GetDefaultText(int id)
{
using( var context = new MyContext() )
{
var text = context.DefaultText.First( d => d.Id == id ).Text;
return this.Content( text );
}
}
This doesn't work. The action doesn't get hit in debug mode.
regards,
Daniel
The $.post that is not working for you, you should prefix the url with / sign and it will be hit as expected:
$.post("/Categories/GetDefaultText", { Id: id }, function (data) {
alert(data);
});
As for the razor solution, you can't use javascript variables in the razor code as it's not a scripting language. What razor does is simply rendering the strings (be it html or javascript or anything) into the page.
To do what you want you either need to request the server to pass the text to your page or render all the texts you have in the page and then access this rendered content in your javascript.

Categories