Shared Properties
Let's continue with our Weather
example (you can find it here).
Define a Shared Property
In freezed, when a property is defined on all constructors, it is automatically considered as being a shared property.
For modddels, you should explicitly define shared properties this way :
@Modddel(
// validationSteps: [...],
sharedProps: [
SharedProp('int', 'temperature'),
],
)
As you can see, you provide an array of SharedProp
. Inside the SharedProp
, you provide the type and the name of the shared property (which should be common to all factory constructors).
Now, you can directly access the temperature
property from the super-sealed classes ValidWeather
, InvalidWeather
and InvalidWeatherValue
, without case-modddels pattern matching :
// before :
final temperature = validWeather.mapWeather(
sunny: (validSunny) => validSunny.temperature,
rainy: (validRainy) => validRainy.temperature,
);
// after
final temperature = validWeather.temperature;
If you want to directly access the temperature
property from the Weather
super-sealed class too, temperature
should first be accessible from the case-modddels Sunny
and Rainy
. Which means that it should be annotated with @withGetter
in both factory constructors :
factory Weather.sunny({
@withGetter required int temperature,
}) // { ... }
factory Weather.rainy({
@withGetter required int temperature,
required double rainIntensity,
}) // { ... }
Now you can do :
final temperature = weather.temperature;
Rules to keep in mind
A shared property should be of the same kind across all case-modddels (either a member or a dependency).
A shared property should have a parameter in all factory constructors.
If you want to share a parameter that is not present in all case-modddels, consider instead declaring it in the case-modddels where it's not present with a Null
type. For example, let's suppose you want to share the rainIntensity
parameter :
factory Weather.sunny({
required int temperature,
// We add this :
Null rainIntensity,
}) // { ... }
factory Weather.rainy({
required int temperature,
required double rainIntensity,
}) // { ... }
Now you can add SharedProp('double?', 'rainIntensity')
.
For IterableEntity/Iterable2Entity2, if the shared property is the member parameter, its type must match the TypeTemplate.
Using a common supertype
The type of the shared property doesn't have to be the same in all factory constructors. Rather, the type specified inside the SharedProp
should be a common supertype.
For example :
@Modddel(
// validationSteps: [...],
sharedProps: [
SharedProp('num', 'price'),
SharedProp('String?', 'summary'),
],
)
class Book extends MultiValueObject<InvalidBook, ValidBook> with _$Book {
factory Book.fiction({
required double price,
required String summary,
}) // { ... }
factory Book.nonFiction({
required int price,
String? summary,
}) // { ... }
//...
}
As you can see, num
is a common supertype of double
and int
, and String?
is a common supertype of String
and String?
.
Shared Properties and Param Transformations
The param transformations (valid, null and non-null) accross the different case-modddels are taken into account for narrowing the type of the shared property in the different super-sealed classes.
For example : Let's say we have this User
SimpleEntity :
@Modddel(
validationSteps: [
ValidationStep([
Validation('incomplete', FailureType<UserIncompleteFailure>()),
]),
ValidationStep([
contentValidation,
]),
],
sharedProps: [
SharedProp('Username?', 'username'),
SharedProp('Age?', 'age'),
]
)
class User extends SimpleEntity<InvalidUser, ValidUser> with _$User {
User._();
factory User.appUser({
@NullFailure('incomplete', UserIncompleteFailure.noUsername())
required NormalUsername? username,
@validParam required ValidAge age,
}) {
return _$User._createAppUser(username: username, age: age);
}
factory User.moderator({
@NullFailure('incomplete', UserIncompleteFailure.noUsername())
required ModeratorUsername? username,
@NullFailure('incomplete', UserIncompleteFailure.noAge())
required Age? age,
}) {
return _$User._createModerator(username: username, age: age);
}
// validate methods ....
}
In this example, User
is a union of two SimpleEntities : AppUser
and Moderator
. They have two shared properties : username
and age
. Username
is a union of two ValueObjects : NormalUsername
and ModeratorUsername
. Age
is a ValueObject.
This table represents the types of these two shared properties in the different super-sealed classes :
username
Username?
Username?
Username
ValidUsername
age
Age?
Age?
Age
ValidAge
For username :
The type of
username
becomes non-nullable inInvalidUserMid
, because at that pointusername
is not null in the two case-modddels (InvalidAppUserMid
andInvalidModeratorMid
) : the two NullFailures have been processed in the previous validationStep.The type of
username
becomes valid (ValidUsername
) inValidUser
, because at that pointusername
is valid in the two case-modddels (ValidAppUser
andValidModerator
) : the contentValidation has been processed in the previous validationStep.
For age :
The type of
age
becomes non-nullable inInvalidUserMid
, because at that pointage
is not null in the two case-modddels :In
InvalidAppUserMid
:age
was already non-nullable.In
InvalidModeratorMid
: the NullFailure has been processed in the previous validationStep.
The type of
age
becomes valid (ValidAge
) inValidUser
, because at that pointage
is valid in the two case-modddels :In
ValidAppUser
:age
was already valid, because it's annotated with@validParam
.In
ValidModerator
: the contentValidation has been processed in the previous validationStep.
This example only showcases the Valid and Non-null param transformations, but it works the same way for Null param transformations : If a shared parameter becomes 'Null' in all case-modddels, then it becomes 'Null' in the appropriate super-sealed class.
Disabling Param Transformations for a Shared Property
Sometimes, you may want to disable a certain kind of param transformation for a shared property.
You can do so easily :
SharedProp('Price?', 'price',
// Ignoring Valid Param Transformations. This means that, even if the
// type of [price] becomes valid in all case-modddels, this shared
// property won't have its type [Price?] narrowed down to [ValidPrice?].
//
ignoreValidTransformation: true,
// Ignoring Non-Null Param Transformations. This means that, even if the
// type of [price] becomes non-nullable in all case-modddels, this
// shared property won't have its type [Price?] narrowed down to [Price].
//
ignoreNonNullTransformation: true,
// Ignoring Null Param Transformations. This means that, even if the
// type of [price] becomes 'Null' in all case-modddels, this shared
// property won't have its type [Price?] narrowed down to [Null].
//
ignoreNullTransformation: true,
),