Is it possible to sort by a resolved property in HotChocolate? A resolved property can be an arbitrary expression, even some LINQ expression, which I set up in an ObjectType<T>-derived class:
descriptor
.Field("MyResolvedField")
.Type<StringType>()
.IsProjected(false)
.Resolve((context, ct) =>
{
//definition goes here
});
I tried to set up a SortInputType<T>-derived class and on the Configure method explicitly mention my resolved field as:
descriptor.BindFieldsExplicitly();
descriptor.Field("MyResolvedField");
But this always fails with an exception.
Is this even possible at all? If so, how?
Related
From the Lit documentation: "The component shouldn't change its own public properties, except in response to user input."
Also from the documentation: "Internal reactive state works just like public reactive properties, except that there is no attribute associated with the property."
However, when you declare a property, there is an option of setting attribute to false, which prevents an attribute from being associated with the property.
#property({attribute: false})
data = {};
What would be the purpose of doing this? Wouldn't the property just act like internal state at that point?
For reference, Lit already has several ways of declaring internal state variables, either with the #state decorator or setting the state option to true, so I'm just not sure why they allow this too.
I think the main use case for this is for when you have to pass big complex data to the component but want it to be set directly as a property and still get lit to rerender stuff for you.
I think this is easier to visualize with an example, let's say you're making a component which will render a list out of an array passed as a property.
If the array was set as an attribute, it would look something like this:
<list-renderer items='[{id: "1", name: "John Doe"}, {id: "2", name: "Alice Williams"}]'></list-renderer>
Now, this example only has two items, but it could be something way bigger, and that attribute will eventually need to be serialized into an array using JSON.parse() by lit. So, you're just doing an extra step, especially if you already had the array as a JS object rather than JSON data.
So, for this kind of cases it's easier to just force users to set items as a JS property directly.
This will also apply for when you need to pass complex configuration setting objects or functions to the component.
Then again, for most of the components you'll be making, you will probably stick with either having the attribute or making it a fully internal state property.
This way you are really free to use any combination.
A property which acts also as state and attribute
A property which acts also as state but not as an attribute
A state, which is not a property
A property, which is an attribute
A property which is not an attribute
see also https://javascript.info/dom-attributes-and-properties for the difference between properties and attributes.
When I run a build with stencil, I get
[Warn]
The #Prop() name "title" is a reserved public name. Please rename the "title"
prop so it does not conflict with an existing standardized prototype member.
Reusing prop names that are already defined on the element's prototype may
cause unexpected runtime errors or user-interface issues on various browsers,
so it's best to avoid them entirely.
I know it's only a warning but since it may cause unexpected runtime errors, I was wondering if some standard for naming it already existed. (since title was for me the more obvious propriety name to use).
If not, what would be the best practice?
title is a global html attribute name - 'reserved'. See https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes. As a best practice, you shouldn't use global attribute names for #Props. I don't know of any 'standard' for naming a property which is the title of your component but isn't the same as the global title attribute (which is usually used for the tooltip).
As of today, the only workaround I found was to add attributes as property and pass all the information through this variable, like the following
attributes: {
title: string
description: string
/** ... */
}
I didn't found better than that.
But I'm still open to any ideas
Properties and component attributes are strongly connected but not necessarily the same thing.
We cannot use the title variable because of the HTMLElement prop type, but we can set the field text as title like this
#Prop({ attribute: 'title' }) heading?: string;
Consider this setting of a context property:
engine.rootContext()->setContextProperty("text", "hey");
And this qml code:
Text {
text: text
}
This doesn't work as expected, because of the name shadowing.
One fix is to name the context property _text - then there's no problem. But I don't want to have to remember this naming convention (both to remember to use it, and to remember what it means when I read it).
Another fix is to change the context property into a property on the root object:
engine.rootObjects()[0]->setProperty("text", "hey"); // [1]
and in the QML:
text: window.text
Is there any reason to avoid the latter way when it seems so good?
Setting a context property has one disadvantage - it requires recompilation of the C++ code. Naturally, that's a non-issue if you are exposing a C++ object, which is the typical and only logical use case.
It may be seen as an advantage that the context property can be available before the root object is created, but in most cases it will work equally well if it is just a property of the root object. And I clarify that "most cases" here means that object properties are typically not being set from C++ and not being used in initial bindings, since obviously, if you do that, the initial binding will fail to resolve the property when the object is created, before the property is being set. Also note that when creating QML object form C++, you can use the two step process - prepare the object, then set any properties and so on, and only then complete the object creation, which will delay binding evaluations, so you won't have that problem.
Another possible advantage is that you can have more than one root object, thus if the property is in the root context, then all root objects and their subsequent children will be able to resolve it.
Another possible advantage - when you set a context property, you typically get assistance from the IDE, as that object is now known to be out there, so you will have auto-complete for that property name. It sometimes works for parent object properties as well, but it is a hit-or-miss.
As for the speed of access, I am not so sure that a context property would be faster than root object property. I haven't tested it, but it is just logical. Any object property is a de-facto context property, as each object has its own context. Considering that, as you recently discovered, context properties can be shadowed by object properties, it is thus logical to conclude that a context property lookup simply goes down the object contexts until it finds that property rather than going straight to it, so if anything, resolving context properties should be a tad slower than resolving root object properties, because they go one step deeper.
Until line [1] has been executed, window.text is undefined, so at first, you get an error message on the console (though once it gets to executing line [1], the text is displayed). But if you use a context property, you're free to create it before loading the QML file, which removes the problem.
Is there a way of declaring a field of any type?
Nothing seems to be documented, so I tried
foo: {
type: undefined
blackbox: yes
}
My issue:
When I run an update on the collection, it works for scalar values. But if foo is an array, what gets saved is an array of nulls.
I narrowed the issue down to the simple-schema declaration of foo. As a temporary work-around, I've disabled the use of this schema branch for now (blackbox-ed the parent of foo).
Is there a more proper way of declaring the schema for field foo?
No, it's not currently possible:
https://github.com/aldeed/meteor-simple-schema/issues/174
You can try using the blackbox option:
https://github.com/aldeed/meteor-simple-schema#blackbox
I tried to set it as a normal page-property, but no luck.
Guess I could use the DynamicProperty class but I really want to avoid this because of the no-cache issue.
Suggestions anyone?
AFAIK the only way to do this is with the DynamicProperty class. If you look at the documentation on the indexer property on the PageData object it says:
Note! Using this indexer will use the Pre and Post handlers for property lookup. I e return values are not guaranteed to belong to the page, but may be dynamic properties, "fetch-data-from"-data etc. To get data guaranteed to belong to this page, use the GetValueand SetValue methods.
Also note that setting values with this indexer will only set values that acually belong to the page, i e you may get a valid value by reading from the indexer, but trying to set a new value for the same index may yield an exception since the value does not exist in the page.
You will need to use the DynamicProperty class:
DynamicProperty myDynProp = DynamicProperty.Load(CurrentPage.PageLink, "PropertyName");
myDynProp.PropertyValue.Value = "new value";
myDynProp.Save();
Alternatively, you could circumvent the Dynamic Property using an idea Joel discusses here