We are facing issue in Glass mapper 4.0 where it does not load item children.
Here is our controller class , It is inheriting from GlassController:
public class CarouselController : GlassController
{
public ActionResult GetCarousel()
{
Model = this.GetDataSourceItem<CarouselViewModel>();
return View(Model);
}
}
And here is our View Model:
public class CarouselViewModel:Carousel_Folder
{
[SitecoreChildren]
public virtual IEnumerable<Carousel> Carousels { get; set; }
}
we get only the parent node information not the childeren (carousels) in the result
Here is the result we get:
[Result Image][1]
Also, following classes were generated with TDS:
[SitecoreType(TemplateId = ICarousel_FolderConstants.TemplateIdString )] //, Cachable = true
public partial interface ICarousel_Folder : IGlassBase
{}
Carousel template is inheriting from two templates content base and image base.
I had this issue before, for me i added [SitecoreChildren(IsLazy = false)] to my model and it works fine, in your case it should be like this :
public class CarouselViewModel:Carousel_Folder
{
[SitecoreChildren(IsLazy = false)]
public virtual IEnumerable<Carousel> Carousels { get; set; }
}
It seems that Carousels property is not par of the Carousel_Folder template, that's why your Interface/Class doesn't have something like:
[SitecoreType(TemplateId=ICarousel_FolderConstants.TemplateIdString)]
public partial class Carousel_Folder : GlassBase, ICarousel_Folder
{
[SitecoreField(ICarouselConstants.CarouselsFieldName)]
public virtual IEnumerable<Carousel> Carousels {get; set;}
}
In this case you will need to get the Parent item and get the children manually, i.e.:
var children = parentItem.Children.Select(x => x.GlassCast<Carousel>())
Related
The requirement is to build a custom form tab screen that starts of with an account selector. After choosing the account, the bottom grid is populated with a list of custom DAC's that basically consists of a key of CompanyID, AccountID, SubID that is stored in a custom table. The existing Account DAC does not have a selector component, which I can use for the top selection. The solution I tried to get past this is to create a new DAC but with only one unbound property, but this is giving me issues on the BQL side when setting up the Master/Detail data views for the screen. I will then attempt to setup a master detail relationship between my custom DAC and the Account DAC. Is this the best approach to build this screen, or is there another which I haven't thought of?
Screen Graph (ignore the where and filters, just doing a proof of concept first):
using System;
using PX.Data;
using PX.Data.BQL.Fluent;
using PX.Objects.GL;
namespace IP_GLRestrictions2
{
public class IPGLAcctToSubLinkMaint : PXGraph<IPGLAcctToSubLinkMaint>
{
public PXSave<IPAccount> Save;
public PXCancel<IPAccount> Cancel;
public PXFilter<IPAccount> MasterView;
public PXFilter<IPAcctSubLink> DetailsView;
public SelectFrom<IPAccount>.View AccountView;
public SelectFrom<IPAcctSubLink>
//.Where<IPAcctSubLink.accountID.IsEqual<IPAccount.accountID.FromCurrent>>
.View SubLinkView;
}
}
Custom DAC ( I want to show the Description and Active as unbound fields as soon as a subaccount ID is chosen in the details grid):
[Serializable]
[PXCacheName("Account To Subaccount Link")]
public class IPAcctSubLink : IBqlTable
{
#region AccountID
[PXDBInt(IsKey = true)]
[PXUIField(DisplayName = "Account ID")]
[PXDBDefault(typeof(IPAccount.accountID))]
[PXParent(typeof(SelectFrom<IPAccount>.Where<IPAccount.accountID.IsEqual<IPAcctSubLink.accountID.FromCurrent>>))]
////[PXSelector(typeof(Search<IPAccount.accountID>),
//// typeof(Account.accountCD),
//// typeof(Account.description),
//// SubstituteKey = typeof(Account.accountCD))]
public virtual int? AccountID { get; set; }
public abstract class accountID : PX.Data.BQL.BqlInt.Field<accountID> { }
#endregion
#region Subid
[PXDBInt(IsKey = true)]
[PXUIField(DisplayName = "Subaccount ID")]
[PXSelector(typeof(Search<Sub.subID>),
typeof(Sub.subCD),
typeof(Sub.description),
typeof(Sub.active),
SubstituteKey = typeof(Sub.subCD))]
public virtual int? Subid { get; set; }
public abstract class subid : PX.Data.BQL.BqlInt.Field<subid> { }
#endregion
#region Description
[PXString]
[PXUIField(DisplayName = "Description")]
//[PXDefault(typeof(Search<Sub.description, Where<Sub.subID, Equal<Current<IPAcctSubLink.subid>>>>))]
public virtual int? SubDescription { get; set; }
public abstract class subdescription : PX.Data.BQL.BqlInt.Field<subdescription> { }
#endregion
#region Active
[PXBool]
[PXUIField(DisplayName = "Active")]
//[PXDefault(typeof(Search<Sub.active, Where<Sub.subID, Equal<Current<IPAcctSubLink.subid>>>>))]
public virtual int? SubActive { get; set; }
public abstract class subActive : PX.Data.BQL.BqlInt.Field<subActive> { }
#endregion
If you just need a selector for a specific field in a DAC the easiest way to do that would be via a cache extension.
You can simply add the PXSelector attribute inside the cache extension.
public sealed class CacheExtension : PXCacheExtension<YourDAC>{
public static bool IsActive() => true;
[YourSelectorAttribute]
public int? AccountID { get; set; }
}
I have this tree structure:
Page1
PageA
PageX
PageY
PageB
Page2
I want Page1 and Page2 as well as all child pages. I have created two classes. In one class using sitecore query
[SitecoreQuery("../*[##templateid={GUID}]", IsRelative = true)]
public virtual IEnumerable<ItemModel> Links { get; set; }
In other ItemModel class getting child pages
public class ItemModel
{
[SitecoreId]
public Guid Id { get; set; }
[SitecoreChildren]
public IEnumerable<SideMenuModel> Children { get; set; }
}
This is working fine but now I want to get only those child pages who have some specific template. Please provide me some solution.
It looks like you have most of the code correct but you need to also apply restrictions to the Children property on the ItemModel class.
You can use a SitecoreQuery on the like you have on the parent model:
public class ItemModel
{
[SitecoreId]
public Guid Id { get; set; }
[SitecoreQuery("./*[##templateid={SideMenuModel-GUID}]", IsRelative = true)]
public virtual IEnumerable<SideMenuModel> ChildItems { get; set; }
}
Or you can use the EnforceTemplate attribute on your SideMenuModel class:
[SitecoreType(TemplateId = "GUID", EnforceTemplate = SitecoreEnforceTemplate.Template)]
public class SideMenuModel
{
[SitecoreId]
public Guid Id { get; set; }
}
public class ItemModel
{
[SitecoreId]
public Guid Id { get; set; }
[SitecoreChildren]
public virtual IEnumerable<SideMenuModel> ChildItems { get; set; }
}
Since you have set EnforceTemplate then using the [SitecoreChildren] attribute means that only items matching the template id will be returned, otherwise they will be skipped.
If you need children of children mapped then you should add [SitecoreChildren] property on your SideMenuModel class as well (or refactor your code so the class references itself) or you could use a "get all descendants query (.//*[##templateid={SideMenuModel-GUID}]) although I would recommend that you ur he Content Search API at this point instead.
You can read more in the blog post about Getting child items with Glass.
I have the following set up in my CMS
Under ContactUsPageSettings I have some items that are using a template called ContactUsQuestionsOption and then below that I have some items that are using a template called ContactUsSubQuestionOption.
I want to return all of the ContactUsQuestionsOption in a drop down list, which I have managed to do. The problem I'm having is having a second drop down list that only returns the children of the selected item. Looking at the image above if Faculty Research and Publications was selected I'd want the second drop down to show BSR, Faculty Enquiries and ResearchSupportEnquiries.
I have a view model that looks like this:
[SitecoreType(AutoMap = true)]
public class ContactUsPageSettingsViewModel : ContactUsPageSettings
{
[SitecoreQuery(".//*[##templatekey='contactusquestionsoption']", IsLazy = false, IsRelative = true)]
public IEnumerable<ContactUsQuestionsOption> QuestionsOptionItems { get; set; }
[SitecoreQuery("./child::*", IsLazy = true, IsRelative = true)]
public virtual IEnumerable<ContactUsPageSettingsViewModel> Children { get; set; }
}
And a second view model like this:
[SitecoreType(AutoMap = true)]
public class QuestionsViewModel : ContactUsQuestionsOption
{
[SitecoreQuery(".//*[##templatekey='contactussubquestionsoption']", IsLazy = false, IsRelative = true)]
public virtual IEnumerable QuestionsOptionItems { get; set; }
}
But this doesn't appear to be the answer. Any ideas?
I finally worked out a solution for this. I changed my parent item in GlassBase adding the declaration [SitecoreParent(InferType = true)] above Parent. I can now filter my subquestions based on the parent id.
Thanks
Gemma :)
Since you have 3 unique data templates in Sitecore, you need 3 unique Glass-based models. Also, you don't have to use the query property in any of the classes, you can simply use the Children property.
Side note, though not necessary to accomplish what you are trying to do, you may find it helpful to ensure all of your Glass models have, at least, the properties in the GlassBase class below.
So heres what I would do:
Create a base class that contains these properties and update all other Glass models to inherit this class and they will have the children property properly mapped (as well as the Parent property).
public class GlassBase
{
public Guid Id { get; set; }
[SitecoreParent(InferType = true)]
public GlassBase Parent { get; set; }
[SitecoreChildren(InferType = true)]
public GlassBase Children { get; set; }
}
Now your template-specific models can simply inherit this class:
[SitecoreType(TemplateId = "Id of Data Template", AutoMap = true)]
public class ContactUsPageSettings : GlassBase
{
}
[SitecoreType(TemplateId = "Id of Data Template", AutoMap = true)]
public class ContactUsQuestionsOption : GlassBase
{
}
[SitecoreType(TemplateId = "Id of Data Template", AutoMap = true)]
public class ContactUsSubQuestionOption : GlassBase
{
}
I'm leaving out properties that map to each of the fields in these templates because I don't know what fields you have in each template.
Side note, make sure you set the TemplateId attribute of the SitecoreType decorator at the top of each of your Glass models to ensure they map to the proper data templates in Sitecore. GlassBase does not need the SitecoreType decorator.
Having some issues getting my repository to retrieve information - keeps coming back null. Any Thoughts would be appreciated - new to this and teaching myself.
Repository:
public class CustomerRepository : ICustomerRepository
{
private masterContext context;
public CustomerRepository(masterContext context)
{
this.context = context;
}
public IEnumerable<Customer> GetCustomers()
{
return context.Customer.ToList();
}
public Customer GetCustomerById(int customerId)
{
var result = (from c in context.Customer where c.CustomerId == customerId select c).FirstOrDefault();
return result;
}
public void Save()
{
context.SaveChanges();
}
Controller:
public class CustomerController : Controller
{
private readonly ICustomerRepository _repository = null;
public ActionResult Index()
{
var model = (List<Customer>)_repository.GetCustomers();
return View(model);
}
public ActionResult New()
{
return View();
}
}
MasterContext which i had efc make:
public partial class masterContext : DbContext
{
public masterContext(DbContextOptions<masterContext> options)
: base(options)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>(entity =>
{
entity.Property(e => e.CustomerName).IsRequired();
});
}
public virtual DbSet<Customer> Customer { get; set; }
public virtual DbSet<Order> Order { get; set; }
}
I think you need to create instances of you Context and your Repository. So in your Controller you need to something like this:
private masterContext context = new masterContext();
private ICustomerRepository repository = new CustomerRepository(context);
I assume that you're not using Dependency injection ... if so you just need to create a Constructor for your Controller that takes CustomerRepository as argument:
public CustomerController(ICustomerRepository _repository) {
repository = _repository;
}
If you did not configure your database context, look here: https://docs.efproject.net/en/latest/platforms/aspnetcore/new-db.html
This will than enable you the dependency injection. Everything you than need to do for the Repository is to use
services.AddScoped<ICustomerRepository,
CustomerRepository>();
And I think it could be good to remove the ToList() in the Repository class and remove the Cast List<Customer> in your Controller and use ToList() instead, if it's really needed. Because if you're using it in the View the ienumerable could also work.
I designing an applications which basically has 3 different logic layers:
DB connector (implemented by ADO.NET).
BL the business logic (the only thing the UI knows).
DB repositories (connects between the first two).
The DB repositories are separated into sections of dependency and every final entity is polymorphic to one interface. In some cases there are dependencies between objects inside the same dependency sections - ISectionFactory (hence dependent).
In practice the BL is going to ask for an object of specific type (such as IngrediantType in my example) from the MainFactory (which is a factor for all the DB)
Because of this design I am forced to cast types on the UI - which obviously is a drag.
How can I change the design ?
Here is a brief look of design:
public class MainFactory
{
private Dictionary<Type, ISectionFactory> m_SectionsFactories;
private ISectionFactory treatmentsSectionFactory =
new TreatmentsSectionFactory();
public MainFactory()
{
m_SectionsFactories = new Dictionary<Type, ISectionFactory>
{
{typeof(IngrediantType),treatmentsSectionFactory}
};
}
public IConcreteDataCollection GetConcreteData(Type i_EntitiesName)
{
return m_SectionsFactories[i_EntitiesName]
.GetConcreteData(i_EntitiesName);
}
}
internal interface ISectionFactory
{
IConcreteDataCollection GetConcreteData(Type i_EntitiesName);
}
public class TreatmentsSectionFactory : ISectionFactory
{
private Dictionary<Type, IConcreteDataCollection>
m_ConcreteDataCollections;
private IngrediantTypes m_IngrediantTypes = new IngrediantTypes();
private Ingrediants m_Ingrediants = new Ingrediants();
public TreatmentsSectionFactory()
{
m_ConcreteDataCollections =
new Dictionary<Type, IConcreteDataCollection>();
m_ConcreteDataCollections
.Add(typeof(IngrediantType), m_IngrediantTypes);
m_ConcreteDataCollections
.Add(typeof(Ingrediants), m_Ingrediants);
}
public IConcreteDataCollection GetConcreteData(Type i_EntitiesName)
{
return m_ConcreteDataCollections[i_EntitiesName];
}
}
public interface IConcreteDataCollection : IEnumerable
{
// Iteratable.
IConcreteData GetById(int i_Id);
void AddNewConcreteData(IConcreteData i_ConcreteData);
void UppdateConcreteData(IConcreteData i_ConcreteData);
void DeleteConcreteData(IConcreteData i_ConcreteToDelete);
}
public class IngrediantTypes : IConcreteDataCollection
{
public string TestType { get; set; }
public IConcreteData GetById(int i_Id){}
public void AddNewConcreteData(IConcreteData i_ConcreteData){}
public void UppdateConcreteData(IConcreteData i_ConcreteData){}
public void DeleteConcreteData(IConcreteData i_ConcreteToDelete){}
public IEnumerator GetEnumerator(){}
}
// also implements IConcreteDataCollection
public class Ingrediants : IConcreteDataCollection
{
}
public interface IConcreteData
{
public int Index { set; get; }
} // the final (highest) entity of all DB entities
public class IngrediantType : IConcreteData
{
public int Index { set; get; }
// other set of properties
}
public class Ingrediant : IConcreteData
{
public int Index { set; get; }
public IngrediantType RelatedIngrediantType { set; get; }
// other set of properties
}
public class mainClass
{
public static void main()
{
MainFactory factory = new MainFactory();
var type = typeof(IngrediantType);
// returns a IngrdiantTypes of type (IConcreteDataCollection)
var t = factory.GetConcreteData(typeof(IngrediantType));
// I want to use the IngrediantType without casting !!!
var s = t.GetById(2);
}
}
It's a little hard to tell what's going on here, but I think the key will be to take advantage of generics like so:
public IConcreteDataCollection<T> GetConcreteData<T>()
{
return ...;
}
If I understand your question correctly, this will allow you to say:
var t = factory.GetConcreteData<IngrediantType>();
You will need to change almost every class in your code to use generics.