Url.Action is how to reformat URL - asp.net-mvc-4

I am creating an MVC4 application.
In my contract controller overview page i have an Url.Action
int teller = 0;
foreach (var item in Model)
{
<a href="#Url.Action("Details", "Contract",new { id = teller })">
<tr>
<td>#Html.DisplayFor(modelItem => item.ContractMSFNo)</td>
<td>#Html.DisplayFor(modelItem => item.StageCode)</td>
<td>#Html.DisplayFor(modelItem => item.ValidFromView)</td>
<td>#Html.DisplayFor(modelItem => item.ValidToView)</td>
</tr>
</a>
teller++;
}
I need to pass the id. I am using id in the ActionLink details in Contract Controller
my controller is
public ActionResult Details(int id)
{
//code
return View(contract);
}
When i click on the link Url generated is
http://localhost:4826/Contract/Details/0
/0 is the id
i want my Url to be http://localhost:4826/Contract/Details
i know this can be acheived thru Html.Actionlink but it is my compulsion to use Url.Action. Can it be acheived with Url.Action

It can't be done by routing or ActionLink. But you may try to use session.
1) Add to your controller new method to save your id to session:
public JsonResult Save(int id)
{
Session["ID"] = id;
return Json("Success");
}
2) Add jQuery method to save data in session from View and delete parameter from Url.Action:
<a class="mylink" href="#Url.Action("Details", "Contract")"></a>
<script>
$(".mylink").click(function(){
var data = { id : teller}; //**teller is from your example
$.get("#Url.Action("Details", "Contract")", data)
});
</script>
3) Change your Details ActionResult to get id from session:
public ActionResult Details()
{
var id = (int)Session["ID"];
//code
return View(contract);
}
P.S: Ask your client, how he expects to give sombody external links. It will be impossible if url doesn't have a parameter. And it is very bad for SEO.

If you want your URL without the id parameter, simply don't pass it to the Url.Action() method, as follows:
#Url.Action("Details", "Contract")

If you add like {id=teller} then route automatically add id parameters end of the link. If you don't need id parameters for this url you need to remove
new { id = teller }
Final version like this
#Url.Action("Details", "Contract")

OK, reading this comment: "no actually there are many ids ... code is foreach (var item in Model) { ", I am not sure I understand what you really want to achieve. You are passing a parameter to the view, which can have only one value. Are you sure that you are not looking for something like:
foreach (var item in Model)
{
<a href="#Url.Action("Details", "Contract",#item.ID>
...
}
instead? The fact the ID is visible or not in the URL seems to be another problem, no ?

Related

Check record if exist (instantly) in MVC

I want to check instantly, after i type something in text box, if a record exist in my database. I managed to return on my page number of how many times record exist in database, but i want to return a message (if exists or not).
So, the question is: How can I display a message if record exist or not?
PS. I`m using ASP.NET MVC
Here is my code:
Model class:
public class AdminModel
{
[Remote("IsUniq", "Home", HttpMethod = "POST")]
public string FirstName { get; set; }
}
My controller action(HomeController):
[HttpPost]
public JsonResult IsUniq(string FirstName)
{
IPAdressProgramEntities r = new IPAdressProgramEntities();
var user = r.spLineExist(FirstName);//spLineExist - procedure in SQL- return how many time record exist in database
return Json(user); //return on my page how many times record exists
}
And this is my view:
#using (Html.BeginForm())
{
<div class="editor-field">
#Html.EditorFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
</div>
}
PS WebConfig is configured and also scripts are included in my view.
Thank you.
If you simply want to show message when your count is greater than 0, Add an Error message property to your data annotation.
[Remote("IsUniq", "Home", HttpMethod = "POST", ErrorMessage = "Exist")]
public string FirstName { get; set; }
and return true of false from your action method. To show the error message, you need to return false from the method.
var responseToSend = user!=0; //user is the count returned by your existing code
return Json(responseToSend);
If you want to show both the messages (Exists/ Not exists), you may consider handling the check yourself with a little jQuery ajax call than relying on the remote data annotation. So simply remove the data annotation.
And listen to the keyup event on this input field, read the value, send to server to check it exist or not, based on the result, show appropriate message
$(function () {
$("#FirstName")
.keyup(function () {
$.post('/Home/IsUniq?FirstName=' + $(this).val(), function (res) {
if (res) {
$("span[data-valmsg-for='FirstName']").text("Not Available")
} else {
$("span[data-valmsg-for='FirstName']").text("Available")
}
})
});
});
Make sure you return True or False from your action method.
[HttpPost]
public JsonResult IsUniq(string FirstName)
{
//If exists
return Json(true);
else
return Json(false);
}

ModelState.IsValid == false, although all model values are inserted

today I have the problem, that after i inserted all data to a formular to create a new Product, the programm say that ModelState.IsValid==false.
When i look into the modelState during debugging there is a Error on field 0. The error: "The CuId field is required".
To prevent that i set CuId right in the Creat POST action like so in the ProductController.cs:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Product product)
{
int lastcu = db.Customers.Max(l => l.Id);
product.CuId = last;
if (ModelState.IsValid)
{
db.Products.Add(product);
db.SaveChanges();
return RedirectToAction("Create", "NewIssue");
}
return View(product);
}
But again it sets the same error.
My view looks like that. Actually the model.CuId should already set there:
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Product</legend>
<div class="editor-label">
#Html.LabelFor(model => model.CuId, "Customer")
#ViewBag.Cuname
#Html.HiddenFor(model => model.CuId, new { id = "lastcu" })
</div>
My GET Controller looks like this:
public ActionResult Create()
{
int lastcu = db.Cu.Max(l => l.Id);
//gives the id a Name
var lastcuname = db.Customers.Find(lastcu).Name;
//show to User by creating the product
ViewBag.Cuname = lastcuname;
ViewBag.CuId = lastcu;
return View();
}
When I look in debug mode into the values of the model product, all fields are filled (also CuId) except of the the foreign key what is bind to the product.CuId and the Id of the product what is set automatically from the database.
Hopefully you can help me. Thanks in advance.
As for the first part of your question, ModelState is populated by the DefaultModelBinder when the method is first called. If property CuId has the [Required] attribute and its value is not posted back, then an error is added to ModelState and therefore ModelState.IsValid is false. Just setting a property of your model does not remove ModelState values.
As for the second part of your question, your not passing you model to the view in the GET method so #Html.HiddenFor(m => m.CuId) generates a hidden input with no value (because the value of model.CuId is null or its default). All you currently doing is passing some values to the view using ViewBag (not good practice) which you never even use. Instead, pass the model to the view as follows.
public ActionResult Create()
{
int lastcu = db.Cu.Max(l => l.Id);
var lastcuname = db.Customers.Find(lastcu).Name;
// Initialize a new instance of the model and set properties
Product model = new Product()
{
CuId = lastcu,
Cuname = lastcuname // assume this is a property of your model?
};
return View(model); // return the model
}
Side note: #Html.LabelFor(model => model.CuId, "Customer") generates a html <label> which is an accessibility element. Clicking on it sets focus to its associated form control. But you don't have an associated form control (just a hidden input which cannot receive focus)

ASP.NET mvc Ajax Helper DropDownListFor send selected item value as parameter to controller

Problem
I want my Ajax form to pass the value of the selected DropDownListFor to the controller but I can't figure out why the controller is not taking any value.
I am working with ASP.NET MVC and I would like to stick with the helper functions as much as I can.
View
#using (Ajax.BeginForm(new AjaxOptions {
HttpMethod = "Get",
UpdateTargetId = "UserResults",
InsertionMode = System.Web.Mvc.Ajax.InsertionMode.Replace }))
{
#Html.DropDownListFor(Model => Model.Roles, new SelectLi(ViewBag.Groups
as System.Collections.IEnumerable, "Value", "Text"), "Select a group",
new { id = "Value", onchange = "$(this.form).submit();" })
}
#Html.Partial("_UsersGroup", ViewData)
Controller
public ActionResult test(int? selectGroup)
{
// Generate dropdownlist Groups
List<SelectListItem> groupList = new List<SelectListItem>();
var query = from s in db.Roles select s;
if (query.Count() > 0)
{
foreach (var v in query)
{
groupList.Add(new SelectListItem { Text = v.Name, Value =
v.ID.ToString() });
}
}
ViewBag.Groups = groupList;
// End
// This part is supposed to take the passed value as parameter
if (selectGroup == null)
{
// To do code comes here, which takes selectGroup as parameter
}
Details
The form should pass a value based on the selection to the controller which takes it as "selectGroup".
ps. this is my first time asking a question, I'm sorry if I made mistakes.
The parameter of you method needs to match the name of the control which is name="Roles" so the method should be
public ActionResult test(int? roles)
Other potential issues with your code
Your controller generates List<SelectListItem> for use by the dropdownlist. There is no need for the unnecessary extra overhead of then creating a new SelectList (which is IEnumerable<SelectListItem>) from it. The view code can simply be #Html.DropDownListFor(m => m.Roles, (IEnumerable<SelectListItem>)ViewBag.Groups, "Select a group")
Do not use Model (capital M) in the expression. If you make any other reference to the model in your view (e.g. #Model.SomeProperty) you will get an error. Using lower case model => model.somProperty is OK but you can simply use m => m.someProperty
The helper will generate an id attribute (in you case id="Role") so it seems unclear why you are adding new { id = "Value", ..}, especially since you don't appear to be referencing the element by its id anywhere
Learn to use Unobtrusive Javascript rather than polluting you mark up with behavior. Remove the onclick attribute and use $('#Roles').change(function() { $('form').submit(); });

Asp Mvc 4 : Add Child to new Parent

I need some help,
I'll do it simply. I have an object Client that has many Appointments. Something Like:
public partial class Client
{
...
public virtual IList<Appointment> Appointments{ get; set; }
}
Is there a way in asp to create a new client with appointments in same page?
P.S: For now, I can add appointments to an existing customer by passing to a partialview the Client id and using ajax and partialView. But, new clients haven't Id. What I've to do?
UPDATE : Precisions
This is what I've in my edit view:
<p>
#Ajax.ActionLink("NewMeeting", "NewAppointmentFrm", new { clientId = Model.Id }, new AjaxOptions { UpdateTargetId = "appointmentPopup", OnSuccess = "displayFormInPopup('#appointmentPopup','" + "NewMeeting") + "',600,'auto');" })
</p>
<div style="display:none;width:600px" id="appointmentPopup"></div>
In the controler I have
Appointment appointment = new Appointment()
{
Client = GetClientById(clientId)
};
return PartialView("NewAppointmentFrm", appointment);
And by submitted the PartialView (NewAppointmentFrm - Appointments Details), I do :
public ActionResult CreateClientAppointment(int clientId, Appointment appointment)
{
var client = GetClientById(clientId);
client.Appointments.add(appointment)
SaveClient(client);
return RedirectToAction("Edit", new { id = candidateId });
}
Thanks for your help
The pattern that MVC4 has in mind is each model in its own page.
If you want to add a new model its done in a separate page.
More or less as shown here :
http://www.asp.net/mvc/tutorials/mvc-music-store/mvc-music-store-part-5
If you try to deviate from that pattern, it's a real pain.
So, I would suggest to put the client in a view and adding an appointment in a separate view
And let MVC do the rest.

Pass ViewBag to _Layout in MVC?

In my Search controller I have:
public JsonResult Search(string term)
{
var termLower=term.ToLower();
var pictures=_PictureRepo.GetAll();
var productsWereSeached = _ProductRepo.GetAll().Where(x => x.Name.ToLower().Contains(term)).Select(x=> new ProductData
{
Name=x.Name,
Price=x.Price,
Id=x.Id,
Warranty=x.Warranty,
Picture=x.Pictures.FirstOrDefault()
});
ViewBag.NOfMatchedProduct = productsWereSeached.Count();
productsWereSeached = productsWereSeached.Take(2);
foreach (var product in productsWereSeached)
{
product.Picture = _PictureRepo.GetAll().Where(x => x.ProductId == product.Id).FirstOrDefault();
}
return Json(productsWereSeached);
}
In my _Layout I have :
<div>
<input id="nOfMatchedProducts" value='#ViewBag.NOfMatchedProduct'/>
<ul id="realPlaceForSearchItems">
</ul>
</div>
Maybe I should put this code from _Layout to PartialView. The question would be, how to pass ViewBag data from controller to PartialView.
"public JsonResult Search" - so you are already calling your method in ajax, right?
Yes, but I am returning 2 productsWereSeached
You already have the result and just need to show it to your targeted element:
$.get('#Url.Action("Search")',function(result){
$("#nOfMatchedProducts").html(result.length);
});
If you want to return additional information like the total number of records searched in addition to "your filtered results", then you can pass it like this:
var totalrows = productsWereSeached.Count();
//filter your search (productsWereSeached)
return Json(new {list=productsWereSeached, totalrows });
Then do this ajax call:
$.get('#Url.Action("Search")',function(result){
$("#nOfMatchedProducts").html(result.totalrows);
});