Keeping model while navigation using angularjs - asp.net-mvc

I just started to learn angularjs and I have a question:
I am trying to create a view with a table of entities with an update button. When the Upadte button is pressed I want to navigate to another view to edit the entity.
In my main page I used this to load my different view into:
<div ng-view></div>
I used ASP.NET MVC to create partial views for the list of entites and the update form.
These two partial views use the same angular controller.
To display the list:
<div ng-controller="CompaniesController">
<table id="tblCompaniesCollection">
<thead>
<tr>
<th>Name</th>
...
</tr>
</thead>
<tbody ng-repeat="Company in Companies">
<tr>
<td><span>{{Company.Name}}</span></td>
...
<td>
<button type="button" ng-click="ChangeView(Company)">Go to edit</button>
</td>
</tr>
</tbody>
</table>
</div>
To update:
<div ng-controller="CompaniesController">
<table id="tblCompanyToEdit">
<tbody >
<tr >
<td>Name</td>
<td><span>{{CompanyToEdit.Name}}</span></td>
</tr>
...
</tbody>
</table>
</div>
In the ChangeView(Company) function I do the following:
$scope.ChangeView = function (Company) {
$scope.CompanyToEdit = Company
$location.path('/EditCompany');
};
What I tried to achieve:
Have a simple partial view for the view of list of entites and the
update view.
After the view of list of entites is loaded into it's place, it
gets the data from the same web application via WEB API
controller.
It is working till this point.
What is not working:
When I click the button store the selected entity in the
$scope.CompanyToEdit and load it into the view.
This way I would not have to create another request to the server to
get the company to edit, but the angular controller recreated on
navigating and I lost my data.
How do you solve this wiht angular? Please tell me if my aproach is not good.

You cannot achieve that with the same controller.
The reason is that when changing views, and I assume you're changing states, the controller is loaded again and is going through an initialization process.
Meaning that $scope.CompanyToEdit won't have a value and will be undefined, unless you initialize that in the controller.
What you need to do, is use a service to keep the data and use the controller to access that service. The service will keep the value since while you're navigating in the same SPA, services exist as singletons.
Inject the service to the controller and access the data there.

Related

How to load another view as PartialView when the current View loads?

I am relatively new to this. I am working on this ASP.NET Core MVC project where I want to load a PartialView, HistoryTable.cshtml in a <div> in the current Main View when the Main View, Locate.cshtml loads. In other words, I want the PartialView to be there whenever the MainView loads/reloads.
I am implementing it in the following way:
Locate.cshtml
#model Project.Models.CustomModels.LocationsHistoryViewModel
<div class="container">
...
<div id="divLocationsHistoryTable"></div>
...
</div>
#section Scripts {
<script type="text/javascript">
$(document).ready(function () {
$(".disabledropdowncntrl").prop('disabled', false).trigger("chosen:updated");
$("#divLocationsHistoryTable").load("/Project/HistoryTable");
});
HistoryTable.cshtml
#model IEnumerable<Project.Models.CustomModels.LocationsHistoryViewModel>
<div class="card">
...
#if(Model.Count() != 0)
{
<thead>
<tr>
<th data-column-id="UserId" data-type="string" data-identifier="true" hidden>User ID</th>
<th data-column-id="Name">Name</th>
...
</tr>
</thead>
}
else
{
...
}
<tbody>
#foreach(var item in Model)
{
<tr>
<td>
#item.User.FirstName #item.User.LastName
</td>
...
</tr>
}
</tbody>
</div>
Controller: Locate ActionMethod
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Locate(int Id, InventoryLocationsHistoryViewModel locationsHistoryVM)
{
...
var HistoryObject = _context.History
.Include(...
.Where(...
.Select(...
{
...
}).ToList();
return PartialView("/Project/HistoryTable", HistoryObject);
}
What to do?
EDIT
The issue that I am facing is similar to this question, however, the difference is that I HAVE to use Submit button here as I am submitting form here. The Submit button has to do two things when clicked:
Save the form details into the database.
Update the HistoryTable that has to be displayed below the Submit button always. This HistoryTable has to be shown at all times (even before clicking Submit). Thus, I cannot use Button.
There's a number of issues here, making it a little difficult to figure out what it is you're actually trying to achieve.
First and foremost, your Locate action should return your Locate.cshtml view, on both GET and POST. The fact that that view includes your HistoryTable.cshtml partial view is an implementation detail. If you only return the partial on POST, you'll only have the partial HTML in the browser, not the full Locate view.
Then, it appears that you're attempting to use jQuery's load method to load your actual partial, not an action that returns that partial. You can't get the view directly; you need to submit an AJAX request (what load is doing) to a route tied to an action that returns that partial.
Next, it looks like your partial needs a payload, i.e. some object needs to be "posted" for it to work with. If that's the case, you need to pass a JavaScript object representation of what you need to post to the load method as its second parameter. As it is, it's simply going to issue a GET request directly, passing no data.
However, since you're only doing this on page load, it calls into question why you're using AJAX at all. AJAX only makes sense if you need to change something later, after page load. If you're doing an AJAX request on load, it should simply be built into the page from the start, negating the need for a separate request.
Lastly, while you can pass a model to a partial view when you include it in a page, more likely than not what you're actually looking for here is a view component, i.e. something capable of actually doing logic like querying from a database, separate from the main request in play.

Grails pass parameter from loop to controller

In Grails I'm trying to delete a record from a (HTML) table, I want to achieve this by passing the ID of the object to the controller. So there is a table in HTML and the user clicks "delete" and the item is deleted from the database. (This logic works according to the unit tests, the problem is passing the ID from view -> controller)
I have tried several solutions found on here such as
<g:each in="${recipe}" var="item">
<tr>
<td>
${item.recipeName}
</td>
<td>
${item.people}
</td>
<td>
<g:link controller="UserRecipe" action="delete" params="[id: '${item.id}']" class="class">Info to Display</g:link></tr>
</g:each>
Which doesn't crash but calls the controller correctly, though the value of the id = null. When I manually edit the url and add /4 for example, the ID will be four. Several solutions on here showed how to pass parameters, but the error arrises when I try to pass a parameter which I access using the ${} notation with these solutions, e.g. using createLink and a params list, it will crash the application because it's a nested ${${}} it seems.
You need to change your params to:
params="[id: item.id]"

ASP.net MVC DisplayNameFor nested model in view

I have a view in my ASP.net MVC project which is associated to a controller (SubmissionController) and class (Submission).
In the view, I am displaying some values from the class using:
#Html.DisplayFor(Function(model) model.Created)
This works perfectly, but what I want to achieve now, is to be able to get the display name of a Collection nested within the model I reference above.
To make that clearer; I am viewing a single Submission of many Submissions in the view (using the Details function of the SubmissionController). Each submission has many Photos, and it is these photos I want to display in the Submission details view along with the submission details themselves.
To display the photos, I have the following piece of code creating a table of photos...
<tbody>
#For Each item In Model.Photos
Dim currentItem = item
#<tr>
<td>
#Html.DisplayFor(Function(modelItem) currentItem.Photo_ID)
</td>
<td>
#Html.DisplayFor(Function(modelItem) currentItem.Photo_Status1.Value)
</td>
<td>
#Html.DisplayFor(Function(modelItem) currentItem.Photo_Size)
</td>
<td>
#Html.DisplayFor(Function(modelItem) currentItem.Photo_Taken)
</td>
</tr>
Next
</tbody>
Whilst untested, I believe the above code should work just fine.
With this in mind, How on earth do I get the DisplayNameFor values for Photo fields/properties?
This apparently, does not work!
#Html.DisplayNameFor(Function(model) model.Photos.Photo_Size)
By the way, I don't want to write in the header names manually, they must come from the model.
Answer
DigitalD's answer was almost there, here is the solution.
#Dim photoModel = New Photo()
#Html.DisplayNameFor(Function(photoModelx) photoModel.Photo_ID)
#Html.DisplayNameFor(Function(model) model.Photos.Photo_Size) won't work because model.Photos is likely an IEnumerable and doesn't have a Photo_Size property. You might get away with passing in an empty model item like so:
#Dim displayModel = new Photo()
#Html.DisplayNameFor(Function(displayModel) displayModel.Photo_Size)
etc.
If I am understanding correctly, you are trying to get the name of the field you are showing ("Photo Taken", "Photo Size", etc)? If this is the case, I would suggest using DataAnnotations and LabelFor(item=>item.Field). This will give you what you are looking for I believe.

Name 'Model' is not declared in a strongly typed view - ASPNET MVC

As of recent, a few of my strongly typed views randomly (w/ zero code changes) decided that 'Model' was not a valid item ... again - ZERO code changes. I simply opened my view and now it's broken ... so logically I deleted the view and created a new one - still broken. Has anyone else come across this issue using MVC? A simple example is below
<%# Control Language="VB" Inherits="System.Web.Mvc.ViewUserControl(Of IEnumerable (Of Project.Library.Entity.User))" %>
<table id="tblUser" cellspacing="0" cellpadding="0" border="0">
<tr>
<th>Username</th>
</tr>
<% For Each item In Model%>
<tr>
<td>
<%=Html.Encode(item.Username)%>
</td>
</tr>
<% Next%>
</table>
The actual error shows up in the view - under the word "Model" is a red line and when I hover over this (in any of my partial views - strongly typed) it says "Name 'Model' is not declared"
'Model' is a property of the ViewUserControl class, so when it's marked as not declared this may mean the following:
There is no such property in ViewUserControl. This can be when an old version of System.Web.Mvc is used. To check this just try to delete the reference to System.Web.Mvc, and add it again and run the application. If the error in editor still exists, see case 2.
Some of the plugins think that there is no such method. Or aliens hacked your VS. You can try to reload the VS or to repair it and after repair ASP.NET MVC.
Hope this will help.

Parsing Form Post Values From a Table in ASP.NET MVC?

Ok, I'm an MVC newbie coming from a webforms background, so please excuse any ignorance here. Here is my scenario. I've got a table consisting of a list of applications and associated permissions. Each table row consists of 3 pieces of information: a checkbox, some text describing the row, and a dropdown list allowing the user to select the appropriate permission for the application. I want to post this data and only work with the rows in the table which were checked (the id of the row is embedded as the checkbox name). From there, I want to grab the selected value from the DropDownList, and call the necessary code to update the DB. Here is my View page's code:
<%foreach (var app in newApps)
{ %>
<tr>
<td><input type="checkbox" name="AddApps" value="<%=app.ApplicationId %>" /></td>
<td><%=Html.Encode(app.ApplicationName)%></td>
<td><%=Html.DropDownList("AppRole", new SelectList(app.Roles, "RoleId", "RoleDescription"))%></td>
</tr>
<%} %>
How would I retrieve the appropriate values from the FormCollection when I get to the controller on form post? I have done this in the past when I only had checkbox values to retrieve by just calling Request.Form["CheckBoxName"] and parsing the string.
Or am I going about this entirely wrong?
You are halfway right in order to post your data that the controller can read the info it must be inside a form as so :
<% using(Html.BeginForm("Retrieve", "Home")) %>//Retrieve is the name of the action while Home is the name of the controller
<% { %>
<%foreach (var app in newApps) { %>
<tr>
<td><%=Html.CheckBox(""+app.ApplicationId )%></td>
<td><%=Html.Encode(app.ApplicationName)%></td>
<td><%=Html.DropDownList("AppRole", new SelectList(app.Roles, "RoleId", "RoleDescription"))%></td>
</tr>
<%} %>
<input type"submit"/>
<% } %>
and on your controller :
public ActionResult Retrieve()
{
//since all variables are dynamically bound you must load your DB into strings in a for loop as so:
List<app>=newApps;
for(int i=0; i<app.Count;i++)
{
var checkobx=Request.Form[""+app[i].ApplicationId];
// the reason you check for false because the Html checkbox helper does some kind of freaky thing for value true: it makes the string read "true, false"
if(checkbox!="false")
{
//etc...almost same for other parameters you want that are in thr form
}
}
//of course return your view
return View("Index");//this vaires by the name of your view ex: if Index.aspx
}
This site gives more details on how to handle the dropdownlist helper:
http://quickstarts.asp.net/previews/mvc/mvc_HowToRenderFormUsingHtmlHelpers.htm
Request.Form will still work, except that your checkboxes all have the same name.
So one way would be to give the checkboxes distinct names, e.g. "AddApps-app.id", and use Request.Form.
However, a more elegant and testable way is to use list binding.
In this model, you give your form elements a certain structured name, and the default model binder will wrap each set of form elements into a list of typed records in the controller. This is fully explained in this blog post.
The advantage here is that your controller deals only with instances of the application type, and hence has no implicit dependency on the way the view is structured. Therefore, it is very easy to unit test.

Resources