MVC pass model between Parent and Child Window - javascript

Thanks in advance. Please excuse me for my grammer. I tried my best to explain my issue In my quest of solving below question I started to develop a POC first.
C# MVC No submit pass object between views
I am having an issue using TempData object and pass my model between my parent popup and child popup. My problem is I am doing TempData["StudentViewModel"] 2 times. First time insert and First time read are good but Second time read even though I make sure that I insert second time before read is not working.
I will try my best to explain it clearly.
I have a ASP.NET page called Class.cshtml. It will have a grid of all class. The user will select a ClassName column and it opens up Students.cshtml as a new popup window which has a grid with StudentName and Address columns. The user will select StudentName and it opens another popup window called StudentDetails.cshtml.
We have a ClassController.cs which is used by all popups and have C# methods. ClassController.js has all javscript code.
public ActionResult GetStudentsDetails()
{
// this will create students for each class.
//Inside student for each class it will also create Student Details.
// First Insert
TempData["StudentViewModel"] = studentViewModel;
return View("Students", studentViewModel);
}
Students.cshtml is an existing popup window like below
<div>
//this is in for loop
string anchorElementId = string.Format("AnchorElementId_{0}", i);
string selectedIndex = i.ToString();
string name = Model.Students[i].Name;
<input class="ButtonLikeHyperLink"
id="myBtnId"
onclick="ShowStudentDetails(#selectedIndex, '#name', '#anchorElementId')"
value="#Model.Students[i].Name"
type="button"/>
//for loop ends here
//First read
<span id="lblHDNStudentViewModel">
#Newtonsoft.Json.JsonConvert.SerializeObject(TempData["StudentViewModel"] as StudentViewModel)
</span>
</div>
Once user selects any StudentName in Students.cshtml popup the below js method is called which opens a Child window popup having particular student details.
ClassController.js
function ShowStudentDetails(selectedIndex, name, anchorElementId)
{
var inputParam = {};
var hiddenField = document.getElementById("lblHDNStudentViewModel");
if (hiddenField != null)
{
inputParam.StudentVM = JSON.parse(hiddenField.innerText);
inputParam.selectedIndex = selectedIndex;
inputParam.name = name;
inputParam.anchorElementId = anchorElementId;
// __callback is our custom method to call controller action method
var retVal = __callback("OnNameSelected", inputParam);
var postedData = JSON.parse(retVal.return_value);
if (postedData.Success == true)
{
// i need to do like below since Model to my popup is dynamic
multipleMatchPopup = window.open('', '', properties);
multipleMatchPopup.document.write(postedData.PartialViewHtml);
}
}
}
ClassController.cs
public JsonResult OnNameSelected(StudentViewModel StudentVM, int selectedIndex, string name, string anchorElementId)
{
// this will create student name details viewmodel for selected name and modify StudentViewModel object.
// for example
StudentDetailsViewModel vm = StudentVM[selectedIndex].DetailsVM;
//since user made selection update few properties in vm
StudentVM[selectedIndex].DetailsVM = vm;
//Second insert
// make sure to set tempdata before RenderPartialViewToString
TempData["StudentViewModel"] = StudentVM;
string sHtml = this.RenderPartialViewToString("~/Views/_PartialStudentDetailsPopup.cshtml", vm);
return Json(new
{
Success = true,
data = StudentVM,
PartialViewHtml = sHtml,
JsonRequestBehavior.AllowGet
});
}
In StudentDetails.cshtml popup I have like this
<div>
.....
<input class="ButtonLikeHyperLink"
id="#buttonId"
onclick="OnUserSelectStudentDetails()"
value="[Select]"
type="button" />
//Second read
//in fiddler innertext is show as null
<span id="lblHDNStudentDetailsViewModel">
#Newtonsoft.Json.JsonConvert.SerializeObject(TempData["StudentViewModel"] as StudentViewModel)
</span>
</div>
ClassController.js
function OnUserSelectStudentDetails()
{
var inputParam = {};
var hiddenField = document.getElementById("lblHDNStudentDetailsViewModel");
if (hiddenField != null)
{
//hiddenField.innerText is null
inputParam.StudentVM = JSON.parse(hiddenField.innerText);
var retVal = __FAFdoCallback("OnUserSelectLenderMatchingFee", inputParam);
...
}
}
ClassController.cs
public JsonResult OnUserSelectLenderMatchingFee(StudentViewModel StudentVM)
{
//StudentVM is null here
}
UPDATE
SOLUTION
I feel real stupid on this issue. As the great detective, Hercule Poirot said, "The great gray cells are not working", mine also did not work
in this case. Sometimes we think so far away from the box that we oversee the basics. I was thinking that this thing cannot be done in so simple so I was thinking about TempData and so on and forgot the fundamental point that my parent popup already have a hidden field and I can read from it and write to it it in my javascript methods of parent and child popups windows and pass it to controller action methods and get back updated and consistent model back.
Taking this basic solution this is what I did
Students.cshtml
<span id="lblHDNStudentViewModel">
#Newtonsoft.Json.JsonConvert.SerializeObject(Model)
</span>
Read this in parent window javascript method like below in ClassController.js
function ShowStudentDetails(selectedIndex, name, anchorElementId)
{
var inputParam = {};
//read
var hiddenField = document.getElementById("lblHDNStudentViewModel");
}
Read this from child window javascript method like below in ClassController.js
function OnUserSelectStudentDetails()
{
var inputParam = {};
// read in child window and access parent window element
var hiddenField = window.opener.document.getElementById("lblHDNStudentViewModel");
}
Write back to parent window element from parent window javascript method like below
document.getElementById("lblHdnCDLenderViewModel").innerText = JSON.stringify(postedData.data);
Write back to parent window element from child window javascript method like below
window.opener.document.getElementById("lblHdnCDLenderViewModel").innerText = JSON.stringify(postedData.data);

Related

How to know/capture the Detail Grid ID of the specific detail grid you are in? (ag-grid javascript)

I have a Master-Detail ag-grid. One column has checkboxes, (checkboxSelection: true). The details grid have a custom status panel with a button. When the user clicks the button in any specific Detail grid, I don't know how to get the SelectedRows from just that one specific detail grid.
The problem is they might leave multiple details displayed/open, and then looping over each Detail Grid will include results from all open grids. I'm trying to isolate to just the grid where the user clicked the button.
I tried looping through all displayed/open detail grids to get the Detail grid ID. But I don't see any info in this that shows me which one they clicked the button in.
I tried in the button component to see if, in the params, there is anything referencing the detailgrid ID that the button is in, but I did not see anything there either.
This is the button component:
function ClickableStatusBarComponent() {}
ClickableStatusBarComponent.prototype.init = function(params)
{
this.params = params;
this.eGui = document.createElement('div');
this.eGui.className = 'ag-name-value';
this.eButton = document.createElement('button');
this.buttonListener = this.onButtonClicked.bind(this);
this.eButton.addEventListener("click", this.buttonListener);
this.eButton.innerHTML = 'Cancel Selected Records <em class="fas fa-check" aria-hidden="true"></em>';
console.log(this.params);
this.eGui.appendChild(this.eButton);
};
ClickableStatusBarComponent.prototype.getGui = function()
{
return this.eGui;
};
ClickableStatusBarComponent.prototype.destroy = function()
{
this.eButton.removeEventListener("click", this.buttonListener);
};
ClickableStatusBarComponent.prototype.onButtonClicked = function()
{
getSelectedRows();
};
Here is the code to loop through and find all open detail grids:
function getSelectedRows()
{
this.gridOptions.api.forEachDetailGridInfo(function(detailGridApi) {
console.log(detailGridApi.id);
});
I was able to work this out, so thought I'd post my answer in case others have the same issue. I'm not sure I took the best approach, but it's seemingly working as I need.
First, I also tried using a custom detail cell renderer, as per the documentation, but ultimately had the same issue. I was able to retrieve the DetailGridID in the detail onGridReady function--but couldn't figure out how to use that variable elsewhere.
So I went back to the code posted above, and when the button was clicked, I do a jquery .closest to find the nearest div with a row-id attribute (which represents the the DetailgridID), then I use that specific ID to get the rows selected in just that detail grid.
Updated button click code:
ClickableStatusBarComponent.prototype.onButtonClicked = function()
{
getSelectedRows(this);
};
Updated getSelectedRow function:
function getSelectedRows(clickedBtn)
{
var detailGridID = $(clickedBtn.eButton).closest('div[row-id]').attr('row-id');
var detailGridInfo = gridOptions.api.getDetailGridInfo(detailGridID);
const selectedNodes = detailGridInfo.api.getSelectedNodes()
const selectedData = selectedNodes.map( function(node) { return node.data })
const selectedDataStringPresentation = selectedData.map( function(node) {return node.UniqueID}).join(', ')
console.log(selectedDataStringPresentation);
}

How to Retrieve Specific Data in Firebase Using Table Cells?

I'm kinda new to Javascript so please don't bite. I wrote simple function that displays my Firebase users in table, I'm using Jquery to create this, so far my code looks like that:
userRef.on("child_added", snap => {
var username = snap.child("name").child("name").val();
var surname = snap.child("name").child("surname").val();
$("#user_table").append("<tr><td>"+ username +"</td></tr>")
});
now i want to display each users data by clicking on his nickname in table, im doing it like that :
$("#user_table").on('click', 'td', function() {
userRef.on("child_added", snap =>{
cosValue = snap.child("cos").child("cos").val();
alert(cosValue);
})
});
and it displays all of my users data.. not this specific one that i clicked on, how can i solve that ? :)
userRef.on("child_added", snap => {
var username = snap.child("name").child("name").val();
var surname = snap.child("name").child("surname").val();
var cosValue = snap.child("cos").child("cos").val();
$("#user_table").append("<tr onClick=\"displayCos(" + cosValue + ")\"><td>"+ username +"</td></tr>")
});
function displayCos(cos) {
alert(cos); //or any other action, e.g. open a PopUp window, display it in another div or table, etc.
}
You don't need the second .on("child_added",...
Also, you may do that with an onClick listener, instead of coding that inline in the tr tag (see https://www.w3schools.com/js/js_htmldom_eventlistener.asp).

SAPUI5 TreeTable's getRows method returns empty array on the first call

I am trying to build an SAPUI5 application using TreeTable and I'm facing some problems to use its methods.
In my app, I have a button which triggers this method.
onChangeViewContext: function(oEvent) {
.........
.........
var aViewContext = oContext.oModel.getProperty(sPath + "/ViewContext");
var aDataModel = oContext.oModel.getProperty("/ApplicationCollection/" + sAppId + "/DataModel");
var oStructure = this._createParentChildStructure(aDataModel);
var oTreeModel = this.getView().getModel("treeModel");
oTreeModel.setData(oStructure);
this._oViewDetailLine = oSource.getParent().getParent().getParent();
this._oViewDetailLine.setVisible(false);
this.byId("idSelectElementsPanel").setVisible(true);
this._setSelectedItems(aViewContext, oTree);
}
What I'm trying to do here is only bind the rows with my treeModel, get tree table object and send it to my _setSelectedItems method which below.
_setSelectedItems: function(aViewContext, oTree) {
oTree.clearSelection();
var sElementName;
var aSelectedIndices = [];
var aElements = [];
var aRows = oTree.getRows();
aRows.forEach(function(row) {
if (row._oNodeState !== undefined) {
aElements.push(row.getCells()[0].getText());
}
});
I need to get rows array here because I will use it for setting selected items of tree table. The problem is when "onChangeViewContext" triggered, oTable.getRows() returns an empty array. But when I click cancel button (which just hides my tree table, nothing more) and then trigger "onChangeViewContext" function again, I can get the rows array completely.
Even on the first call when I try to get table's model, I can get the treeModel and its data correctly.
I've tried to refresh bindings, aggregations etc. But no luck.
By the way, I'm using row binding in my xml view like this :
<t:TreeTable id="idSelectElementsTree" rows="{path: 'treeModel>/'}" selectionMode="MultiToggle" enableSelectAll="false"
rowSelectionChange="onSelectElement">
I'm really drowning here so any any help would be appreciated.
Edit : rest of the setSelectedIndexes function :
aViewContext.forEach(function(name) {
sElementName = name;
if (aElements.indexOf(sElementName) !== -1) {
aSelectedIndices.push(aElements.indexOf(sElementName));
}
});
aSelectedIndices.forEach(function(idx) {
if (oTree.getRows()[idx]._bHasChildren) {
oTree.expand(idx);
}
oTree.addSelectionInterval(idx, idx);
});
What could help here is to add an event rowsUpdated="onRowsUpdated" to the table in the XML view. This event is triggered after the table has been loaded and will hence provide you with the data via;
this.getView().byId("sTableId").getRows();
The difference to your approach is that the event would not be triggered by the press of a button but automatically, as the table is rendered. You can then also use this function to trigger another one as per your use case.

How to make HTML helper method fire eveytime partial view is rendered

I have a view ( for creating a recipe ) to which I dynamically add a partial view ( representing products ). There can be several products added to recipe. Partial view is added on button click, using jQuery, and this works fine:
$('.loadPartial').on('click', function (evt) {
evt.preventDefault();
evt.stopPropagation();
var $productsDiv = $('#productsDiv'),
url = $(this).data('url');
$.get(url, function (data) {
$productsDiv.append(data);
});
});
Partial view has a combo. Name of this combo is generated dynamically using Html helper extension method 'GetIndexedName' which adds unique index to specified name. If I click button twice it should render two partial views, after first click combo name should be "ProductsCombo0", after the second "ProductsCombo1"
#Html.DropDownList(
#Html.GetIndexedName("ProductsCombo"),
null,
htmlAttributes: new { #class = "form-control col-md-3" }
)
The problem is that the method #Html.GetIndexedName fires only after the first button click ( checked in debugger ). Next clicks only render partial view, but do use the method to generate name. All combos have name "ProductsCombo0", "ProductsCombo0"
Do you know how to make it fire everytime partial view is rendered?
If it can`t be done this way could you recommend me some other solution for generating unique ids?
Because every time you make the ajax call for a new row, It is a totally separate http call and this call does not have any information whether you are making the call for the first or eighth row. You need to store this value(which row you are making this call for) and use that when you build the dropdown name.
So first update your action method to accept a row index value.
public ActionResult AddRow(int id)
{
var vm = new AddItemVm {RowIndex = id};
vm.Items = new List<SelectListItem>()
{
new SelectListItem {Value = "1", Text = "IPhone"}
};
return PartialView("_NewItem", vm);
}
I am having small view model to pass the data between my action method and the partial view, which looks like,
public class AddItemVm
{
public int RowIndex { set; get; }
public List<SelectListItem> Items { set; get; }
}
And in the partial view, which is strongly typed to our view model, we will read the Model.RowIndex property and use that to build the dropdown name/id.
#model YourNamespaceHere.AddItemVm
<div>
#Html.DropDownList("ProductsCombo"+Model.RowIndex, Model.Items)
</div>
Now we need to make sure that we are sending a unique row index value to our AddRow method every time we want to add a new row. You can keep a javascript variable and increment it's value everytime user tries to add a new row and use that value.
$(function () {
var rowIndex = 0;
$('.loadPartial').on('click', function (evt) {
evt.preventDefault();
evt.stopPropagation();
rowIndex++;
var $productsDiv = $('#productsDiv'),
url = $(this).data('url') + '/' + rowIndex;
$.get(url, function (data) {
$productsDiv.append(data);
});
});
});
I finally found a solution to the problem. Data from AJAX call was cached and that is why function was called only once. All I had to do was to disable it. So I changed jQuery 'get' mehtod to 'post' which ( what i know ony now ) is not cached.

How to get whole object as an response after click?

I have simple JavaScript snippet:
var obrazek = [{nazwa: "Sniadanie", wiek: 100, autor: "Alicja"},{nazwa: "Kolacja", wiek: 10, autor: "Misiek"}];
function galeria(nazwa, wsad) {
this.nazwa = nazwa;
this.wsad = wsad;
this.print = function(element) {
for (var i=0;i<this.wsad.length;i++) {
var text = "<li>"+this.wsad[i].nazwa+"</li>"
element.append(text);
}
}
}
$(document).ready(function() {
gal = new galeria('test', obrazek);
gal.print($('#galeriaTest'))
});
It gives me:
<ul id="galeriaTest>
<li>Sniadanie</li>
<li>Kolacja</li>
</ul>
What I want is simple method that will return object after click event:
Object { nazwa="Sniadanie", wiek=100, autor="Alicja"} (in FireBug)
How to code it?
As long as your data set is static, you can just associate the object to the DOM element using the data() function.
Here's an example.
If your data set is dynamic, you could still associate a reference to the Galeria and some ID type of information to get a similar albeit improved result.
$("selector").on('click', function(e){
console.log( obrazek ); // would put object in a console, you can check it via firebug
});

Categories