-
- Notifications
You must be signed in to change notification settings - Fork 447
Property system in NativeScript 3.x.x. #801
Conversation
398d8e6
to e0ccbba
Compare ``` | ||
| ||
The TypeScript transpiler is run by a grunt script. | ||
Being a TypeScript framework, NativeScript uses TypeScript properties. After transpilation, these result in ECMAScript v.5 compliant JavaScript with setter and getter methods to support working with class members, thus ensuring readable and manageable code. This article will guide you through the process of creating basic UI plugin using the property system. All the code is in TypeScript and requires NativeScript 3.x.x or newer version. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sounds out of context to me. What getter & setter methods? What readable & manageable code?
Better remove that fluff :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find the transition from “NativeScript uses TypeScript properties” to “This article will guide you through the process of creating basic UI plugins” a little strange.
Are properties only applicable to UI elements? If so, this article should state that. Should readers only care about this article if they’re building user interface plugins? (If so, this article shouldn’t be considered a “core concept”, because only a handful of our users will find this information valuable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm with TJ here - the content flow feels unnatural as if this article contains two separate topics squished together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tjvantoll, @PetyaSotirova perhaps the focus of this article should be "Creating NativeScript Plugins" and should replace the old UI plugins article? Let me know how you feel about it.
`textProperty: Property<MyButtonBase, string>` - here the owning type is MyButtonBase meaning that this property will be defined on instances of MyButtonBase. The type of the property is `string` so it will accept any text. | ||
| ||
###<a id = "setting"></a>Setting a style property | ||
If the type of the property not `string` we will probably need to specify `valueConverter` and `equalityComparer`. The `valueConverter` will be called if a string value is set to your property (for example from xml or css) and there you will have to convert that string to meaningful value if possible or throw exception if you can't. If `equalityComparer` is specified it will be called everytime a value is set to a property. There you can compare current and new value for equality. For example if your property is of type `Color` you can use `Color.equals` as `equalityComparer` function so even if new instance of `Color` is set the comparer will return `false` if current color and new color have the same `argb` value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing is
If the type of the property isn't
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to your property
may better as to this property
If the type of the property not `string` we will probably need to specify `valueConverter` and `equalityComparer`. The `valueConverter` will be called if a string value is set to your property (for example from xml or css) and there you will have to convert that string to meaningful value if possible or throw exception if you can't. If `equalityComparer` is specified it will be called everytime a value is set to a property. There you can compare current and new value for equality. For example if your property is of type `Color` you can use `Color.equals` as `equalityComparer` function so even if new instance of `Color` is set the comparer will return `false` if current color and new color have the same `argb` value. | ||
| ||
Setting a style property is similar to setting a regular property but you use the nested `style` object (that is a property of the View class, which means that every UI component has style). | ||
There is one more property in the `Property` constructor: `affectsLayout: boolean`. When set to `true` setting new value to this property will trigger a new layout pass. `textProperty` sets `affectsLayout: isIOS`. This means that this property will request new layout pass only for `ios`. This is done as performance optimization. `android` has an integrated layout system so most of the time it will invalidate it self when needed. Thus we skip one native call by defining `affectsLayout` as `true` only for `ios`. But `ios` doesn't have integrated layout system so if you know that your property could affect the layout you should specify it in the `Property` constructor. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is one more property
There is one more option
If the type of the property not `string` we will probably need to specify `valueConverter` and `equalityComparer`. The `valueConverter` will be called if a string value is set to your property (for example from xml or css) and there you will have to convert that string to meaningful value if possible or throw exception if you can't. If `equalityComparer` is specified it will be called everytime a value is set to a property. There you can compare current and new value for equality. For example if your property is of type `Color` you can use `Color.equals` as `equalityComparer` function so even if new instance of `Color` is set the comparer will return `false` if current color and new color have the same `argb` value. | ||
| ||
Setting a style property is similar to setting a regular property but you use the nested `style` object (that is a property of the View class, which means that every UI component has style). | ||
There is one more property in the `Property` constructor: `affectsLayout: boolean`. When set to `true` setting new value to this property will trigger a new layout pass. `textProperty` sets `affectsLayout: isIOS`. This means that this property will request new layout pass only for `ios`. This is done as performance optimization. `android` has an integrated layout system so most of the time it will invalidate it self when needed. Thus we skip one native call by defining `affectsLayout` as `true` only for `ios`. But `ios` doesn't have integrated layout system so if you know that your property could affect the layout you should specify it in the `Property` constructor. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
only for ios
only when executing on ios
If the type of the property not `string` we will probably need to specify `valueConverter` and `equalityComparer`. The `valueConverter` will be called if a string value is set to your property (for example from xml or css) and there you will have to convert that string to meaningful value if possible or throw exception if you can't. If `equalityComparer` is specified it will be called everytime a value is set to a property. There you can compare current and new value for equality. For example if your property is of type `Color` you can use `Color.equals` as `equalityComparer` function so even if new instance of `Color` is set the comparer will return `false` if current color and new color have the same `argb` value. | ||
| ||
Setting a style property is similar to setting a regular property but you use the nested `style` object (that is a property of the View class, which means that every UI component has style). | ||
There is one more property in the `Property` constructor: `affectsLayout: boolean`. When set to `true` setting new value to this property will trigger a new layout pass. `textProperty` sets `affectsLayout: isIOS`. This means that this property will request new layout pass only for `ios`. This is done as performance optimization. `android` has an integrated layout system so most of the time it will invalidate it self when needed. Thus we skip one native call by defining `affectsLayout` as `true` only for `ios`. But `ios` doesn't have integrated layout system so if you know that your property could affect the layout you should specify it in the `Property` constructor. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But ios
doesn't have
Because ios
doesn't have
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because ios
doesn't have integrated layout system if you know that this property could affect the layout you should specify it in the Property
constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I’ve asked this one before: but given the way this paragraph is written I wonder why we wouldn’t just make affectsLayout: isIOS
the default. Everyone reading this will just do that because we tell them to.
| ||
console.log("myProperty of the object " + myClassInstance.toString() + " changed with " + value); | ||
} | ||
The next file is `my-button-base.ts`. In the base file we define all common fields, properties and methods that are applicable for both Android and iOS. At the top of the file we declare our new properties `text: Property` and `myOpacity: CssProperty`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest something like "In my-button-base.ts
we define..." instead of "The next file is my-button-base.ts
. In the base file"
│ ├── include.gradle | ||
└── ios/ | ||
└── Info.plist | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest here to keep the naming of the files consistent with the plugin seed. We discussed it in the morning with @vakrilov and decided that the current seed naming is ok. It's not a big deal but I think it will help the users to map the example with the seed. So it's up to you.
`textProperty: Property<MyButtonBase, string>` - here the owning type is MyButtonBase meaning that this property will be defined on instances of MyButtonBase. The type of the property is `string` so it will accept any text. | ||
| ||
###<a id = "setting"></a>Setting a style property | ||
If the type of the property not `string` we will probably need to specify `valueConverter` and `equalityComparer`. The `valueConverter` will be called if a string value is set to your property (for example from xml or css) and there you will have to convert that string to meaningful value if possible or throw exception if you can't. If `equalityComparer` is specified it will be called everytime a value is set to a property. There you can compare current and new value for equality. For example if your property is of type `Color` you can use `Color.equals` as `equalityComparer` function so even if new instance of `Color` is set the comparer will return `false` if current color and new color have the same `argb` value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"If the type of the property not string
" -> If the type of the property is not string
: a missing "is"
`textProperty: Property<MyButtonBase, string>` - here the owning type is MyButtonBase meaning that this property will be defined on instances of MyButtonBase. The type of the property is `string` so it will accept any text. | ||
| ||
###<a id = "setting"></a>Setting a style property | ||
If the type of the property not `string` we will probably need to specify `valueConverter` and `equalityComparer`. The `valueConverter` will be called if a string value is set to your property (for example from xml or css) and there you will have to convert that string to meaningful value if possible or throw exception if you can't. If `equalityComparer` is specified it will be called everytime a value is set to a property. There you can compare current and new value for equality. For example if your property is of type `Color` you can use `Color.equals` as `equalityComparer` function so even if new instance of `Color` is set the comparer will return `false` if current color and new color have the same `argb` value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing is mentioned about valueChange
. Although it's self-explanatory maybe it would be nice to have some example about it also.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the type of the property not
string
we will probably need to specifyvalueConverter
andequalityComparer
.
“Probably” is not a good word to use in documentation 😄
The docs should spell out exactly when you need valueConverter
and equalityComparer
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additionally to what TJ said, I'd change the tense in this paragraph to present simple. The valueConverter IS called when the property is changed.
__Example 3__ demonstrates how to implement the functionality of changing a notification. It adds a callback function, `onMyPropertyChanged`, that prints a message about a change in a property. | ||
// Overload 'on' method so that it provides intellisense for 'tap' event. | ||
on(event: "tap", callback: (args: EventData) => void, thisArg?: any); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could also think about using TapEventData instead of the default EventData. The example will make more sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left this in a specific comment, but I thought I’d mention this overall too: this feels like an article on how to build UI plugins that happens to be titled “Properties” and put in the “Core Concepts” folder.
Is there a reason that all NativeScript developers need to know about properties? If yes, I think we should start the article with generic property information for non-plugin authors. If no, I think we should rename this article “Building UI Plugins” and put it in the Plugins folder.
Not all title headings are with the proper casing, e.g. Platform - Specific Code. |
``` | ||
| ||
The TypeScript transpiler is run by a grunt script. | ||
Being a TypeScript framework, NativeScript uses TypeScript properties. After transpilation, these result in ECMAScript v.5 compliant JavaScript with setter and getter methods to support working with class members, thus ensuring readable and manageable code. This article will guide you through the process of creating basic UI plugin using the property system. All the code is in TypeScript and requires NativeScript 3.x.x or newer version. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm with TJ here - the content flow feels unnatural as if this article contains two separate topics squished together.
└── ios/ | ||
└── Info.plist | ||
``` | ||
> Note: For more details about the plugin infrastructure in NativeScript refer to [this article]({%slug plugins-infrastructure%}). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the purpose of having this here if it is already available in the Plugins article?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the plugins article, we have the detailed explanation of a NativeScript plugin. Here we are showing what the users will need to go step-by-step through the specific article - e.g. where to place their prerequisite files like my-button.d.ts
or my-button.android.ts
## Basic UI Plugin Structure | ||
| ||
Dependency properties provide valuable features that simplify the creation of a rich User Interface (UI), including: | ||
The first file `index.d.ts` defines the public API of the control (in our case MyButton). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd drop "first" here - the 'index.d.ts file defines...
`textProperty: Property<MyButtonBase, string>` - here the owning type is MyButtonBase meaning that this property will be defined on instances of MyButtonBase. The type of the property is `string` so it will accept any text. | ||
| ||
###<a id = "setting"></a>Setting a style property | ||
If the type of the property not `string` we will probably need to specify `valueConverter` and `equalityComparer`. The `valueConverter` will be called if a string value is set to your property (for example from xml or css) and there you will have to convert that string to meaningful value if possible or throw exception if you can't. If `equalityComparer` is specified it will be called everytime a value is set to a property. There you can compare current and new value for equality. For example if your property is of type `Color` you can use `Color.equals` as `equalityComparer` function so even if new instance of `Color` is set the comparer will return `false` if current color and new color have the same `argb` value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additionally to what TJ said, I'd change the tense in this paragraph to present simple. The valueConverter IS called when the property is changed.
- `initNativeView` - in this method you setup listeners/handlers to the nativeView | ||
- `disposeNativeView` - in this method you clear the reference between nativeView and javascript object to avoid memory leaks as well as reset the native view to its initial state if you want to reuse that native view later. | ||
| ||
In this implementation we use singleton listener (for android - `clickListener`) and handler (for ios - `handler`) in order to reduce the need to instantiate native classes and to reduce memory usage. If possible it is recommended to use such techniques to reduce native calls. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most of the platform specific code above is documented. There are three important methods:
•createNativeView: Override this method, create and return your nativeView.
•initNativeView: Setup listeners/handlers to the nativeView.
•disposeNativeView: Clear the reference between nativeView and javascript object in this method to avoid memory leaks. Reset the native view to its initial state if you want to reuse that native view later.
In Android, avoid access to native types in the root of the module (note that ClickListener is declared and implemented in a function which is called at runtime). This is specific for the V8 snapshot feature which is generated on a host machine where android runtime is not running. What is important is that if you access native types, methods, fields, namespaces, etc. at the root of your module (e.g. not in a function) your code won't be compatible with V8 snapshot feature. The easiest workaround is to wrap it in a function like in the above initializeClickListener function.
In this implementation, we use singleton listener (for android - clickListener) and handler (for ios - handler) in order to reduce the need to instantiate native classes and to reduce memory usage. If possible it is recommended to use such techniques to reduce native calls.
We have discussed the main purpose of this article and I will re-write it so it will be strictly Property System related and not structured like it is a tutorial for a UI plugin. Closing this PR in favor for upcoming new one. |
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Article about the property system and creating/migrating UI plugin in NativeScript 3.0.0.
Source code based on this demo plugin.