I have a class that has a generic List as property, that I have to initialize, and that has a function that returns a card using that list:
class buildCard{
buildCard(this.list);
final List list;
Widget buildCard(int position) {
return Card(child: ListTile(title: list[position].name,),);
}
Now, as you can see, I use
list[position].name
that works for the majority of the stuff that I have, but, in case I will have a list which doesn't have "name" as key, I will have some troubles. How can I avoid this problem?
You can use is to check the type
Widget buildCard() {
if(list[position].name is List) {
return Card(child: ListTile(title: list[position][0].name,),); // or similar - I don't know your exact structure
} else {
return Card(child: ListTile(title: list[position].name,),);
}
}
You have to mix both previous answers to get what you want (credit goes to #GünterZöchbauer)
abstract class Named {
String name;
}
class Person implements Named {
String name;
Person(this.name);
}
class NotAPerson {
String noName;
NotAPerson(this.noName);
}
class BuildCard {
BuildCard(this.list);
final List list;
Widget buildCard(int position) {
if(list[position] is Named) {
return Card(child: ListTile(title: list[position].name,),);
} else {
return Card(child: ListTile(title: list[position].noName,),);
}
}
}
main() {
BuildCard b = BuildCard([Person, NotAPerson]);
b.buildCard(0); // Will access name
b.buildCard(1); // Will not access name
}
You could constrain your List so only objects that have the name property can be passed in.
abstract class Named {
Widget name;
}
class buildCard {
buildCard(this.list);
final List<Named> list;
Widget buildCard(int position) {
return Card(child: ListTile(title: list[position].name,),);
}
}
Related
Actually, I need to pass 'emptylist' to all screens in navigation, through arguments. But I couldn't find any type called 'ListType' in NavType. How can I pass the list to all screens?
fun Navigation(navController: NavHostController) {
NavHost(navController = navController, startDestination = Screen.HomeScreen.route) {
composable(
Screen.HomeScreen.route,
arguments = listOf(
navArgument("empList"){
type= NavType.ListType //Actually there is no ListType in NavType
}
)
) {
HomeScreen(navController)
}
composable(Screen.EmloyeeListScreen.route) {
EmployeeList(navController)
}
composable(Screen.EmployeeDetailsScreen.route) {
EmplyoeeDetails(navController)
}
composable(Screen.ProductScreen.route) {
ProductScreen(navController)
}
}
}
I'm quite new to Typescript and have a bit of a problem to get my code working. I have the following interface/class structure
interface IInterface {
id : number;
method() : string;
}
class IClass implements IInterface {
id : number;
method() : string { return "foo";}
}
Now I want to get some data from a webservice via the following call
$.get("/some/url", (data : Array<IInterface>) => {
for (var i = 0; i < data.length; i++) {
console.log(data[i].id);
console.log(data[i].method());
}
});
While this compiles perfectly fine in typescript and also all properties are set perfectly fine, I get a runtime TypeError data[i].method is not a function
So now my question: How can I cast/assign (?) this correctly, so that the methods are also available in the resulting JavaScript?
UPDATE As requested: a dump of the data I get from the webservice.
data = [{id : 1}, {id : 2}, ...]
Of course, this is a simplified example, the real classes/interfaces have some more properties (they all are assigned correctly), but also only one method (so far, when I get it working, some more will follow).
The problem is what you receive are objects which are complying to the interface, but they don't have a method property inside.
What you need to do in order to get this working is, you need to create new objects from type IClass in the reponse handler which extend the objects inside data:
$.get("/some/url", (data: Array<IInterface>) => {
return data.map((d) => return new IClass(d.id));
});
This is a common problem of converting JSON objects into instances of the classes. There are some proposals discussed here: JSON to TypeScript class instance?
Below is the solution I came up to do such deserialization:
export class Helper
{
public static DESCRIPTOR_SIGN = '$';
private static m_entityModules = [];
private static ReviveDateTime(key: any, value: any): any
{
if (typeof value === 'string')
{
let a = /\/Date\((\d*)\)\//.exec(value);
if (a)
{
return new Date(+a[1]);
}
}
return value;
}
private static RessurectValue(json: any, environments: any[]): any
{
if(json == null)
{
return null;
}
else if(Helper.IsString(json))
{
return json;
}
else if(json instanceof Date)
{
return json;
}
else if(typeof json === 'object')
{
return Helper.RessurectInternal(json, environments);
}
else
{
return json;
}
}
private static RessurectInternal(json: any, environments: any[]): any
{
var instance;
if(!json[Helper.DESCRIPTOR_SIGN])
{
instance = {};
}
else
{
instance = Helper.CreateObject(json[Helper.DESCRIPTOR_SIGN]);
if(Helper.IsUndefined(instance))
{
throw new Error('Unknown type to deserialize:' + json[Helper.DESCRIPTOR_SIGN]);
}
}
for(var prop in json)
{
if(!json.hasOwnProperty(prop) || prop === Helper.DESCRIPTOR_SIGN)
{
continue;
}
let val = json[prop];
instance[prop] = Helper.Ressurect(val, environments);
}
return instance;
}
private static CreateObject(className: string, environments?: any[]): any
{
let instance: any;
for(let e of environments)
{
var construct = e[className];
if(!Helper.IsUndefined(construct))
{
instance = new construct();
break;
}
}
return instance;
}
private static IsNumber(val: any): boolean
{
return typeof val == 'number' || val instanceof Number;
}
private static IsUndefined(val: any): boolean
{
return typeof(val) === 'undefined';
}
private static IsString(val: any): boolean
{
return typeof val == 'string' || val instanceof String;
}
/**
* Deserialize json object object into TypeScript one.
* #param json json object that must have its class name in '$' field
* #param environments list of modules containing all types that can be encountered during deserialization
* #return deserialized typescript object of specified type
*/
public static Ressurect(val: any, environments: any[]): any
{
if(val instanceof Array)
{
if(val.length == 0)
{
return val;
}
else
{
let firstElement = val[0];
if(typeof firstElement !== 'object')
{
return val;
}
else
{
let arr = [];
for (var i = 0; i < val.length; i++)
{
var element = val[i];
arr.push(Helper.RessurectValue(element, environments));
}
return arr;
}
}
}
else
{
return Helper.RessurectValue(val, environments);
}
}
}
Some notes for the above. It works based on the assumption that:
There is a parameterless constructor in every type that can be deserialized.
Every JSON object contain field '$' with its class name inside.
Sample of usage. Lets suppose yu have defined all your serializable classes in one external mode called 'Types'. Then to deserialize JSON object you write:
import * as types from './Types'
//Some code to get jsonObj that has jsonObj.$ set to "RealObject" - a type from "Types" module
let realObj = <types.RealObject>Helper.Resurrect(jsonObj, [types]);
Hope this helps.
I've a parent object (Product) and a child (Inventory). I'm trying to retrieve the value of the child object from a DisplayProducts class that I've created.
public class DisplayProducts {
private Product__c products;
public DisplayProducts(Product__c item) {
this.products = item;
}
// Properties for use in the Visualforce view
public String name {
get { return products.Name; }
}
public String colour {
//error here
get { return products.Inventorys__r.Colour__c; }
}
public class Product {
public List<DisplayProducts> getProducts() {
if(products == null) {
products = new List<DisplayProducts>();
for(Product__c item : [Select ProductID__c,Name, Price__c, (SELECT Inventory__c.Size__c, Inventory__c.Colour__c, Inventory__c.Quantity__c FROM Product__c.Inventorys__r)
From Product__c WHERE ProductID__c = :prodID]) {
products.add(new DisplayProducts(item));
}
}
return products;
}
}
I keep getting a compile error: invalid FK relationship.
I tried
products.Inventorys__r[0].Colour__c;
but it will only retrieved the first element.
How do I retrieve the child item via the DisplayProducts Class? Any help will be greatly appreciated.
Thank you.
The glass mapper will return null object or (no items) for SitecoreQuery and SitecoreChildren attribute that are placed on the GlassModels. These attributes don't take any such parameter where I can specify them to return items if they don't exist in the the context lanaguge. The items e.g. exist in EN but don't exist in en-ES. I need to put a lot of null check in my views to avoid Null exception and makes the views or controller very messy. It is lot of boiler plate code that one has to write to make it work.
In Page Editor the SitecoreChildren returns item and content authors can create items in that langauge version by editing any field on the item. This automatically creates the item in that langauge. However the same code will fail in Preview mode as SitecoreChidren will return null and you see null pointer exception.
SitecoreQuery doesn't return any items in page editor and then Content Authors wont be able to create items in Page editor.
To make the experience good if we can pass a parameter to SiteocreQuery attribute so it disable VsersionCount and returns the items if they dont exist in that langauge.
This is actually not possible. There is an issue on GitHub which would make it easy to create a custom attribute to handle this very easy. Currently you need to create a new type mapper and copy all the code from the SitecoreQueryMapper. I have written a blog post here about how you can create a custom type mapper. You need to create the following classes (example for the SitecoreQuery).
New configuration:
public class SitecoreSharedQueryConfiguration : SitecoreQueryConfiguration
{
}
New attribute:
public class SitecoreSharedQueryAttribute : SitecoreQueryAttribute
{
public SitecoreSharedQueryAttribute(string query) : base(query)
{
}
public override AbstractPropertyConfiguration Configure(PropertyInfo propertyInfo)
{
var config = new SitecoreSharedQueryConfiguration();
this.Configure(propertyInfo, config);
return config;
}
}
New type mapper:
public class SitecoreSharedQueryTypeMapper : SitecoreQueryMapper
{
public SitecoreSharedQueryTypeMapper(IEnumerable<ISitecoreQueryParameter> parameters)
: base(parameters)
{
}
public override object MapToProperty(AbstractDataMappingContext mappingContext)
{
var scConfig = Configuration as SitecoreQueryConfiguration;
var scContext = mappingContext as SitecoreDataMappingContext;
using (new VersionCountDisabler())
{
if (scConfig != null && scContext != null)
{
string query = this.ParseQuery(scConfig.Query, scContext.Item);
if (scConfig.PropertyInfo.PropertyType.IsGenericType)
{
Type outerType = Glass.Mapper.Sc.Utilities.GetGenericOuter(scConfig.PropertyInfo.PropertyType);
if (typeof(IEnumerable<>) == outerType)
{
Type genericType = Utilities.GetGenericArgument(scConfig.PropertyInfo.PropertyType);
Func<IEnumerable<Item>> getItems;
if (scConfig.IsRelative)
{
getItems = () =>
{
try
{
return scContext.Item.Axes.SelectItems(query);
}
catch (Exception ex)
{
throw new MapperException("Failed to perform query {0}".Formatted(query), ex);
}
};
}
else
{
getItems = () =>
{
if (scConfig.UseQueryContext)
{
var conQuery = new Query(query);
var queryContext = new QueryContext(scContext.Item.Database.DataManager);
object obj = conQuery.Execute(queryContext);
var contextArray = obj as QueryContext[];
var context = obj as QueryContext;
if (contextArray == null)
contextArray = new[] { context };
return contextArray.Select(x => scContext.Item.Database.GetItem(x.ID));
}
return scContext.Item.Database.SelectItems(query);
};
}
return Glass.Mapper.Sc.Utilities.CreateGenericType(typeof(ItemEnumerable<>), new[] { genericType }, getItems, scConfig.IsLazy, scConfig.InferType, scContext.Service);
}
throw new NotSupportedException("Generic type not supported {0}. Must be IEnumerable<>.".Formatted(outerType.FullName));
}
{
Item result;
if (scConfig.IsRelative)
{
result = scContext.Item.Axes.SelectSingleItem(query);
}
else
{
result = scContext.Item.Database.SelectSingleItem(query);
}
return scContext.Service.CreateType(scConfig.PropertyInfo.PropertyType, result, scConfig.IsLazy, scConfig.InferType, null);
}
}
}
return null;
}
public override bool CanHandle(AbstractPropertyConfiguration configuration, Context context)
{
return configuration is SitecoreSharedQueryConfiguration;
}
}
And configure the new type mapper in your glass config (mapper and parameters for the constructor):
container.Register(Component.For<AbstractDataMapper>().ImplementedBy<SitecoreSharedQueryTypeMapper>().LifeStyle.Transient);
container.Register(Component.For<IEnumerable<ISitecoreQueryParameter>>().ImplementedBy<List<ItemPathParameter>>().LifeStyle.Transient);
container.Register(Component.For<IEnumerable<ISitecoreQueryParameter>>().ImplementedBy<List<ItemIdParameter>>().LifeStyle.Transient);
container.Register(Component.For<IEnumerable<ISitecoreQueryParameter>>().ImplementedBy<List<ItemIdNoBracketsParameter>>().LifeStyle.Transient);
container.Register(Component.For<IEnumerable<ISitecoreQueryParameter>>().ImplementedBy<List<ItemEscapedPathParameter>>().LifeStyle.Transient);
container.Register(Component.For<IEnumerable<ISitecoreQueryParameter>>().ImplementedBy<List<ItemDateNowParameter>>().LifeStyle.Transient);
You can then simply change the SitecoreQuery attribute on your model to SitecoreSharedQuery:
[SitecoreSharedQuery("./*")]
public virtual IEnumerable<YourModel> YourItems { get; set; }
For the children you could either use the shared query mapper and querying the children or create the same classes for a new SitecoreSharedChildren query.
Edit: Added bindings for IEnumerable<ISitecoreQueryParameter> as they are missing and therefor it threw an error.
XAML RadGrid
<telerik:RadGridView d:DataContext="{d:DesignInstance {x:Type local:A}}" Name="myGridView" Grid.Column="2" ItemsSource="{Binding Path=MyList}" Margin="7,7,7,2" IsFilteringAllowed="False" ShowColumnHeaders="True" AutoGenerateColumns="True" />
C# Code
Class A:INotifyPropertyChanged
{
private List<Fields> MyList;
public event PropertyChangedEventHandler PropertyChanged;
public List<Fields> _theList
{
get
{
if (MyList == null)
{
MyList = new List<Fields>();
}
return MyList;
}
set
{
if (MyList != value)
{
MyList = value;
PropertyChanged(this, new PropertyChangedEventArgs("_theList"));
}
}
}
}
When the items in MyList change dynamically, the radgridview does not update automatically, It works if I reset the Itemssource in the code:
mygridview.Itemssource = Null;
mygridview.Itemssource = MyList;
I have to reset the itemssource everytime in the code after the MyList changes. Why GridView does not update automatically when contents of MyList change?
Also, During design time it shows me appropriate column headers with no data in the columns because the list is empty. But when I run the application, column headers disappear and no data is displayed in the radgrid when contents of MyList change dynamically.
When the items in MyList change dynamically,
You're notifying when your list property changes...which doesn't cover the above scenario. In order to support what you want, I think you need a collection that supports the INotifyCollectionChanged interface.
http://msdn.microsoft.com/en-us/library/system.collections.specialized.inotifycollectionchanged.aspx
Out of the box, ObservableCollection supports this. It derives from IList<T>. So perhaps you could do:
private IList<Fields> MyList;
private IList<Fields> MyObservableList;
public event PropertyChangedEventHandler PropertyChanged;
public IList<Fields> _theList
{
get
{
if (MyObservableList == null)
{
MyObservableList = new ObservableCollection<Fields>();
}
return MyObservableList;
}
set
{
if (MyList != value)
{
MyList = value;
MyObservableList = new ObservableCollection<Fields>(MyList );
// this will throw a null reference exception if no one' is listening. You
PropertyChanged(this, new PropertyChangedEventArgs("_theList"));
}
}
}
If you can forgoe having a List<T> instance for having an ObserableCollection<T> instance, the above could be even simpler:
private ObservableCollection<Fields> MyList;
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Fields> _theList
{
get
{
if (MyList== null)
{
MyList= new ObservableCollection<Fields>();
}
return MyList;
}
set
{
if (MyList != value)
{
MyList = value;
// this will throw a null reference exception if no one' is listening. You should make a method OnPropertyChanged that checks if PropertyChanged != null.
PropertyChanged(this, new PropertyChangedEventArgs("_theList"));
}
}
}
Also, as an aside, usually the private members are _camelCase and the public members are PascalCase...not sure if it's intentional.