Skip to content
This repository was archived by the owner on Nov 17, 2022. It is now read-only.

Conversation

NickIliev
Copy link
Contributor

Article about the property system and creating/migrating UI plugin in NativeScript 3.0.0.
Source code based on this demo plugin.

@NickIliev NickIliev force-pushed the niliev/properties branch from 398d8e6 to e0ccbba Compare June 8, 2017 07:31
```

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.

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 :)

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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.

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

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.

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.

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.

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

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.

Copy link
Contributor

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`.
Copy link
Contributor

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
```
Copy link
Contributor

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.
Copy link
Contributor

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.
Copy link
Contributor

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.

Copy link
Contributor

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 specify valueConverter and equalityComparer.

“Probably” is not a good word to use in documentation 😄

The docs should spell out exactly when you need valueConverter and equalityComparer.

Copy link
Contributor

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);
Copy link
Contributor

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.

Copy link
Contributor

@tjvantoll tjvantoll left a 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.

@PetyaSotirova
Copy link
Contributor

PetyaSotirova commented Jun 9, 2017

Not all title headings are with the proper casing, e.g. Platform - Specific Code.
Please use imperative mood wherever possible to avoid ambiguity. For example, this sentence:
"We also provide intellisense when myButton.on method is called so that it is known that a tap event is exposed."
Who is "we" - we who develop the framework or we who are going through this tutorial.

```

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.
Copy link
Contributor

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%}).
Copy link
Contributor

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?

Copy link
Contributor Author

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).
Copy link
Contributor

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.
Copy link
Contributor

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.
Copy link
Contributor

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.

@NickIliev
Copy link
Contributor Author

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.

@NickIliev NickIliev closed this Jun 12, 2017
@lock
Copy link

lock bot commented Aug 27, 2019

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.

@lock lock bot locked and limited conversation to collaborators Aug 27, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

6 participants