Skip to content
mike edited this page Apr 27, 2020 · 11 revisions

or Materialization

Sirius has the support of the next types of binding:

  1. Model to View
  2. View to Model
  3. View to View
  4. View to Function
  5. Model to Function

Sirius provides a DSL for creating a binding between two sides. Let's check each of them:

Model to View

We want to reflect changes in a Model to some View. Usually, after a response from our server, we want to do that. Of course, sometimes values of our request to a server are not valid, and in this case, we want to return validation errors also.

class MyModel extends Sirius.BaseModel
  @attrs: ["name"]
  @validate:
    name:
      length: min: 3, max: 10

and our HTML-document

<div id="model2view">
    <div class="model-name"></div>
    <div class="model-errors"></div>
</div>

Okay with it

after that we should define our flow: how to map attributes in the Model to the View? For that goal, Sirius has the Materializer class, which describes all types of transformations:

Sirius.Materializer.build(model, view)
  .field((b) -> b.name) # I can pass function with object which describes all attributes + all validators
    .to((v) -> v.zoom(".model-name")) # pass function or pass directly selector as: '.model-name' or a View: new Sirius.View(".model-name")
  .field((b) -> b.errors.name.length) # errors from validation 
    .to((v) -> v.zoom(".model-errors"))
    .transform((x) -> "errors are: #{x}")     # transform value as you want
  .run()                                      # run transformation    

after that, if we enter some not valid values like "12" we will get: errors are: Required length in range [0..Infinity], given: 2". or valid value will be reflected if we put a correct name.

Do you ask yourself what should I do if a model has a lot of validators?

That's easy:

.field((b) -> b.errors.name.all)    # all validators for the attribute

.field((b) -> b.errors.all)         # all validators for the model

if you want to change any attribute of a view, add class, to change data-* attribute, etc... use the attribute method:

.field((b) -> b.name)
.to('div')
.attribute('class')

Change a default behavior (swap)

...
.handle((zoomed_view, transformed_value) -> zoomed_view.render(transformed_value).append('class')) # add new value to the class attribute

View to Model

Common flow: from input-form to a model, and then from the model to our server, right?

Our model is still the same.

<div id="view2model">
 <input name="model-name" />
</div>

and a materialization:

Sirius.Materializer.build(view, model)
  .field((v) -> v.zoom("input[name='model-name']"))          # from html-input
  .to((b) -> b.name)                                         # to attribute name
  .transform((changes) -> changes.text)                      # changes contain element, attribute, and changes see docs 
  .run()

if I put value to the input, I will see in logs something like that:

DEBUG [MyModel]: [MyModel] set: 'asd' to 'name'

I made the materialization for the attribute, the same flow is possible for a validation.

View to View

<div id="view2view-source">
    <input name="model-name" />
</div>

<div id="view2view-mirror">
    <div class="mirror"></div>
</div>

and the materialization process:

Sirius.Materializer.build(view1, view2)
  .field((v) -> v.zoom("input[name='model-name']"))          # from the input
  .to((v) -> v.zoom('.mirror'))                              # to the div.mirror
  .transform((changes) -> changes.text)                      # from all changes we need only text-property  
  .handle((zoomed_view, transformed) ->                       
    zoomed_view.render(transformed).swap())                  #  zoomed_view = .mirror, tranformed is our text 
.run()

View to Function

<div id="view2function">
    <input name="model-name" />
</div>
Sirius.Materializer.build(view)
  .field((v) -> v.zoom("input[name='model-name']"))
  .to((changes) -> console.log(changes.text))                  # just for printing
.run()  

Model to Function

Sirius.Materializer.build(model)
.field((b) -> b.name)
.to((value) -> console.log("=====> #{value}"))
.field((b) -> b.errors.all)
.to((value) -> console.log("=====> error: #{value}"))
.run()

if we change the name of our model, we will see in the console:

INFO [LengthValidator]: LengthValidator: start validate 'asd123'
DEBUG [MyModel]: Validate: 'MyModel.name = asd123' with 'length' validator, valid?: 'true'
=====> error: Required length in range [0..Infinity], given: 2

or

DEBUG [MyModel]: [MyModel] set: 'asd123' to 'name'
=====> asd123

More about Changes

We have three options of changes: from the text (like in textarea, input), from checked/selected, or from changes in attributes of an element. Sirius has supported in a materialization:

# text:
{text, from, target}

{text, attribute, from, target}

{state, from, target}

state is always boolean value

attribute is an attribute of an element, like class/data, etc...

text is a new value (texted)

from is a zoomed view

target is an original event (click, input, select, checked, and so on)

All of them are available inside transform or handle methods.