aadi1295
aadi1295

Reputation: 992

Avoid to show Null or specific values to razor view engine

I am working on asp.net mvc3 web application using MS Sql server 2008 express rc2. In my app I have two different brands in DB and one of them have few Null or 'unknown' values (e.g. 'unknown' is added to DB instead of Null). My question is how to pass only non null values to View Engine instead of using If/Else statements in View?

in controller:

var model = _data.GetViewModel(query);
        if (model != null)
        {                
            return View(model);
        }
        else
            return View("Error");

in ViewModel;

    public int Id { get; set; }
    public string Query { get; set; }
    public string Brand { get; set; }
    public string Family { get; set; }
    public string Type { get; set; }

in Model:

    public ViewModel GetViewModel(string query)
    {
        var data = _comp.Get(p => p.Query == query);
        if (data == null) return null;
        return new ViewModel
        {
            Id = data.id,
            Brand = data.brand,
            Family = data.family,
            Type = data.type
        };
   }

in View (I am currently using If statement):

@if (Model.Brand != null)
{
<span class="brand">@Model.Brand</span> 
}

@if (Model.Family != null)
{
<span class="family">@Model.Family</span> 
}

@if (Model.Type != null)
{
<span class="type">@Model.Type</span>
}

Note: I want to avoid If statement because there are too many values in the Database of each brand, and many of the them are Null, So I don't want to generate Html for those Null values. I am using If/Else statement like above code, and for checking too many values in View using If, it costs Memory on server and processor, and it also slow down server response time.

I want to have an alternative method to do this. Should I use Partial views or anything else? Please Please help me to solve this, Your help is very appreciated. Thanks and Regards.

Upvotes: 1

Views: 6369

Answers (2)

Pure.Krome
Pure.Krome

Reputation: 86937

First, some background/context, then my suggestion.

(By the way, this all applies to any version of ASP.NET MVC or ASP.NET NancyFX (yes, there's another option out there!!), etc)

Context / Background

To solve this, people generally fall into two types of categories:

  1. Just get data and let the View decide what to show (common one, not the proper way IMO).
  2. The Controller should handle all the heavy lifting and give the view the exact answer (proper way, IMO).

The first way is quick and dirty. Sure it works, but it puts too much logic into the view. Views are not supposed to do any logic at all (exception: for loops, and maybe the odd if/else, maybe). The main reason for this is testing. Yep, that dirty word which people hate and think it's for hippies only. Or .. I don't have the time to test.. so I manually test, etc.. If you put any business logic into a view, you cannot test that.

The second way might seem a bit slower at first, but that's like anything - the more you practice, the faster you go. This is (IMO) the preferred method of doing things because you can test the controller. The controller should create a view model which will have -the exact- results that the view needs. Not extra. For example, imagine you want to return a list of Brands to the display/view. Most people do (the equivalent of) Get-all-brands into a list, and send that list to the view, even though 80% of those properties are -not- going to be used by that view! Even if ONE property is not going to be used by that view, do not retrieve it nor send it to the view!

So - TL;DR; do all the heavy lifting in the controller. The View is dumb. Just dump the exact view model data, to the view.

Solution to your problem

Ok, so let's roll with idea #2 and get all this stuff happening in the controller.

// Grab the results.
// ASSUMPTION: It is only returning the -exact- data I need. No more, no less.
var results = _data.GetViewModel(query);
if (model == null)
{                

// Project the results into a perfectly tight & svelte view model 
// 100% specific for this view.
var viewModel = results.
                Select(x => new ViewModel
                {
                    Id = x.Id,
                    Brand = string.IsNullOrEmpty(x.Brand) 
                              ? string.Empty 
                              : x.Brand,
                    Family = string.IsNullOrEmpty(x.Family) 
                              ? string.Empty 
                              : x.Family,
                    Type = string.IsNullOrEmpty(x.Type) 
                              ? string.Empty 
                              : x.Type,
                }).ToList();    


return viewModel;

Testing this..

[Fact]
public void GivenSomeBrands_Index_ReturnsAViewModel()
{
    // Arrange.
    // NOTE: Our fake repostitory has some fake data. In it ..
    //       Id: 1, Brand: Gucci.
    //       Id: 22, Brand: null.
    var controller = new BrandController(SomeFakeRepositoryThingy);

    // Act.
    var result = controller.Index(); // This calls that controller code, above.

    // Assert.
    Assert.IsNotNull(result); // Controller returned some result.
    Assert.IsNotNull(result.Model); // We have some model data.

    var model = result.Model as IList<ViewModel>(); // Cast the Model value.
    Assert.NotNull(model); // We have a strongly typed view model.

    // We check the first brand value.
    Assert.Equal("Gucci", model.First().Brand); 

    // We know this item has a null Brand, 
    Assert.Equal(string.Empty, model[21].Brand); but the ViewModel converted it.
}

Upvotes: 3

user47589
user47589

Reputation:

You could write a custom HTML helper:

public static string MyHelper<V>(this HtmlHelper helper, V value, string css)
{
    if (value == null)
        return "";

    return String.Format("<span class='{0}'>{1}</span>", value, css);
}

Then in your view:

@Html.MyHelper(Model.Brand, "brand");
@Html.MyHelper(Model.Family, "family");
@Html.MyHelper(Model.Type, "type");

Upvotes: 0

Related Questions