I would like to detect a property change from one object and then forward that value (or recompute the value and pass the result) to another object's property. I saw the example from the documentation which demonstrates value forwarding:
class MyModel extends Observable {
StreamSubscription _sub;
MyOtherModel _otherModel;
MyModel() {
...
_sub = onPropertyChange(_otherModel, #value,
() => notifyPropertyChange(#prop, oldValue, newValue);
}
String get prop => _otherModel.value;
set prop(String value) { _otherModel.value = value; }
}
But I don't know where to get the oldValue and newValue from.
I suppose those should be passed as parameters to the callback of onPropertyChange (the third parameter), but that is not the case. The callback provides no parameters. Is this an oversight or am I missing something ?
import 'dart:async';
import 'package:observe/observe.dart';
class MyOtherModel extends Object with Observable {
#observable
String value;
}
class MyModel extends Object with Observable {
StreamSubscription _sub;
MyOtherModel _otherModel = new MyOtherModel();
MyModel() {
///...
_otherModel.changes.listen((crs) {
crs.forEach((PropertyChangeRecord cr) =>
notifyPropertyChange(#prop, cr.oldValue, cr.newValue));
});
}
String get prop => _otherModel.value;
set prop(String value) => _otherModel.value = value;
}
void main() {
MyModel m = new MyModel();
m.prop = 'bla';
m.changes.listen(print);
// initiate change notification
Observable.dirtyCheck();
}
output
[#<PropertyChangeRecord Symbol("value") from: null to: bla>]
Related
So I have a class like Question like bellow:
#JsonSerializable()
class Question {
String id;
String content;
Question({this.id, this.content});
factory Question.fromJson(Map<String, dynamic> json) =>
_$QuestionFromJson(json);
Map<String, dynamic> toJson() => _$QuestionToJson(this);
}
Please keep in mind that those _$QuestionFromJson and _$QuestionToJson comes from this library https://pub.dev/packages/json_serializable
Say I have many class like that which have a fromJson factory and a toJson method. I want to create a base class that contains those 2 method. A base model is easy for toJson as bellow:
abstract class BaseModel {
Map<String, dynamic> toJson();
}
But what about the factory method, I have no idea how to declare them then override it simply like:
#override
factory Question.fromJson(Map<String, dynamic> json) =>
_$QuestionFromJson(json);
EDIT:
My idea of using this is because I want to create a converter utility that I only need to pass in the class of the result like Converter.listFromJson<MyClass>(jsonString). For now, the helper is:
static List<T> listFromJson<T>(jsonString, Function mappingFunction) {
return myJsonMap.map(mappingFunction).cast<T>().toList();
}
so I have to map each item by passing the map function every time I use this helper method:
Converter.listFromJson<Question>(
jsonMap, (item) => Question.fromJson(item));
There'are a few more class that needs to be convert to the list like this. I want to reuse the method without the (item) => Question.fromJson(item) method part. That's why I want to create a base class that have the factory fromJson method so that I can use it in the converter
return myJsonMap.map((item) => BaseModel.fromJson(item)).cast<T>().toList();
then I just simply call
Converter.listFromJson<Question>(jsonMap);
Thank you for your time.
i don't know if i got you correctly, that's what i understood from your question
abstract class BaseModel{
BaseModel();
BaseModel.fromJson(Map<String,dynamic> json);
}
class Question extends BaseModel{
final String id;
final String name;
Question({this.id,this.name}): super();
#override
factory Question.fromJson(Map<String, dynamic> json) {
return Question(
id: json['id'],
name: json['name']
);
}
}
void main(){
Map<String,dynamic> json = {'id': "dsajdas",'name': 'test'};
Question question = Question.fromJson(json);
print('question: ${question.id}');
}
That was my approach but you can't do such a thing. There is a workaround by declaring .fromJson(json) in a variable. Look at my sample codes, hope you can get an idea.
class Categories {
final String id;
String name;
String image;
Categories({this.id, this.name, this.image});
Categories.fromJson(dynamic json)
: id = json['id'],
name = json['name'],
image = json['image'];
}
class CategoriesModel extends AppModel<Categories> {
List<Categories> list = [];
Function fromJson = (dynamic json) => Categories.fromJson(json);
}
class AppModel<T> {
List<T> list = [];
Function fromJson;
List<T> getList() {
if (this.list.isNotEmpty) return this.list;
List<dynamic> list = GetStorage().read('tableName');
list.forEach((data) {
this.list.add(fromJson(data));
});
return this.list;
}
}
The parser is complaining that the property inheritor .list is not subtype of ModelList.list, but LeadsGroup does inherit from Model.
Is this wrong? What is the correct way to do this?
My base class:
abstract class ModelList {
List<Model> get list;
set list(List<Model> n);
}
The inheritor:
class ListLeadsGroup extends ModelList {
List<LeadsGroup> list;
}
class LeadsGroup extends Model {
}
If you have code like
class Foo extends ModelList {}
ModelList ml = new ListLeadsGroup();
ml.list.add(new Foo());
ml.list is of type Model, therefore adding Foo should be legit.
But this very likely is not what you want.
This is why List<ListLeadsGroup> can't override List<Model>.
This should do what you want:
abstract class ModelList<T extends Model> {
List<T> get list;
set list(List<T> n);
}
class ListLeadsGroup extends ModelList<LeadsGroup> {
List<LeadsGroup> list;
}
class LeadsGroup extends Model {
}
just copied from Matan Lurey's comment on Gitter
import 'package:func/func.dart';
class ModelRegistry {
final _factories = <Type, Func0<Model>>{};
Model create(Type type) => _factories[type]();
void register(Type type, Model factory()) {
_factories[type] = factory;
}
}
main() {
var registry = new ModelRegistry();
registry.register(FooModel, () => new FooModel());
var foo = registry.create(FooModel);
}
found a solution using the new keyword covariant. Now the classes that extends ModelList can override the List<Model> list without warnings.
#serializable
abstract class ModelList extends ModifiedModel
implements Model {
Type get listType {
throw new UnimplementedError();
}
List<Model> get list;
set list(covariant List n);
}
I have a custom element <test-object> which looks like this:
#PolymerRegister('test-object')
class TestObject extends PolymerElement with TestBehavior {
TestObject.created() : super.created() {
}
}
The TestBehavior:
#reflectable
class TestModel extends JsProxy {
#Property(notify: true)
num value = 0;
PolymerElement _target;
TestModel(PolymerElement target) {
_target = target;
}
changeBy(num by) {
value += by;
_target.set('testModel.value', value);
}
}
#behavior
abstract class TestBehavior implements PolymerBase {
#Property(notify: true)
TestModel testModel;
PolymerElement _instance;
static ready(instance) {
instance._init(instance);
}
static created(instance) {
instance._instance = instance;
}
_init(PolymerElement instance) {
set('testModel', new TestModel(instance));
}
}
My main app looks like this:
<dom-module id="main-app">
<style>
:host {
display: block;
#apply(--layout-center-center);
}
</style>
<template>
<div>{{testObject.testModel.value}}</div>
<test-object id="obj"></test-object>
</div>
</template>
</dom-module>
#PolymerRegister('main-app')
class MainApp extends PolymerElement {
#Property(notify: true)
TestObject testObject = null;
MainApp.created() : super.created();
ready() {
set('testObject', testObject = $$('#obj') as TestObject);
}
#Listen('click')
clicked([_]) {
testObject.testModel.changeBy(1);
}
#Observe('testObject.testModel.*')
valueChanged([_]) {
window.console.log('Value was changed');
}
}
This is a very simple toy example. I click <main-app> which causes the value in testModel to increase (this works). However, <main-app> doesn't update the div which should display the value. Also, valueChanged is never invoked.
I want to notify testObject about the valueupdate in testModel and propagate this notification up to main-app which than should update its UI via data binding.
Why is this not working?
You can't have a property in model classes, only in components
#Property(notify: true)
num value = 0;
should just be
#reflectable
num value = 0;
The set and notifyPath methods should really be called from your element classes, not the models. This actually simplifies your code a lot, and makes everything work as expected as well. Below I have pasted the new MainApp, TestModel, and TestBehavior classes (I also made some other minor edits).
#PolymerRegister('main-app')
class MainApp extends PolymerElement {
// Defines a read-only property (implicit because of the getter).
#property
TestObject get testObject => $['obj'];
MainApp.created() : super.created();
// I added the 2nd optional argument here, to fix a reflectable error
#Listen('click')
clicked([_, __]) {
// Notify directly here, this is the primary change.
notifyPath('testObject.testModel.value', testObject.testModel.changeBy(1));
}
#Observe('testObject.testModel.*')
valueChanged([_]) {
window.console.log('Value was changed');
}
}
// Removed the `_instance` field.
class TestModel extends JsProxy {
// use #reflectable instead of #Property(notify: true)
#reflectable
num value = 0;
TestModel();
num changeBy(num by) {
value += by;
// Added a return value for convenience
return value;
}
}
#behavior
abstract class TestBehavior implements PolymerBase {
#Property(notify: true)
TestModel testModel = new TestModel();
}
The following code defines the Polymer element
What do I need as a valid default constructor for this class?
My question is what is needed for a proper constructor
import 'package:polymer/polymer.dart';
import 'lib/NPIDefs.dart';
import 'dart:html';
/**
* A Polymer click counter element.
*/
#CustomTag('detail-panel')
class NPIDetailPanel extends PolymerElement {
#published #observable NPIRecord record;
#observable String detailPanelICON = "unfold-less";
NPIDetailPanel.created() : super.created() {
}
setValue(NPIRecord npiRec) {
record = npiRec;
}
void dremoveDetailPanel() {
Element e;
e = shadowRoot.querySelector('#dpanel');
if(e != null) {
e.remove();
}
}
The code below gets a The class 'NPIDetailPanel' does not have a default constructor error
Please show how to do a default constructor
in the definition of the class
void addDetailPanel(Event e) {
NPIDetailPanel e1;
e1 = new NPIDetailPanel();
}
/* How do I add a proper default constructor? */
You can create a new instance of a Polymer element using new Element.tag('some-tag');
Just add a factory constructor that contains this to your Polymer element class.
#CustomTag('detail-panel')
class NPIDetailPanel extends PolymerElement {
factory NPIDetailPanel NPIDetailPanel() => new Element.tag('detail-panel'); // <== added
#published #observable NPIRecord record;
#observable String detailPanelICON = "unfold-less";
NPIDetailPanel.created() : super.created() {
}
setValue(NPIRecord npiRec) {
record = npiRec;
}
void dremoveDetailPanel() {
Element e;
e = shadowRoot.querySelector('#dpanel');
if(e != null) {
e.remove();
}
}
}
see also Instantiating polymer element via dart code
What is the difference between extends Object with Observable and extends Observable as applied to the class below.
The result is the same when the application is run.
library models;
import 'package:polymer/polymer.dart';
class Person extends Object with Observable {
#observable String name;
#observable bool signedAgreement = false;
Person();
Person.from(Person other) {
name = other.name;
signedAgreement = other.signedAgreement;
}
blank() {
name = '';
signedAgreement = false;
}
}
library models;
import 'package:polymer/polymer.dart';
class Person extends Observable {
#observable String name;
#observable bool signedAgreement = false;
Person();
Person.from(Person other) {
name = other.name;
signedAgreement = other.signedAgreement;
}
blank() {
name = '';
signedAgreement = false;
}
}
There no difference in the behaviour between this two declarations.
Here's a quote from Florian Loitsch :
When you extend "Object" with a mixin the first mixin can always take the place of "Object".
The only little difference is in class hierarchy (superclass are not the same) :
import 'dart:mirrors';
abstract class Mixin {}
class A extends Mixin {}
class B extends Object with Mixin {}
main() {
print(reflectClass(A).superclass);
// => ClassMirror on 'Mixin'
print(reflectClass(A).superclass.superclass);
// => ClassMirror on 'Object'
print(reflectClass(B).superclass);
// => ClassMirror on 'dart.core.Object with .Mixin'
print(reflectClass(B).superclass.superclass);
// => ClassMirror on 'Object'
}