I have Get and Post partial Action. Get take me a list of image which I have in ma app.
[HttpGet]
public PartialViewResult ViewImageFileList()
{
IEnumerable<string> allImages = Directory.EnumerateFiles(Server.MapPath("~/Images/NBAlogoImg/"));
return PartialView(allImages);
}
Post delete image which I extra.
[HttpPost]
public PartialViewResult ViewImageFileList(string imageNameType)
{
var fileToDeletePath = Path.Combine(Server.MapPath("~/Images/NBAlogoImg/"), imageNameType);
if (System.IO.File.Exists(fileToDeletePath))
{
fileOperations.Delete(fileToDeletePath);
}
return PartialView();
}
My .chhtml of my partial view
#model IEnumerable<string>
<div class="name-block-style">
Логотипы которые имеются
</div>
<div id=team-logo-wrapper-images>
<ul>
#foreach (var fullPath in Model)
{
var fileName = Path.GetFileName(fullPath);
<li>
<div class="box-name-image">
<p class="image-name-type">#fileName</p>
<img src="#Url.Content(string.Format("~/Images/NBAlogoImg/{0}", fileName))"
class="logo-images" alt="Логотип команды"
title="Логотип команды" />
</div>
</li>
}
</ul>
<div id="delete-image-form" class="form-group">
#using (Ajax.BeginForm(
"ViewImageFileList",
"Team",
new AjaxOptions() { HttpMethod = "POST", OnComplete = "reloadPage()" }))
{
<label>Введите имя с указание типа изображения</label>
<input type="text" class="form-group" name="imageNameType" id="imageNameType" />
<input type="submit" value="Удалить" class="btn btn-primary" />
}
</div>
<script>
function reloadPage() {
location.reload();
}
</script>
My problem is Null references when I write the deleting image and submit it(i do it by ajax). I have this error Null reference but when I click to continue, the image deleted and my script to reload page work.
I want to understand why I take the null and how I can fix it, because it stops my app always when I delete an image.
The problem is that when you POST after you delete the image you don't populate the model of the partial view, as you do correctly in ViewImageFileList. This has a result when the View Engine try to build the view that you would send after the POST to the client, to get a null reference exception when try to perform the foreach on a null reference.
That being said, the thing you need is to pass to the PartialView all the images. So just add before the return statement in the action method you POST this:
var allImages = Directory.EnumerateFiles(Server.MapPath("~/Images/NBAlogoImg/"));
return PatialView(allImages);
When you browsing images you return view with model passed
return PartialView(allImages); //allImages is a model
But when you deleting images you return view without any model
return PartialView(); //need to pass a model
So after deleting you would like to redirect to ViewImageFileList to browse
all images
[HttpPost]
public RedirectToRouteResult ViewImageFileList(string imageNameType)
{
var fileToDeletePath = Path.Combine(Server.MapPath("~/Images/NBAlogoImg/"), imageNameType);
if (System.IO.File.Exists(fileToDeletePath))
{
fileOperations.Delete(fileToDeletePath);
}
return RedirectToAction("ViewImageFileList");
}
or retrieve images in delete action once again and pass the list to view
[HttpPost]
public PartialViewResult ViewImageFileList(string imageNameType)
{
var fileToDeletePath = Path.Combine(Server.MapPath("~/Images/NBAlogoImg/"), imageNameType);
if (System.IO.File.Exists(fileToDeletePath))
{
fileOperations.Delete(fileToDeletePath);
}
IEnumerable<string> allImages = Directory.EnumerateFiles(Server.MapPath("~/Images/NBAlogoImg/"));
return PartialView(allImages);
}
Related
I have a grid which includes below hyperlink row,currently for all rows we have same hyperlink and only ID is changing and it is working fine.
<a href=" + #ViewBag.Url+ ID + " target='_blank'>Test</a>
Now for every row, we have different link url which i would get from action method when I pass ID.
I want to call MVC Action Method to get hyperlink url and then open it in another tab.How can I accomplish this?
I tried this one but it is not opening hyperlink?
<div class="row">
<div class="col-md-4">
Click Here;
</div>
</div>
public string GetPDFUrl(string id)
{
return "test.com" + id;
}
There are several ways to solve your problem. one of them is using child actions.
Put your generating URL part into a partial view to put your logic in your action method. So, create a child action method that can only be called in your views.
[ChildActionOnly]
public ActionResult GenerateUrlPartial(int id)
{
var generatedUrl = "";//your url business is here
var model = new UrlInfo { Url = generatedUrl };
return PartialView(model);
}
Then, create GenerateUrlPartial.cshtml partial view :
#model UrlInfo
#{
Layout = null;
ViewBag.Title = "GenerateUrlPartial";
}
<div class="row">
<div class="col-md-4">
Click Here;
</div>
</div>
And in your loop, call the action method like this :
#for (int i = 0; i < 10; i++)
{
Html.RenderAction("GenerateUrlPartial", new { id = i });
}
Hope this helps.
What I want to do
I am very new to MVC.
I'm trying to create a page that allows users to perform the following actions on the same page:
View the list (table)
Add a new item (Filling the form and clicking the Add button should update the table)
Delete an item from the list (Clicking the Delete button in a row should update the table)
A simple example looks like this but I actually have two lists on one page (Fees and Costs):
Question
What would be the best way to achieve this?
Should I go with Dylan Beattie's method posted here which would look something like this?
public ActionResult MyAction(string submitButton, MyViewModel form)
{
switch (submitButton)
{
case "AddFee":
return (AddFee(form));
case "AddCost":
return (AddCost(form));
case "RemoveFee":
return (RemoveFee(form));
case "RemoveCost":
return (RemoveCost(form));
}
}
public ActionResult AddFee(MyViewModel form)
{
Fee newFee = ....; // Get entered data from `form`
_repository.InsertFee(newFee);
return View("Create"); //Back to the original page
}
Or is there any other recommended methods to handle this such as using JavaScript?
You could create the table as a partial view and re render this via ajax.
Wrap the partial view in a div and Wrap the form in #using (Ajax.BeginForm(.... and target the wrapper div. Your controller action that is targeted by the ajax request will need to return a partial view.
Here is a simple example
public class HomeController : Controller
{
public ActionResult Index()
{
MYvm vm = new MYvm() { id = 1, name = "This is my View Model" };
return View(vm);
}
public ActionResult DA(MYvm vm)
{
vm.name = "CHANGED";
return PartialView("Part", vm);
}
View:
#model MvcApplication1.Controllers.HomeController.MYvm
#{
ViewBag.Title = "Home Page";
}
#using (Ajax.BeginForm("DA", "Home", new AjaxOptions() { UpdateTargetId = "cont", HttpMethod = "Get" }))
{
<div>
Id: #Html.EditorFor(model => model.id)
</div>
<div>
Name: #Html.EditorFor(model => model.name)
</div>
<input type="submit" value="SubmitForm" />
}
<div id="cont">
#{Html.RenderPartial("part", Model);}
</div>
Partial View
#model MvcApplication1.Controllers.HomeController.MYvm
#{
ViewBag.Title = "part";
}
<h2>part</h2>
#Model.name
Should I go with [previous SO answer]
No. That answer was for a different scenario where the question had a form with two submit buttons that wanted to do two different actions (and wasn't even the accepted answer to that question).
Your sample screenshot indicates that some javascript/jquery and ajax would solve the issue cleanly.
As you're new to MVC, try to keep it relatively simple. Break up the page into separate parts:
the containing page
the edit form
the list with remove
the edit/list work independently and should be written in a way that they could be put on any other page - the page is just there to contain them and doesn't do much else (obviously your real page will contain more, but add those parts as separate components as well).
1 Create actions for your list and edit forms that return partialviews - just the parts that are needed for that view (self-contained)
controller:
[HttpGet]
public ActionResult AddCost()
{
var model = new Cost();
return PartialView(model);
}
[HttpPost]
public void AddCost(Cost model)
{
if (ModelState.IsValid) {
db.SaveCost(model);...
}
}
form Views/Home/AddCost.cshtml:
#using (Ajax.BeginForm(...
{
<div class='editor-label'>#Html.LabelFor(model=>model.Description)</div>
...etc...
}
I'll leave you to set the Ajax.BeginForm properties. But make sure the on-success calls reloadCostList() (see below)
controller
public ActionResult CostList()
{
var model = db.loadCosts(); ...
return PartialView(model);
}
list, Views/Home/CostList.cshtml
#model IEnumerable<ViewModels.Cost>
<table>
<thead>
<tr>
<th>Cost Description</th>
...
<tbody>
#foreach (var cost in Model.Costs)
{
<tr data-id='#cost.Id'>
<td>#Html.DisplayFor(x=>cost.Description)</td>
...
<td><a href='#' class='remove-button'>Remove</a></td>
}
...
2 Create an action + view for the main page with placeholder for the form and calls the list partial-action, eg:
<div id="body">
<div id="formWrapper">
#Html.Action("AddCost")
</div>
<div id="listWrapper">
#Html.Action("ListView")
</div>
</div>
if you already load the data for the page, you can pass it directly to the partial, but there's no need:
#Html.Partial("ListView", Model.Costs)
this allows you to refresh the list via an ajax call, something like:
function reloadCostList() {
$(".listWrapper").load("Home/CostList");
}
(ideally, $.ajax and add some fancy UI to indicate loading)
3 Add a remove action to your controller
[HttpPost]
public void RemoveCost(int id)
{
}
4 Wire up the Remove link
$(function() {
$(".remove-button").click(function() {
var id = $(this).closest("tr").attr("id");
$.post("/Home/RemoveCost/" + id, null, function() {
$(".listWrapper").load("Home/CostList");
// or reloadCostList(); from above
// or:
//$(".listWrapper tr[id=" + id + "]").hide();
});
});
}
rather than re-load the entire list, you could just remove the row (add some fancy UI like fade-out...)
I am new to C# and MVC, while I understand the Controller and the Model side. I have encountered a problem when accessing methods within a controller in order to do a simple conversion that I can then return to my View.
My Controller:
public class Exercise05Controller : Controller
{
//
// GET: /Exercise05/
public ViewResult Index()
{
return View();
}
public ActionResult GramsToOunces(double? grams)
{
ViewData["grams"] = grams;
ViewData["ounces"] = (grams * 0.035d);
if (grams < 5)
{
return RedirectToAction("Index", "Home");
}
else if (grams > 5)
{
return View("GramsToOunces");
}
return RedirectToRoute(new
{
controller = "Exercise05",
action = "Index"
});
}
}
My Index View:
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<form action="/Exercise05/GramsToOunces" method="post">
<input name="grams" type="text" placeholder="Insert grams to convert to ounces" />
<input type="submit" value="Convert Grams to Ounces" />
</form>
My GramsToOunces View:
#{
ViewBag.Title = "Index";
}
<h2>GramsToOunces</h2>
<!-- Currently nothing else here -->
I believe my issue is arising somewhere on this line; action="/Exercise05/GramsToOunces". Using debugging has shown me that the the controller processes all the information and gets ready to return the view to suddenly just not return anything. I wish to do a simple conversion and then return this as a view. Would someone be able to point me as to where I am going wrong?
As long as your view is named the same as the controller action, try:
#using (Html.BeginForm())
{
<input name="grams" type="text" placeholder="Insert grams to convert to ounces" />
<input type="submit" value="Convert Grams to Ounces" />
}
This is just a sanity check, but you are using the line return View("GramsToOunces", "ounces");. According to the docs http://msdn.microsoft.com/en-us/library/dd470743(v=vs.118).aspx, first string is the view name and second string is the master page. Do you have a master page called "ounces"? If not that could explain why no result is returning. Perhaps you want to use return View("GramsToOunces");
If you simply want to do the conversion and show the result to user. Just make it Ajaxified. Do not post the entire form.Make an ajax call and get the result and show it to user.
Assuming you have jQuery loaded to your form.
#using(Html.BeginForm("GramsToOunce","Exercise5"))
{
<input name="grams" id="gram" type="text" placeholder="Insert grams" />
<input type="submit" id="btnSubmit" value="Convert Grams to Ounces" />
<div id="result"></div>
}
<script type="text/javascript">
$(function(){
$("#btnSubmit").click(function(e){
e.preventDefault();
var url=$(this).closest("form").attr("action");
$.post(url,{ grams : $("#gram").val()} ,function(res){
$("#result").html(res);
});
});
});
</script>
Let's make sure that our action method returns the value.
[HttpPost]
public string GramsToOunces(double? grams)
{
var result=grams * 0.035d;
return result.ToString();
}
Also if you simply want to do the multiplication, just do it in the client side(javascript). No need of a server call.
According to the documentation this line:
return View("GramsToOunces", "ounces");
Tries to return the view with the specified master page. However it seems there is no master page "ounces" in your project. So most likely what you need here is simply:
return View("GramsToOunces");
Note that you do not need to pass any models whatsoever because you are already using the ViewData.
I have a page with a video at the top and a list of videos you can choose from. Currently, clicking a link in the video list will reload the entire page. I need it to only refresh the partial view I have containing the video at the top of the page.
I saw several posts here on SO showing how to reload partial views with JQuery, but couldn't get it to work correctly in my situation. I'm unsure how to pass the correct id of the video along.
Controller:
public ActionResult Videos(int topVideo = 0)
{
VideosModel model = new VideosModel();
model.Videos = StatsVideoService.GetEntityList(new Lookup(TableStatsVideo.IsDeleted, false)).OrderByDescending(x => x.DateCreated).ToList();
if (topVideo == 0)
model.TopVideo = model.Videos.First();
else
{
model.TopVideo = model.Videos.Where(x => x.StatsVideoId == topVideo).FirstOrDefault();
if (model.TopVideo == null)
model.TopVideo = model.Videos.First();
}
return View(model);
}
View:
#model Project.Models.VideosModel
<section class="videos">
<div id="top_video">
#{Html.RenderPartial("StatsVideo", Model.TopVideo);}
</div>
<ul>
#foreach (var item in Model.Videos)
{
<li>
<div class="videoList">
<a href ="#Url.Action("Videos", "Home", new { topVideo = item.StatsVideoId })">
<img src="#Url.Content("~/Content/img/video-ph.png")" />
</a>
<p class="videoTitle">#item.Title</p>
</div>
</li>
}
</ul>
</section>
If there's any more information needed, please let me know.
After several hours of bashing my head against the wall, I got it to work! Just as a reference to anyone else in the future who's viewing this article, here's how I got it to work:
I set the onclick of the link to point to a javascript method, passing in the id of the video as a parameter:
#foreach (var item in Model.Videos)
{
<li>
<div class="videoList">
<a href ="#" onclick="updateTopVideo(#item.StatsVideoId)">
<img src="#Url.Content("~/Content/img/video-ph.png")" />
</a>
<p class="videoTitle">#item.Title</p>
</div>
</li>
}
And then I included this script in the view at the bottom:
<script>
var updateTopVideo = function (itemId) {
var url = '#Url.Content("~/Home/StatsVideo/")';
url = url + itemId;
$.get(url, "", callBack, "html");
};
var callBack = function (response) {
$('#top_video').html(response);
};
</script>
Finally, I added a method to my controller that would return the partial view needed for the video at the top of the screen:
public ActionResult StatsVideo(int Id)
{
IStatsVideo vid = StatsVideoService.GetEntity(new Lookup(TableStatsVideo.StatsVideoId, Id));
if (vid == null)
vid = StatsVideoService.GetEntityList(new Lookup(TableStatsVideo.IsDeleted, false)).OrderByDescending(x => x.DateCreated).FirstOrDefault();
return PartialView(vid);
}
This code should be fairly easy to understand. Basically, the onclick calls the first javascript method, which then calls the controller. The controller builds the partial view and returns it. The first javascript method passes it to the second javascript method which sets the html of the div "top_video" to be the returned partial view.
If anything doesn't make sense, or anyone's having trouble with this in the future, let me know and I'll do my best to offer some help.
I think there may be several confusing and inconsistent elements here.
First, you are returning a full view instead of a partial view. This reloads all containing elements, not just the part that is relevant to your partial view.
Second, you are using Url.Action, which only generates the url. I would recommend using Ajax.ActionLink, which allows you to do fully ajax calls, refreshing the content of your partial div and updating a target div element.
instead of:
<div class="videoList">
<a href ="#Url.Action("Videos", "Home", new { topVideo = item.StatsVideoId })">
<img src="#Url.Content("~/Content/img/video-ph.png")" />
</a>
<p class="videoTitle">#item.Title</p>
</div>
try the more modern solution
<div class="videoList">
#Ajax.ActionLink(
"Videos",
"Home",
"new { topVideo = item.StatsVideoId },
new AjaxOptions {
HttpMethod = "GET",
OnSuccess = "handleSuccess"
}
)
</div>
This way you can be very specific on what you want each link to do, and you can pass along multiple parameters as well as define a callback function. You can also use "UpdateTargetId" in your ajax options to load your newly refreshed partial view into a DOM element.
You can remove the around the image and just store the url generated by the Url.Action in a data-href attribute.
Then you can use the jquery load method to load the data:
$(".videolist>img").click(function () {
$("#content").load($(this).data("href"));
});
I created a fiddle that loads content dynamically here, so you can play with it if you want: http://jsfiddle.net/bTsLV/1/
I'm learning MVC3 and building a little "to-do" website as a learning exercise, so I'm open to the idea that I'm just completely going down the wrong path!
Anyway, I have a page working perfectly with regular postbacks. I'm trying to Ajax it up with jQuery and UnobtrusiveAjax and everything still technically works correctly (the data is passed to the controller and saved in my database). The problem is that in the element I replace, each form's fields are all filled with the values that I just passed in on the one form.
Index.cshtml
#model WebUI.Models.HomeViewModel
#{
ViewBag.Title = "Index";
<script src="#Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
}
<h2>My Goals</h2>
...snip...
<div id="goal_div">
#foreach (var goal in Model.Goals)
{
Html.RenderPartial("GoalDetail", goal);
}
</div>
GoalDetail.cshtml
#model Domain.Entities.Goal
<div class="goal" id='goal_id#(Model.ID)'>
<h4>#Model.Name</h4>
<p>#DateTime.Now.ToString()</p>
<p class="goal_description">#Model.Progress % complete</p>
<ul>
#foreach (var task in Model.Tasks)
{
using (Ajax.BeginForm("UpdateTask", "Home", new AjaxOptions { UpdateTargetId = "goal_id" + Model.ID }))
{
#Html.HiddenFor(g => g.ID)
#Html.Hidden("TaskID", task.ID)
<li class="task">
#Html.CheckBox("IsComplete", task.IsComplete)
#Html.TextBox("TaskName", task.Name)
#task.Name
<input class="actionButtons" type="submit" value="Update task" />
</li>
}
}
<li>
#using (Html.BeginForm("AddTask", "Home"))
{
#Html.HiddenFor(g => g.ID)
#Html.Editor("TaskName")
<input class="actionButtons" type="submit" value="Add task" />
}
</li>
</ul>
</div>
HomeController.cs
public class HomeController : Controller
{
private IGoalRepository Repository;
public HomeController(IGoalRepository repo)
{
Repository = repo;
}
public ViewResult Index()
{
HomeViewModel viewModel = new HomeViewModel();
viewModel.Goals = Repository.Goals;
return View(viewModel);
}
public ActionResult AddTask(int ID, string TaskName)
{
bool success = Repository.SaveTask(ID, 0, TaskName, DateTime.Today, false);
return RedirectToAction("Index");
}
public ActionResult UpdateTask(int ID, int TaskID, bool IsComplete, string TaskName)
{
bool success = Repository.SaveTask(ID, TaskID, TaskName, DateTime.Today, IsComplete);
Goal updatedGoal = Repository.Goals.FirstOrDefault(g => g.ID == ID);
return PartialView("GoalDetail", updatedGoal);
}
public ActionResult AddGoal(string Name, DateTime Enddate)
{
bool success = Repository.SaveGoal(0, Name, DateTime.Today, Enddate);
return RedirectToAction("Index");
}
public ActionResult UpdateGoal(int GoalID, string Name, DateTime Enddate)
{
bool success = Repository.SaveGoal(GoalID, Name, DateTime.Today, Enddate);
return RedirectToAction("Index");
}
}
I have the time there just to make sure that the AJAX refresh has actually happened, and you'll see why I have the task name there twice.
This is what I see when I first load the page:
Then I check the checkbox of the the 2nd task of the 1st goal, rename it "Updated Task #2", and click the update button. That's when this happens:
Seeing how the task names NOT part of the form are all correct (ignoring the re-ordering for now), and the progress value has been updated correctly (it just takes the completed tasks and divides by the total number of tasks), I have no idea why all the form values have been replaced. Even the AddTask form has been filled in, even though I haven't changed that one to use Ajax yet. I've been searching for reasons for this for 2 days now, and have come up empty.
After even more searching, I finally discovered the issue. Basically, it has to do with the way ModelState works in MVC. Reading this help thread and this article really helped me understand what was happening with my page. I ended up calling ModelState.Clear() in my controller right before returning the partial view, but this SO question and answer
suggests another method.