ASP.NET MVC3 passing data between primary view and a search view via Ajax link - asp.net-mvc-3

I have just started learning ASP.NET MVC3.
I have the following scenario. In a create view for a certain model the user can lookup code/description by clicking on a link (rendered with Html.ActionLink helpers). The lookup values are retrieved from lookup tables in a database and presented in a separate view. The two views are handled by two different controllers. When the user selects a lookup value in the latter view that value (code+description) should be copied back to the create view.
How can data be passed between the two views? Is this not possible due to the stateless nature of Http requests?
I tried that with an Ajax link, but it didn't worked out.
code snippet Create view:
<fieldset>
<legend>Z-Info</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ZZL_U_CODE)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ZZL_U_CODE)
#Html.ValidationMessageFor(model => model.ZZL_U_CODE)
</div>
<div class="editor-label">
#Ajax.ActionLink("Land code test", "Index", "Domein", new {name = "lan" },
new AjaxOptions {
HttpMethod = "Get",
Url = Url.Action("Index", "Domein", new {name = "lan" }),
OnBegin = "OnBegin",
OnSuccess = "InsertCodeNaam",
OnFailure = "OnFailure",
OnComplete = "OnComplete"
})
</div>
When the user select a code/description the following Select action is called which returns Json data back.
Select action:
public class DomeinController : Controller
{
private ZZLEntities db = new ZZLEntities();
//
// GET: /Domein/
public ViewResult Index(string name)
{
DomeinViewModel model = DomeinRepositry.GetAll(name);
return View(model);
}
GET: /Domein/Select/5
public JsonResult Select(int id, string naam)
{
return Json(new DomCodeNaam { codeValue = id, naamValue = naam }, JsonRequestBehavior.AllowGet);
}
Are there other solutions possible? Can partial views be an option?

Well you have two options:
Just post back lookup values and then internally redirect to the
first ("create") view, but this time passing (internally) the values
chosen by user so the view can be rendered with chosen values. Maybe
not fancy but very easy to implement. You will loose data that user have already entered into first form though, unless you post it too or you make this a 2 step process.
If you want to use Ajax, you need update appropriate parts of the
form in the first "create" view on the client side, depending on the
actions of user (i.e. what lookup values they have chosen).
I am however a bit confused with what you exactly mean by "separate view"

Related

MVC Razor View update Form on SelectedIndexChange

I have a form in a View that brings together a number of pieces of information (address, telephone etc). All these elements are wrapped up in a view model. There is one section that asks the user to select a county. On selection, I want to be able to show a price based on the county selected.
I came across the following SO question which is close to what I want, but it looks like the action submits the form to a 'change controller'. I naively need to be able to basically call two controllers - one onSelectedChange and the other onSubmit. I'm pretty sure ya can't do this!
Here' what I'm after:
#model ViewOrder
#using (Html.BeginForm("Order", "Home"))
{
#* - textboxes et al - *#
<p>
#Html.DropDownListFor(x => x.Counties,
new SelectList(Model.Counties, "CountyId", "County"),
new { #class = "form-control input-sm" })
</p>
<p>
#* - £Price result of dropdown list selection and
add to View Model to add to sub total - *#
</p>
<input type="submit" text = "submit"/>
}
I'm very new to MVC - Could do this easily in webforms (but I'm sticking with MVC!) There must be some form of Ajax action that would allow this. Any suggestions?
First you have a problem with you #Html.DropDownListFor() method. Model.Counties is a complex object (with properties CountyId and County) but you cannot bind a <select> (or any control) to a complex object, only a value type. Your model needs a property (say) public int SelectedCountry { get; set; } and then #Html.DropDownListFor(x => x.SelectedCountry, new SelectList(Model.Counties, "CountyId", "County"), ...)
To display the price, you need to handle the .change event of the dropdown, pass the selected value to a controller method, and update the DOM.
Script (based on the property being SelectedCountry)
var url = '#Url.Action("GetPrice", "yourControllerName")';
$('#SelectedCountry').change(function() {
$.getJSON(url, { ID: $(this).val() }, function(data) {
// do something with the data returned by the method, for example
$('#someElement').text(data);
});
});
Controller
public JsonResult GetPrice(int ID)
{
// ID contains the value of the selected country
var data = "some price to return";
return Json(data, JsonRequestBehavior.AllowGet);
}

MVC3 Nesting Partial Views with a call to Ajax.ActionLink

I am aware of the previous two questions which talk about nesting partial views but the solutions don't work for my design (which might not be the best one but I'm unsure how to adapt it).
Background:
I collect questionnaire responses from users and store them on an sql server as xml files.
I have a partial view which loads a table with all the Responses of a given user, this partialview populates the table with things like Response date, link to xml response document, questionnaire name, link to xml questionnaire document (the questionnaire info is pulled from a different table) and an Ajax ActionLink which redirects to action which parses the two relevant xml documents to print out Question and Answer list (i.e. visualise the response to be human readable) inside the second partial view.
The first partial view contains a div underneath the table which I wish to populate onclick of the Ajax.ActionLink with the second partial view.
Problem:
The answers are rendered correctly however the partial view is loaded into a whole new page, without any styling.
The other solutions to this nesting problem use RenderPartial() however I use return PartialView()
Code:
First Partial View:
<table>
<thead>
<tr><th>headers with other info</th>
<th>Display(/th>
<tr>
</thead>
<tbody>
<tr><td>cells with other info</td>
<td>#Ajax.ActionLink("View", "DisplayResponse","HealthStatus", new { respID = item.UniqueID,qVersion=item.QuestionnaireVersion, qname = item.QuestionnaireName }, new AjaxOptions { UpdateTargetId = "responseDisp" })</td>
</tbody>
</table>
<div id="responseDisp"></div> <--- **This is the div I wish to populate, does anyone know why it's not working?**
DisplayResponse Action (without the logic for parsing the xml documents)
public ActionResult DisplayResponse(Guid respID, int qVersion, String qname) {
var allResponses = ZData.Responses;
var response = (from r in allResponses
where r.UniqueID == respID
select r
).First();
//geting an XML questionnaire document
var questionnaireDetails = ZodiacData.Questionnaires;
var questionnaire = (from q in questionnaireDetails
where q.Name == qname && q.Version == qVersion
select q
).First();
//creating XMLDocument to read the questionnaire
XmlDocument xqdoc = new XmlDocument();
xqdoc.LoadXml(questionnaire.Xml);
XmlElement qroot = xqdoc.DocumentElement;
ViewBag.qroot = qroot;
XmlDocument xrdoc = new XmlDocument();
xrdoc.LoadXml(response.Raw);
XmlElement rroot = xrdoc.DocumentElement;
ViewBag.rroot = rroot;
return PartialView("_PrintedResponse");
}
I would be grateful for any help!
In MVC3 the #AJax. helpers are rendering regular form and a tags with some extra data- attributes. To make the magic work some Javascript is needed which will use this generated data- attributes to make the necessary jQuery ajax calls.
These js functions are living in the jquery.unobtrusive-ajax.js so add this line to your layout or view and it should work:
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")"
type="text/javascript"></script>
First, as mentioned above, you must have a reference to the jquery.unobtrusive-ajax.js file as this will get things "wired" up correctly for you.
This answer is also in response to your comment on your question about how you're passing your models to your views. You are actually making things more complicated for yourself by using the ViewBag for your model.
By using the ViewBag for your models, you will have a harder time finding/fixing/resolving issues in typo's as well as the great features of the Razor helpers. The ViewBag is a dynamic object and there are no compile time type checks. You don't need to cast your objects either (less code).
The preferred (and best practice) is to hook things up like so:
1) Your controller contains ViewModels (Strongly Typed) that are passed to the ViewModels
Controller
public ActionResult Something() {
return View();
}
public ActionResult UserView() {
UserViewModel mdoel = new UserViewModel {
Email = "me#somewherecool.com",
FirstName = "Your",
SStatuses = new List<SStatus>{
new SStatus {
ID = 0
}
}
};
return PartialView("_SomethingPartial", mdoel);
}
Index ("Something" view)
#{
ViewBag.Title = "Something";
}
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
<h2>Something</h2>
#Ajax.ActionLink("Ajax Click", "UserView", new AjaxOptions { UpdateTargetId = "MyDivContainer", InsertionMode = InsertionMode.Replace })
<div id="MyDivContainer">
<!-- my content should be here -->
</div>
Partial View
#model StackModels.UserViewModel
<div class="par">
#Html.LabelFor(m => m.FirstName)
<div class="field">
#Html.TextBoxFor(m => m.FirstName)
#Html.ValidationMessageFor(m => m.FirstName)
</div>
</div>

MVC3 Ajax.BeginForm with PartialView and persistent routedata issue

I have a main view and the URL for this view has a Action/Controller/Area and id value, something like:
http://localhost:56513/Incident/IncidentHome/Index/8c02a647-a883-4d69-91be-7ac5f7b28ab7
I have a partialview in this main view, one that calls methods in the controller via Ajax. This partial view needs to know the ID value of the url for the parent page. I found how to do this is through 'ParentActionViewContent'. Something like:
using (Ajax.BeginForm("UpdatePersonalStatusPanel", "Status", new { area = "Tools" , id = ViewContext.ParentActionViewContext.RouteData.Values["id"].ToString() }, new AjaxOptions { UpdateTargetId = "divPersStatus" }))
{
<p style="text-align: center;">
<span class="editor-label">#Html.LabelFor(m => m.StatusText)</span> <span class="editor-field">#Html.EditorFor(m => m.StatusText)</span>
<input type="submit" value="Change Current Status" />
</p>
}
Now, this works fantastic for calling the controller method. The ID is passed correctly so that the controller can then see it in the routedata. I use the id to perform a database call, and then return the partialview again. The problem is on the return. I get a 'Object reference not set to an instance of an object' on the ViewContext.ParentActionViewContext.RouteData.Values["id"].ToString() bit in the ajax.beginform , and my targetid doesn't refresh.
Clearly I must be doing something wrong. Does someone else have a better way to see the parent view's routedata through Ajax?
If I'm understanding you correctly, this partial view calls itself. So ParentActionViewContext works the first time because the first time your main view calls an action using this partial view. However, later an ajax call directly returns this partial view. When the partial view is invoked directly there is no Parent View action hence the null reference on ParentActionViewContext.
Rather than deal with with route data I recommend including the id in the model of your partial view.
new { area = "Tools" , id = Model.Id }

Setting authorization on a specific ASP .NET MVC 3 field?

Is it possible to set authorization on a specific field in MVC 3?
My initial thought (and MSDN research) indicates that the [Authorize] tag is only for controller level actions (create,edit,index,etc). I can do this on the controller action:
[Authorize(Roles = "RoleA,RoleB")]
public ActionResult Create()
{
return View(new Tracking());
}
The scenario is that two roles (RoleA and RoleB) can access the 'Edit' controller. But only RoleA can change the first field. The other role (B) can only view the field.
I would like to do something like this on a specific field:
[Required]
[Range(1, 99)]
[Authorize(Roles = "RoleA")]
public int Sequence { get; set; }
UPDATE1:
A little more research down the StackOverflow rabbit roles reveals that I need to use partial views.
So in my view I add this code:
<div>
#if (Context.User.IsInRole("RoleA"))
{
#Html.Partial("_SequenceEdit")
}
else
{
#Html.Partial("_SequenceView")
}
</div>
So if the user is RoleA they get a partial view that allows editing of the 'sequence' field. Otherwise they get a view only of the 'sequence' field.
My view only partial view looks like this:
<div class="editor-label">
#Html.LabelFor(model => model.Sequence)
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.Sequence)
#Html.HiddenFor(model => model.Sequence)
#Html.ValidationMessageFor(model => model.Sequence)
</div>
I see that you've already figured out how to modify the view in order to not show a text box to users in Role B. But you should also do server-side validation to make sure only users in Role A can edit the field.
[Authorize(Roles = "RoleA,RoleB")]
[HttpPost]
public ActionResult Edit(int trackingID, Tracking newTrackingObject)
{
// grab the current version of the tracking object from your data repo
var oldTrackingObject = trackingRepo.GetByID(trackingID);
// check if the user is in role A and edit the sequence number
if(Context.User.IsInRole("RoleA"))
oldTrackingObject.Sequence = newTrackingObject.Sequence;
// continue processing the new tracking object
// after all processing is done, persist the edited tracking object back to the repo
trackingRepo.Update(oldTrackingObject);
trackingRepo.SaveChanges();
}
This will prevent users in Role B from changing the sequence field by manually editing the hidden form field (eg. with FireBug or a similar tool.)

ASP.NET MVC 3 #Html.DropDownListFor ignoring selectedValue

In my asp.net MVC 3 project I would like to create a contact that's related to a company.
You can either directly create a contact OR go via the company details view and add a new contact passing the companyId to set that company already in the dropdown on the contact create form.
The problem is that I can 't get the passed company as default in my dropdown.
Global.asax
routes.MapRoute("contactCreate", "contact/toevoegen/{companyid}", new { action = "ContactCreate", controller = "Backend", companyid = UrlParameter.Optional });
Controller method
public ActionResult ContactCreate(int? companyid)
{
Contact contact = new Contact();
ViewBag.StatusList = srep.getContactStatusses();
ViewBag.CompanyId = companyid;
return View(contact);
}
View
#model xxx.Models.Contact
...
<div class="editor-label">
#Html.LabelFor(model => model.bedrijf_id)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.bedrijf_id, new SelectList(ViewBag.Bedrijven, "bedrijf_id", "bedrijf_naam",ViewBag.CompanyId), "--Kies bedrijf--")
#ViewBag.CompanyId
#Html.ValidationMessageFor(model => model.bedrijf_id)
</div>
...
#ViewBag.CompanyId has a value.
Any idea why it's not setting the selected value?
When doing a "DropDownListFor" it will try to match up the value passed in from the model for the selected value. So in your example it will use "bedrijf_id" as the selected value. It looks like you want the selected value to be from something outside of your model.
From the comments I think what you want is just a DropDownList as follows:
#Html.DropDownList("DropDownList", new SelectList((ViewBag.Bedrijven, "bedrijf_id", "bedrijf_naam", ViewBag.CompanyId), "--Kies bedrijf--")
Hope this helps.

Resources