Mobx.dart: What's the best way to create a Store model while parsing the json from an api?

Created on 15 Mar 2019  ·  18Comments  ·  Source: mobxjs/mobx.dart

I use the json_annotations package to make the fromJson and toJson part of my models easy to work with since I deal with APIs and pushing data back and forth. I have a project that works with grocery lists and flows like this - GroceryList -> GroceryListItems. I am observing a ObservableList<GroceryList>() that is working as intended, but it's not monitoring changes at the GroceryListItems, so I think I need to add an observable at that level too. I did create a computed object that grabs the current list - this seems to work as a good work around, but not sure if its the best option. Here is my model and the store that I use to keep all of the Mobx pieces together: https://gist.github.com/CalebJTSmith/ceab5397f482163caad612df84887bba

How can I make this code better and work better as I drop down from a GroceryList screen to the details of the GroceryListItems?

question discussion

Most helpful comment

@pavanpodila let me know if I need to make any edits to this..

https://medium.com/@hramnathnayak/visual-studio-code-plugins-for-creating-data-models-and-mobx-dart-store-f14eb4f6b7e9

All 18 comments

I use the quicktype plugin for vscode

https://marketplace.visualstudio.com/items?itemName=quicktype.quicktype

and have the workspace setting for this plugin to

"quicktype.justTypes": false

with this when you copy a JSON and paste it to a file on vscode using the view > command pallet > Paste JSON as code > dart and give your model a name it generate the toJSON and fromJSON functions along with creating the model for you...

I then modify this for mobx by making the class abstract , extend Store , add the part file name create the actions , etc.. ( make sure you do this for the nested classes too )

I wish quicktype plugin would create the abstract class and add the part file too for me.. but this works for me. No matter how big the response json from api is, its a quick way to get the model created...

There is also an online version of quicktype if you would like to give it a try first.

https://app.quicktype.io/

Hope this helps.

Oh nice this is awesome! Solid find here.

... And setup your own snippets in vscode to speed up modify model for mobx... Code > Preferences > user snippets > dart.json

after you type prefix value and enter with this.. paste the class name you can copy from the generated file... will replace all location in the custom snippet..

"Mobx class": {
    "prefix": "cls",
    "body": [
      "class $1 = _$1 with _$$1;",
      "abstract class _$1 implements Store{",
      "$2"
    ],
    "description": "mobx class"
  }

Update - I do not recommend this anymore. See my below post for a better alternative.

@hramnathnayak I think you should blog about all the valuable tips you are sharing about VSCode. If you do write it, I can link it in the Community section of mobx.pub.

I was looking for a better way of converting the model generated from quick-type (from my initial post above) to Mobx Store rather than using snippets which is too time consuming. Here is what I have...
@pavanpodila - Will document this in a blog soon.

vscode Install the "replace rules" plugin from below link
https://marketplace.visualstudio.com/items?itemName=bhughes339.replacerules

edit vscode setting.json to add below rules... more info is available on the plugin marketplace if you need to edit these rules.
"replacerules.rules": { "add mobx part file": { "find": "\n\\s(.*?)\\s(.*?)FromJson", "replace": "\npart '$2.g.dart';\n\n$1 $2FromJson" }, "import mobx dart": { "find": "import 'dart:convert';", "replace": "import 'dart:convert';\nimport 'package:mobx/mobx.dart';" }, "mobx serializer class name update": { "find": "return (.*?).fromJson", "replace": "return _$1.fromJson" }, "mobx factory class name update": { "find": "factory (.*?).fromJson", "replace": "factory _$1.fromJson" }, "create mobx abstract class": { "find": "class\\s*(.*?)\\s*{", "replace": "class $1 = _$1 with _$$$1;\nabstract class _$1 with Store {" }, "update constructor": { "find": "^\\s*(.*?)\\({", "replace": "_$1({" }, "update list to observable list": { "find": "List<(.*?)>(.*?);", "replace": "ObservableList<$1>$2;" }, "update Set to observable Set": { "find": "Set<(.*?)>(.*?);", "replace": "ObservableSet<$1>$2;" }, "update Map to observable Map": { "find": "Map<(.*?),(.*?)>(.*?);", "replace": "ObservableMap<$1,$2>$3;" } }, "replacerules.rulesets": { "convert to Mobx Store": { "rules": [ "add mobx part file", "import mobx dart", "mobx serializer class name update", "mobx factory class name update", "create mobx abstract class", "update constructor", "update list to observable list", "update Set to observable Set", "update Map to observable Map" ] } }

You can run individual rule or the entire rule set from
view > command pallette > Replace Rules:Run Rule
or
view > command pallette > Replace Rules:Run RuleSet

to make this even better add a keyboard shortcut to run the ruleset by going to
code > Preferences > keyboard shortcuts

Edit keybindings.json to add below

{ "key": "ctrl+shift+M", "command": "replacerules.chooseRuleSet", "when": "editorTextFocus && !editorReadonly", "args": { "ruleSet": "convert to Mobx Store" } }

Select your shortcut key on this keybinding - I have selected ctrl+shift+M ( M for Mobx ) .

now after you generate model from quick-type convert it to Mobx Store by hitting ctrl+shift+M and enter.

you can then add the observable annotation to the fields you think need to be observable.

Good Day !

I'm using VSCode snipped with json_serializable and generated code described in official Flutter docs: https://flutter.dev/docs/development/data-and-backend/json#serializing-json-using-code-generation-libraries.

However, I cannot make it work currently with mobx generated files like I mentioned in #120.

There is more incompatibility between two libraries, please ignore above suggestion.

@pavanpodila let me know if I need to make any edits to this..

https://medium.com/@hramnathnayak/visual-studio-code-plugins-for-creating-data-models-and-mobx-dart-store-f14eb4f6b7e9

Looks good @hramnathnayak 👍

Updated article with settings generate store to be compatible mobx 0.2.0

We use the swagger tool for restful API documentation... And I am working with swagger codegen to create mobx stores for all your restful endpoints by providing it the API specifications json or yaml file... Still work in progress ... I think this would be the quickest way to create mobx stores... Will update this space when I am ready to share this.

@hramnathnayak that sounds really cool. Codegen is definitely the way to go! Please share with one of your helpful blog posts :-)

@pavanpodila I am referring to the below blog from David Norman on how use swagger codegen

https://medium.com/capital-one-tech/how-to-make-swagger-codegen-work-for-your-team-32194f7d97e4

along with the documentation on the swagger codegen git hub page

https://github.com/swagger-api/swagger-codegen/tree/3.0.0#to-generate-a-sample-client-library

I got the mobx store generated with this method using a custom "mustache template".

When we add actions , service calls manually to this abstract class generated, we are modifying geneated code .. so in future if the API endpoints have additional fields in response and I want to regenerate the abstract store class we lose the changes done to store manually.

Do you have any suggestions on how we can keep the generated code separate ? Do you think we could use multiple mixing classes ? i.e. something like
abstract class Classname with Classnamemixin, Store{}

Blog I came across on mixins
https://medium.com/flutter-community/dart-what-are-mixins-3a72344011f3

Also do you think we are ok to mark every field on the store class observable although we may not have an observer for it?

Anything else you think we should generate from an API spec json? Service call placeholder classes, documentation readme files? ( Will keep additional items for future updates to mobx-swagger codegen template )

Trying to be as lazy as possible 😀 and have most things generated.

Do you have any suggestions on how we can keep the generated code separate ? Do you think we could use multiple mixing classes ? i.e. something like
abstract class Classname with Classnamemixin, Store{}

Yes, nothing stopping us from doing it as long as each mixin keeps the method/field names distinct.

Also do you think we are ok to mark every field on the store class observable although we may not have an observer for it?

Ya should be absolutely fine. When there are no observers, MobX is smart enough to not even fire a notification :-). So go and mark all your writable fields as @observable

Anything else you think we should generate from an API spec json? Service call placeholder classes, documentation readme files? ( Will keep additional items for future updates to mobx-swagger codegen template )

You can generate a toString() implementation that spits out the name of the class and few useful fields. May be provide some hooks if needed for certain pre/post operations inside methods?

The models and api classes that are generated by swagger codegen are included onto a single library using the part/part of directives. This is required so that we don't have issues with imports when one model includes an instance of another model and this info is not available to codegen templates so that we can use imports instead. So having them included in a single library takes care of this issue for swagger-codegen.

That said the mobx stores cannot have a second part directive that is required by mobx-codegen and then to run build runner script.

I guess the only was out is to include what mobx-codegen does into the swagger-codegen template so that I have the Store and the supporting code as part of mobx-codegen all created with swagger-codegen.

@pavanpodila - I have not yet looked in detail on what mobx-codegen does but I hope I can get this done with swagger-codegen template. Let me know if you have any items that i need to keep in mind while I try to replicate what mobx-codegen does.

@pavanpodila I am facing issue with mobx codegen when I have an additional mixin on the abstract store class. Codegen does not generate code for observables/actions on the additional mixin ( _B ) in below code. I am looking to generate the abstract Store model from an openAPI specification using openapi codegen then use mobx codegen on top. I would like to separate the generated abstract class ( _A ) from the from any additional computed observables/actions the developer may want to may add manually, So i am trying separate that out into a mixin ( _B ).
Appreciate your help with this.

`import 'package:mobx/mobx.dart';

part 'a.g.dart';

class A = _A with _$A;

abstract class _A with _B, Store {
@observable
String myvariable;

@action
doSomething() {}
}

mixin _B {
@action
String doSomethingElse() {}
}
`

The code generator relies on a class to exist as it generates a mixin internally. You may want to model this with classes instead 🙁

I was looking for a better way of converting the model generated from quick-type (from my initial post above) to Mobx Store rather than using snippets which is too time consuming. Here is what I have...
@pavanpodila - Will document this in a blog soon.

vscode Install the "replace rules" plugin from below link
https://marketplace.visualstudio.com/items?itemName=bhughes339.replacerules

edit vscode setting.json to add below rules... more info is available on the plugin marketplace if you need to edit these rules.
"replacerules.rules": { "add mobx part file": { "find": "\n\\s(.*?)\\s(.*?)FromJson", "replace": "\npart '$2.g.dart';\n\n$1 $2FromJson" }, "import mobx dart": { "find": "import 'dart:convert';", "replace": "import 'dart:convert';\nimport 'package:mobx/mobx.dart';" }, "mobx serializer class name update": { "find": "return (.*?).fromJson", "replace": "return _$1.fromJson" }, "mobx factory class name update": { "find": "factory (.*?).fromJson", "replace": "factory _$1.fromJson" }, "create mobx abstract class": { "find": "class\\s*(.*?)\\s*{", "replace": "class $1 = _$1 with _$$$1;\nabstract class _$1 with Store {" }, "update constructor": { "find": "^\\s*(.*?)\\({", "replace": "_$1({" }, "update list to observable list": { "find": "List<(.*?)>(.*?);", "replace": "ObservableList<$1>$2;" }, "update Set to observable Set": { "find": "Set<(.*?)>(.*?);", "replace": "ObservableSet<$1>$2;" }, "update Map to observable Map": { "find": "Map<(.*?),(.*?)>(.*?);", "replace": "ObservableMap<$1,$2>$3;" } }, "replacerules.rulesets": { "convert to Mobx Store": { "rules": [ "add mobx part file", "import mobx dart", "mobx serializer class name update", "mobx factory class name update", "create mobx abstract class", "update constructor", "update list to observable list", "update Set to observable Set", "update Map to observable Map" ] } }

You can run individual rule or the entire rule set from
view > command pallette > Replace Rules:Run Rule
or
view > command pallette > Replace Rules:Run RuleSet

to make this even better add a keyboard shortcut to run the ruleset by going to
code > Preferences > keyboard shortcuts

Edit keybindings.json to add below

{ "key": "ctrl+shift+M", "command": "replacerules.chooseRuleSet", "when": "editorTextFocus && !editorReadonly", "args": { "ruleSet": "convert to Mobx Store" } }

Select your shortcut key on this keybinding - I have selected ctrl+shift+M ( M for Mobx ) .

now after you generate model from quick-type convert it to Mobx Store by hitting ctrl+shift+M and enter.

you can then add the observable annotation to the fields you think need to be observable.

ObservableList has no constructor such as ObservableList.from . Is this still usable ?

I guess the json serialization part of this ruleset does not work as expected. I would suggest setting the “quicktype.justTypes”: true in your vscode and follow the guide on https://mobx.pub/guides/json for the json serialization / deserialization part.

I personally have moved to generating the stores using a custom class using openapi codegen https://github.com/OpenAPITools/openapi-generator as I have a openapi json specification available for my restful services.

Was this page helpful?
0 / 5 - 0 ratings