4

in my MVC web apps, I need to create a popup window with a Partial view (contains webgrid) in it, The problem is the partial view data did not show on the dialogbox, I only see the title. it seems I have problem with the controller call, as well, please be noted I used dynamic model creation since the model was created dynamically in the controller, not an existing model, no sure how to use this type of model. Thanks for any help, This is my codes: this is button in razor view: this jQuery codes:

  $(document).ready(function () {
            $("#GetEmp").click(function (event) {
                $("#popup").dialog({
                    width: 200,
                    hight: 400,
                    title: 'please select an employee',
                    modal: true,
                    open: function (event, ui) {
                        $(this).load("@Url.Action("Travel", "GetEmployee")");
                    }

                });
            });
        });

This is controller code:

    public class Employee
    {
        public string EmpName;
        public string EmpPhone;
        public string EmpNum;
    }

    [HttpPost]
    public ActionResult GetEmployee()
    {
        List<Employee> Emp = new List<Employee>
        {
               new Employee {  EmpName= "ScottGu", EmpPhone = "23232323", EmpNum="242342"},
               new Employee { EmpName = "Scott Hanselman", EmpPhone = "3435353", EmpNum="34535"},
               new Employee { EmpName = "Jon Galloway", EmpPhone = "4534535345345",   
                  EmpNum="345353"}

         };

        return PartialView("_EmpPartial", Emp );
    }

And this is Employee partial view: please note, I am using dynamic in model, since the model is created dynamically.

 @model dynamic
 <ul>
 @foreach (var emp in Model) {
   <li>
     @emp.EmpName
   </li>   
   } 
  </ul>

Hi, thanks for the quick response, I tried it, Remove [HttpPost] and changed @foreach to @foreach (var emp in Model as List), but the does not compile with a red underline in it. do I have to keep @model dynamic in my partial view?

ArunPratap
  • 4,816
  • 7
  • 25
  • 43
user2949042
  • 355
  • 2
  • 5
  • 15

2 Answers2

0

You are passing Emp as view model, which is not a dynamic object, so you should not use dynamic inside the view. I would use @model List<Employee> in the view.

Also, to load the content, you are performing $(this).load("@Url.Action("Travel", "GetEmployee")"); which will send a GET request to your server, but you assigned the [HttpPost] attribute to your controller, so it won't serve a get request. I would remove the [HttpPost].

UPDATE:

A better solution would be to create a ViewModel class like this:

namespace Travelmvc.ViewModels
{
    public class EmployeeListViewModel
    {
        public List<Employee> Emp { get; set; }
    }
}

In my projects, I like creating a ViewModels directory where I put all my view models. Some people simply put there ViewModels in the Models directory though. Depending on where you put your ViewModel, you will have to adjust the namespace above.

You will also need to add the following line at the top of the controller file so that the controller knows about your ViewModels:

using Travelmvc.ViewModels;

Then you would use it like this in your controller:

public ActionResult GetEmployee()
{
    var employeeList = new EmployeeListViewModel {
        Emp = new List<Employee>
        {
           new Employee {  EmpName= "ScottGu", EmpPhone = "23232323", EmpNum="242342"},
           new Employee { EmpName = "Scott Hanselman", EmpPhone = "3435353", EmpNum="34535"},
           new Employee { EmpName = "Jon Galloway", EmpPhone = "4534535345345",   
              EmpNum="345353"}

        }
    };

    return PartialView("_EmpPartial", employeeList );
}

And your view would look like this:

@using Travelmvc.ViewModels
@model EmployeeListViewModel
<ul>
@foreach (var employee in Model.Emp) {
   <li>
     @employee.EmpName
   </li>   
} 
</ul>

UPDATE 2:

If you need to use a dynamic object to pass the data to the view, you can use the one that already exists and that is called ViewBag. Then your controller would look like this:

using Travelmvc.Models;

public ActionResult GetEmployee()
{
    ViewBag.Emp = new List<Employee>
    {
       new Employee {  EmpName= "ScottGu", EmpPhone = "23232323", EmpNum="242342"},
       new Employee { EmpName = "Scott Hanselman", EmpPhone = "3435353", EmpNum="34535"},
       new Employee { EmpName = "Jon Galloway", EmpPhone = "4534535345345",   
              EmpNum="345353"}

    };

    return PartialView("_EmpPartial");
}

The using directive would actually go at the top of your controller file.

And your view would not be strongly typed, and it would look like this:

@using Travelmvc.Models
<ul>
@foreach (var employee in ViewBag.Emp as List<Employee>) {
   <li>
     @employee.EmpName
   </li>   
} 
</ul>

In order for this to work, both your controller and your view will need to be aware of the Employee class, hence the using directives. You may need to adjust the namespace depending on where you put your Employee class. In the code above, I am assuming you put it in Models\Employee.cs:

namespace Travelmvc.Models
{
    public class Employee
    {
        public string EmpName { get; set; }
        public string EmpPhone { get; set; }
        public string EmpNum { get; set; }
    }
}

UPDATE 3:

Here is the JavaScript I would use. I would perform the load before opening the dialog, and I would open it only if the load succeeded.

$(document).ready(function () {
    $("#GetEmp").click(function (event) {
        $("#popup").load('@Url.Action("GetEmployee", "Travel")', function (responseText, textStatus, XMLHttpRequest) {
            if (textStatus == "error") {
                alert("Error");
            } else {
                $(this).dialog({
                    width: 200,
                    hight: 400,
                    title: 'please select an employee',
                    modal: true });
            }
        });
    });
});
Jean-François Beauchamp
  • 5,485
  • 8
  • 43
  • 77
  • Thanks, I think I have to add public class Employee { public string EmpName; public string EmpPhone; public string EmpNum; } to the ViewModels so I did, but I got compile error on Emp = new List in controller, the error is on "new" with red line. any idea – user2949042 Nov 16 '13 at 17:59
  • actually, in my case, I would prefer to use model.dynamic, because in my case, the List will be created dynamically, it created from the view data in Lotus Notes database, so basically, in the List part, it will loop to read Domino database and build a list, then used in partial view. any idea if we use model.dynamic? – user2949042 Nov 16 '13 at 18:06
  • @user2949042 Yes, you could put the Employee class in the ViewModel file and remove it from the controller. As far as it goes for the dynamic object, there is already one called ViewBag that is passed to the view. See the second update in my answer. – Jean-François Beauchamp Nov 17 '13 at 06:26
  • still not partial view,my JQuery codes below: $(document).ready(function () { $("#GetEmp").click(function (event) { $.post('@Url.Action("Travel", "GetEmployee")', {}, function (resp) { $('#popup').html(resp); }); $("#popup").dialog({ width: 200, hight: 400, title: 'please select an employee test', modal: true, }); }); }); any idea to fix it? – user2949042 Nov 17 '13 at 23:02
  • I followed your instruction, no compile error, but no partial view in the popup window, I think something wrong with my jQUery code above, I can see the popup window, but no partial view, and in debug mode, the controller has not been called at all. Any idea how to fix the jQuery codes? – user2949042 Nov 17 '13 at 23:04
  • by the way, I was using the ViewBag to implement your solution, please let me know if you have any idea to fix the problem for my responses above, thanks – user2949042 Nov 18 '13 at 00:37
  • See UPDATE 3 in my answer above. By the way, I had called the action GetEmployees and you are using GetEMployee without an s. I changed that in my code. Also, the syntax is `@Url.Action("ACTION_NAME", "CONTROLLER_NAME")`. I think you inverted the action name and the controller name in your code. – Jean-François Beauchamp Nov 18 '13 at 06:32
  • I inverted my controller codes, and I used your codes in UPdate3, but I still got run time error: Microsoft JScript runime error: Object doesn't support proprty or method 'fail', your entire JQuery codes were highlighted with yellow color, I also add tag on controller code: GetEmployee, the codes in controller has not executed yet, I think there still are something wrong in jQuery codes,any idea? – user2949042 Nov 18 '13 at 16:37
  • @user2949042 I thought load() would work with fail(), but apparently not... Check the code above, I updated it and removed the fail(). – Jean-François Beauchamp Nov 18 '13 at 21:26
  • I tried your this update code again, now I got alert("Error"); , any idea? – user2949042 Nov 18 '13 at 22:20
  • @user2949042 Trace your code, and check the content of the responseText, textStatus, XMLHttpRequest variables when the alert() is called. You could also try loading your PartialView directly in a browser to see what error it returns or use the developer tools in your browser to see what is happening over the network. – Jean-François Beauchamp Nov 18 '13 at 22:58
  • I found in controller I should use abosolute path of partial view like this:return PartialView("~/Views/Shared/_Employee.cshtml"); then, following your new instruction, I found textStatus="Success", but this time, no alert("error") popup, the code crushed on the foreach loop of partialview with error of"Object reference not set to an instance of an object.", I also checked ViewBag.Emp={System.Collections.Generic.List}, so I think this time, it seems the ViewBag.Emp did not generated at controller side at all, any idea? – user2949042 Nov 19 '13 at 00:14
  • I also tried to add this code in Create class of controller, public ActionResult Create() { ViewBag.Emp = new List { new Employee { EmpName= "ScottGu", EmpPhone = "23232323", EmpNum="242342"}, new Employee { EmpName = "Scott Hanselman", EmpPhone = "3435353", EmpNum="34535"}, new Employee { EmpName = "Jon Galloway", EmpPhone = "4534535345345", EmpNum="345353"} }; and insert partial view in razor view without using dialog window, the error is same as I use popup window – user2949042 Nov 19 '13 at 00:22
  • I think the problem is now very clear is that the ViewBag.Emp did not generated at all, but I do following your instruction add namespace Travelmvc.Models { public class Employee { public string EmpName { get; set; } public string EmpPhone { get; set; } public string EmpNum { get; set; } } } in model. I also have the same class in Controller, and I have @using Travelmvc.Models on top of partialview(_Emplopyee). please let me know if I need do any changes for this. thanks – user2949042 Nov 19 '13 at 00:26
  • @user2949042 What version of MVC are you using? – Jean-François Beauchamp Nov 19 '13 at 04:10
  • I am using Visual Studio 2012 professional, and chose MVC template from it, the MVC template should be ASP.NET MVC 4 WEb Application template, and I am using C# and razor view, please let me know if you need more infor. If this is hard to achieve, I also think another option, can I use autocomplate in the textbox and cascating the data to many other fields? for example, the Employee Name field with autocomplete feature, when select one employee, then all related information such as Title, Phone,EmployeeID etc will be populated on other fields. This is another option for my this question. – user2949042 Nov 19 '13 at 13:24
  • @user2949042 I don't understand why you would not have access to ViewBag in the view if you are using MVC 4. What is the exact error message you are getting concerning ViewBag? – Jean-François Beauchamp Nov 20 '13 at 16:59
  • @user2949042 You could also try `@foreach (var employee in (List)ViewBag.Emp) {`instead of `@foreach (var employee in ViewBag.Emp as List) {` – Jean-François Beauchamp Nov 20 '13 at 17:01
  • even tried using your this new codes, it is still not work, the error is on @foreach (var employee in (List)ViewBag.Emp) with error of:Cannot convert type 'string' to 'System.Collections.Generic.List' I also added VieweBag.Emp in the partial view, I can see the data is in format of: data "[{\"Name\":\"1000\",\"DESCRIPTION\":\"1000-Honda Car\",\"Enabled\":true],{\"NAME\":\"1001\",..., I think it is not proper Jason format. So we need to add your Update3 codes with Jason type, how to add "type: Jason" in Update3 code? – user2949042 Nov 21 '13 at 02:20
  • and in my controller side, I did convert the datatable data to Jason format:DataTable dt = null; dt = LookupTable.GetProduct(); ViewBag.Emp = ProdJson(dt); return PartialView("~/Views/Shared/_Employee.cshtml"); – user2949042 Nov 21 '13 at 02:29
  • public string ProdJson(DataTable dt) {System.Web.Script.Serialization.JavaScriptSerializerserializer = new System.Web.Script.Serialization.JavaScriptSerializer(); List> rows = new List>();Dictionary row = null; foreach (DataRow dr in dt.Rows) {row = new Dictionary(); foreach (DataColumn col in dt.Columns) { row.Add(col.ColumnName.Trim(), dr[col]);} rows.Add(row); } return serializer.Serialize(rows);} – user2949042 Nov 21 '13 at 02:35
  • @user2949042 ViewBag is a dynamic object. It has nothing to do with JSON, and I don't understand why ViewBag.Emp is seen as a string. Can you remove that JSON conversion you are doing and post the code as you have it now? – Jean-François Beauchamp Nov 21 '13 at 15:33
  • @user2949042 You can find more information here: http://stackoverflow.com/questions/4705426/whats-the-difference-between-viewdata-and-viewbag. Personally, I would go with the ViewModel approach. I proposed using the ViewBag because you wanted to use a dynamic object, but maybe a List<> is to complex an object to store in the ViewBag. – Jean-François Beauchamp Nov 21 '13 at 15:43
  • @user2949042 Also have a look at [http://www.rachelappel.com/when-to-use-viewbag-viewdata-or-tempdata-in-asp.net-mvc-3-applications](http://www.rachelappel.com/when-to-use-viewbag-viewdata-or-tempdata-in-asp.net-mvc-3-applications) – Jean-François Beauchamp Nov 21 '13 at 15:48
0

Change your controller as:

public ActionResult GetEmployees()
{
    var employeeList = new EmployeeListViewModel {
        Emp = new List<Employee>
        {
           new Employee {  EmpName= "ScottGu", EmpPhone = "23232323", EmpNum="242342"},
           new Employee { EmpName = "Scott Hanselman", EmpPhone = "3435353", EmpNum="34535"},
           new Employee { EmpName = "Jon Galloway", EmpPhone = "4534535345345",   
              EmpNum="345353"}

        }
    };

    return Json(RenderPartialView("_EmpPartial", employeeList ));
}

protected string RenderPartialView(string partialViewName, object model = null)
        {
            if (ControllerContext == null)
                return string.Empty;

            if (string.IsNullOrEmpty(partialViewName))
                throw new ArgumentNullException("partialViewName");

            ModelState.Clear();//Remove possible model binding error.

            ViewData.Model = model;//Set the model to the partial view

            using (var sw = new StringWriter())
            {
                var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, partialViewName);
                var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
                viewResult.View.Render(viewContext, sw);
                return sw.GetStringBuilder().ToString();
            }
        }

And your jquery would be like:

$(document).ready(function () {


      $("#GetEmp").click(function (event) {
        $.post('@Url.Action("Travel", "GetEmployee")', {}, function(resp){
             $('#popup').html(resp);
         });
                $("#popup").dialog({
                    width: 200,
                    hight: 400,
                    title: 'please select an employee',
                    modal: true,

                });
            });
        });
serene
  • 685
  • 6
  • 16