5

How do I bind a Custom Item generated with "Custom Item Generator" to my view model for a controller rendering? There is a data source that is returning a list of the Custom Items. Also, one of the custom items is an image. It sounds like I should be using Glass Mapper, but it's not clear to me what it does and how that relates to what I am trying to do. This code does not build.

View:

@using Sitecore.Mvc.Presentation
@using Sitecore.Mvc
@model MyCompanyBootstrap.CustomItems.RotatorContentItem

@foreach (var item in Model.RotatorItems)
{
    <h2>@item.Fields["Title"]</h2>
    @item.Fields["Sub Title"]
}
@Model.Title
<br />
@Model.Text

Controller Action:

public ActionResult Index()
{
    var dataSourceId = RenderingContext.CurrentOrNull.Rendering.DataSource;
    var dataSource = Sitecore.Context.Database.GetItem(dataSourceId);
    var viewModel = new RotatorContentItem();
    viewModel.RotatorItems = dataSource.Children;
    viewModel.Initialize(RenderingContext.Current.Rendering);
    return View(viewModel);
}

Custom Item:

public partial class RotatorContentItem : CustomItem
{

public static readonly string TemplateId = "{E493DF4A-E97E-49B5-BF9F-F70397EA8D21}";


#region Boilerplate CustomItem Code

public RotatorContentItem(Item innerItem) : base(innerItem)
{

}

public static implicit operator RotatorContentItem(Item innerItem)
{
    return innerItem != null ? new RotatorContentItem(innerItem) : null;
}

public static implicit operator Item(RotatorContentItem customItem)
{
    return customItem != null ? customItem.InnerItem : null;
}

#endregion //Boilerplate CustomItem Code


#region Field Instance Methods


public CustomTextField Title
{
    get
    {
        return new CustomTextField(InnerItem, InnerItem.Fields["Title"]);
    }
}


public CustomTextField SubTitle
{
    get
    {
        return new CustomTextField(InnerItem, InnerItem.Fields["Sub Title"]);
    }
}


public CustomImageField ImageLarge
{
    get
    {
        return new CustomImageField(InnerItem, InnerItem.Fields["Image Large"]);
    }
}


public CustomImageField ImageSmall
{
    get
    {
        return new CustomImageField(InnerItem, InnerItem.Fields["Image Small"]);
    }
}


public CustomGeneralLinkField Link
{
    get
    {
        return new CustomGeneralLinkField(InnerItem, InnerItem.Fields["Link"]);
    }
}


public CustomCheckboxField IsOverlayBlack
{
    get
    {
        return new CustomCheckboxField(InnerItem, InnerItem.Fields["Is Overlay Black"]);
    }
}


#endregion //Field Instance Methods
}

Edit: There are a number of things wrong with this code that I am seeing now. I think there should be a new class specifically for the View Model itself. And that class should have a list of RotatorContentItems.

public List<RotatorContentItem> RotatorContentItems { get; set; }

But, it is not clear to me how to bind that list in the controller action.

public class MyViewModel
{
    public List<RotatorContentItem> RotatorContentItems { get; set; }
    public IEnumerable<Item> RotatorItems { get; set; }
}

public ActionResult Index()
{
    var dataSourceId = RenderingContext.CurrentOrNull.Rendering.DataSource;
    var dataSource = Sitecore.Context.Database.GetItem(dataSourceId);
    var viewModel = new MyViewModel();
    viewModel.RotatorContentItems = dataSource.Children;
    viewModel.RotatorItems = dataSource.Children;
    return View(viewModel);
}

This will not build on the following line:

viewModel.RotatorContentItems = dataSource.Children;

Error is:

Cannot implicitly convert type 'Sitecore.Collections.ChildList' to 'System.Collections.Generic.List<MyCompnayBootstrap.CustomItems.RotatorContentItem>'
  • It doesn't appear so from this post, but just to clarify, are you using any type of ORM, like Glass.Mapper? – LonghornTaco Oct 6 '16 at 14:31
  • No, I'm not using Glass Mapper, I have not learned how to use that yet and it is not used in the site I inherited. Do I need to use Glass Mapper? Does it do something different than the Custom Item Generator? – ADH Oct 6 '16 at 14:32
  • 1
    @ADH What problem are you having exactly? The code you've posted makes it look as though your RotatorContentItem is getting passed to your view properly. – Corey Smith Oct 6 '16 at 14:39
  • 1
    @ADH Your question isn't being down voted because you aren't using Glass Mapper; asking questions about how to use the Sitecore API are perfectly valid and encouraged. Your question is being down voted because it's too vague--we didn't find out your exact problem until 7 comments later. The fact that your code isn't working because it doesn't build is something you need to include in your question from the start. – Corey Smith Oct 6 '16 at 15:00
  • 1
    You won't have an question downvoted or closed for not using GlassMapper (or any ORM for that matter). What you must do in a question is provide enough information for it to be answered. You say your code is not building, what is the error you are getting? Include that in your question. – Kasaku Oct 6 '16 at 15:01
1

The problem is that on MyViewModel your RotatorContentItems are of type List<RotatorContentItem>, but dataSource.Children is of type Sitecore.Collections.ChildList.

View Model:

public class MyViewModel
{
    public List<RotatorContentItem> RotatorContentItems { get; set; }
}

You're not using RotatorItems at all so no need to add that property to MyViewModel.

Controller Action:

public ActionResult Index()
{
    var dataSourceId = RenderingContext.CurrentOrNull.Rendering.DataSource;
    var dataSource = Sitecore.Context.Database.GetItem(dataSourceId);

    var viewModel = new MyViewModel
    {
        RotatorContentItems = dataSource.Children.Select(c => new RotatorContentItem(c)).ToList()
    };

    return View(viewModel);
}

View:

@using Sitecore.Mvc.Presentation
@using Sitecore.Mvc
@model MyViewModel

@foreach (var item in Model.RotatorContentItems)
{
    <h2>@item.Title.Text</h2>
    @item.SubTitle.Text
}
  • This seems close but there are 2 issues. One is that I needed to add .Value for the Title and Text fields. The other is an Object reference not set to an instance of an object error on the "var viewModel = new MyViewModel()" line. – ADH Oct 6 '16 at 16:27
  • Move initialization of RotatorContentItems to a separate line. viewModel.RotatorContentItems = dataSource.Children.Select(c => new RotatorContentItem(c)).ToList(). Where do you get the object reference error now? – Corey Smith Oct 6 '16 at 16:37
  • I have this working, but not sure how to give you the code. – ADH Oct 6 '16 at 16:43
  • @ADH I incorporated your changes. – Corey Smith Oct 6 '16 at 16:50
5

I believe your problem is when you're instantiating the custom item. You want to pass an Item to the CustomItem constructor in order to use the properties.

Try this:

public ActionResult Index()
{
    var dataSourceId = RenderingContext.CurrentOrNull.Rendering.DataSource;
    var dataSource = Sitecore.Context.Database.GetItem(dataSourceId);
    var viewModel = new RotatorContentItem(datasource);
    viewModel.RotatorItems = dataSource.Children;
    viewModel.Initialize(RenderingContext.Current.Rendering);
    return View(viewModel);
}

This code will be repeated in every controller you create, so it is worth abstracting it to a base class that your controllers would inherit from, and you can pass the CustomItem type as a type argument. You can do something like this,

public abstract class BaseCustomItemController<T> : where T is CustomItem
{
  public virtual ActionResult Index()
  {
    var dataSourceId = RenderingContext.CurrentOrNull.Rendering.DataSource;
    T viewModel = Sitecore.Context.Database.GetItem(dataSourceId);
    return View(viewModel);
  }
}

Glass.Mapper is worth learning and probably a better choice for working with MVC. You can use the GlassViewModel class it ships with and skip controllers altogether and just use View Renderings.

  • 2
    Boom! I would add that utilizing "CIG" is probably something you should look at moving away from, and going towards Glass.Mapper, which as mentioned above is the better choice for MVC implementations. – Pete Navarra Oct 6 '16 at 15:18
  • 2
    It's a lot harder to test View Renderings, and anything that uses any logic should be handled in a controller anyhow. It's very, very easy to use something like Glass or Fortis to get strongly typed datasources to your controllers. – Jon Upchurch Oct 6 '16 at 15:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.