Multiple IEnumerable implement in mvc3 on VIew

1k Views Asked by At
@{IEnumerable<BC.Models.APPLICATION> data = ViewBag.list;} 
@Html.Grid(data).Columns(columns =>
{
columns.Add(c => c.APPLICATION_NO).Titled("Application No").Filterable(true);
})

But i want to do something like that :

@if(some conditon)
{
@{IEnumerable<BC.Models.APPLICATION> data = ViewBag.list;} 
}
else
{
@{IEnumerable<BC.Models.RIGHTS> data = ViewBag.list;} 
}
@Html.Grid(data).Columns(columns =>
{
columns.Add(c => c.APPLICATION_NO).Titled("Application No");
})

But its not working can anybody have some idea about it.
Now if i do something like this it works

@if(some conditon)
    {
    @{IEnumerable<BC.Models.APPLICATION> data = ViewBag.list;} 
    @Html.Grid(data).Columns(columns =>
    {
    columns.Add(c => c.APPLICATION_NO).Titled("Application No");
    })
    }
    else
    {
    @{IEnumerable<BC.Models.RIGHTS> data = ViewBag.list;} 
    @Html.Grid(data).Columns(columns =>
    {
    columns.Add(c => c.APPLICATION_NO).Titled("Application No");
    })
    }

My problem is that APPLICATION_NO property present in both Model class so i don`t want to use

    @Html.Grid(data).Columns(columns =>
    {
    columns.Add(c => c.APPLICATION_NO).Titled("Application No");
    })

Twice in my code.

3

There are 3 best solutions below

9
On
@if(some conditon)
{
@{IEnumerable<BC.Models.APPLICATION> data = ViewBag.list;} 
}
else
{
@{IEnumerable<BC.Models.RIGHTS> data = ViewBag.list;} 
}
@Html.Grid(data).Columns(columns =>
{
columns.Add(c => c.APPLICATION_NO).Titled("Application No");
})

The code can't work because data is declared into if blocks. If the grid has to work only on shared fields of the two classes you can think about using an Interface that APPLICATION and RIGHTS will implement and change the code like this:

@{IEnumerable<BC.Models.IAPPLICATION_NO> data = (IEnumerable<BC.Models.IAPPLICATION_NO>)ViewBag.list;} 
@Html.Grid(data).Columns(columns =>
{
columns.Add(c => c.APPLICATION_NO).Titled("Application No");
})

where IAPPLICATION_NO is an interface like:

public interface IAPPLICATION_NO
{
    string APPLICATION_NO { get; }
}

I don't know what APPLICATION_NO is, so I used string and the interface can define only get for grid.

Otherwise, if you need to display different data for those two types you should consider using two views or different grid declaration in the if blocks.

I worked on a sample of my answer on VS:

I attach you the code:

public interface AInterface
{
    string AProperty { get; }
}

public class AList : AInterface
{
    public string AProperty { get; set; }
}

public class BList : AInterface
{
    public string AProperty { get; set; }
}

these are the classes

now the controller:

public class TestController : Controller
{
    public ActionResult Index()
    {
        var random = new Random((int)DateTime.Now.Ticks);

        if (random.Next() % 2 == 0)
            ViewBag.List = new List<AList> { new AList { AProperty = "Atestvalue" } };
        else
            ViewBag.List = new List<BList> { new BList { AProperty = "Atestvalue" } };

        return View();
    }
}

and the view:

@{
    ViewBag.Title = "Index";
    IEnumerable<TestMvcApplication.Interfaces.AInterface> test = ViewBag.List;
}

<h2>Index</h2>

@foreach (var item in test)
{
    <div>
        @item.AProperty
    </div>
}

this solves your problem as you can see

Without using interfaces:

@{IEnumerable<dynamic> data = (IEnumerable<dynamic>)ViewBag.list;} 
@Html.Grid(data).Columns(columns =>
{
columns.Add(c => c.APPLICATION_NO).Titled("Application No");
})

but you lose the IntelliSense completion and if the member is missing I think you receive a runtime error.

I tried your Grid assembly but it uses c# Expression and it's incompatible with dynamic. Another solution could be casting one list to another using LINQ in the controller:

IEnumerable<BC.Models.Application> data;
if (some condition)
{
    data = applicationList; //applicationList's type is IEnumerable<BC.Models.Application>
}
else
{
    data = rightsList.Select(t => new Application { APPLICATION_NO = t.APPLICATION_NO }); //rightsList's type is IEnumerable<BC.Models.RIGHTS>
}

ViewBag.list = data;

In the view you can keep the working code you posted at the top of the question. You have not multitype IEnumerable support because you use only one type but without using a common interface between these classes I think we must go to reflection but I think it's hard to write that code.

0
On

Why not

@{string columnName = "default column name";
if(some_condition)
{
    // I'm not sure what's going on with the data variable
    columnName = "alternate column name";
}
else
{
    // Again, do your stuff with the data variable
}
}
@Html.Grid(data).Columns(columns =>
{
    columns.Add(c => c.APPLICATION_NO).Titled(columnName);
})

I think that you're going to end up with (at best) confusing code if you try to be too clever in your views. A grid for rendering IEnumerable<A> isn't adaptable to rendering IEnumerable<B> unless A:ISomeInterface and B:ISomeInterface and the grid renders ISomeInterface. Alternatively just pass the column name as a property of the view model.

1
On

Your problem is that you are not using when of the most important concepts in MVC: view models.

As for your last comment, what you want to use is a view model, i.e. a class created specifically to send data to the view to show it.

To do so:

  1. create a public class which has the APPLICATION_NO (needed for the view model class)
  2. create another public class which will be your view model. That's a class that shapes the data as you need it on the razor template (in this case, it will hold a list of the class defined in 1)
  3. in your controller, return the view passing the model as second parameter. I.e. don't use the ViewBag/ViewData, but a view model instead, like this return View("ViewName", model)
  4. use the model in your view: declare the model type using @model and use it inside the Razor templae with the provided Model variable

In this way, you shape the data on the server, and avoid including a lot of code in you Razor template (which is not advisable). And, of course, you have intellisense, and your templates become typed.

Code for 1:

public class ApplicationModel
{
   public int APPLICATION_NO {get; set;}
}

Code for 2:

public class ApplicationsViewModel
{
   public List<ApplicationModel> Applications { get; set; }
}

Code for 3 (inside the controller)

var model = new ApplicationsViewModel();

if (...) // include the condition to choose the kind of data inside the list
{
   model.Applications = list.Select(item =>
     new ApplicationModel { APPLICATION_NO = item.APPLICATION_NO } ).ToList()
}
else
{
   // same code here, for the other kind of data in the list
}

// return the view with this model
return View("ApplicationView", model);

Code for 4:

// decalre the model type at the beginning
@model YourNamespace.ApplicationViewModel;

// access the model using the Model variable
@Html.Grid(Model.Applications).Columns(columns =>
  {
  columns.Add(c => c.APPLICATION_NO).Titled("Application No");
  })

This allows you to build MVC applications with several advantages, for example testability, code reusing, readability or availability of a ModelState. Belive me, many of theses things are really,really important, specially the ModelState.

Besides you can use code annotations (attributes that give extra info to the HTML helpers) in your view model, that attributes can provide labels, validation and some other automatic functionality.

Don't doubt to include all the needed properties inside your view model.

Using a view model allows you to create an ad-hoc class without the need to change you domain or business layer classes, i.e. you don't need to use interfaces, code annotations and so on. However many times it's interesting to add the code annotations in the business classes and nest them inside the view models.

Remember tha sometimes you can use the same view model to post the data back to the server, specifying it as the paramter type of your POST action.

By the way, the interface solution is a good one, but this solution doesn't required the interface. (This solution would have a better implementationusing that interface, but that's your choice).