Wednesday, April 8, 2015

Circling Around MVVM in Polymer


I think MVVM is overkill for most Polymer elements. So tonight I apply it to an extremely simplistic Polymer element to confirm my bias.

I kid. I kid.

Several Patterns in Polymer readers have inquired about MVVM (Model-View-ViewModel) and I have replied that I think it likely overkill. But I have not really investigated—thoroughly or otherwise. I start with the simple case, not to quickly confirm my bias and move one, but to better help me get a grip on the necessary concepts. I tend to read about MV-whatever patterns and then lump them in the same place in my brain. Hopefully this will help.

The simple element remains the initial <hello-you> element in the book:



I continue to use last night's Polymer 0.8 code, mostly so I can continue to get a feel for the new stuff. I have the feeling this may, inadvertently, be a reasonable choice for MVVM (though it is still overkill). The “Fabulousize Me” button point to a method in the backing class that updates colors in the view. That sounds very much like view logic instead of business logic, which should push the code in a View Model.

But I'm getting ahead of myself...

The view in a Polymer element is simple enough. It is the <template> for the Polymer element. For the <hello-you> element, this is the view code:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="hello-you">
  <template>
    <h2>Hello <span>{{your_name}}</span></h2>
    <p>
      <input value="{{your_name::input}}">
      <input type=submit
             value="Fabulousize Me!"
             on-click="feelingLucky">
    </p>
    <p class=help-block>
      <content></content>
    </p>
  </template>
  <script src="hello_you.js"></script>
</polymer-element>
The your_name property comes directly from the model. The ViewModel can proxy it, but it is the purview of the Model. The your_name property is part of the business logic of <hello-you>. The feelingLucky() method on the other hand...

The backing class currently looks like:
var colors = ['red', 'blue', 'green'];
Polymer({
  is: 'hello-you',
  your_name: '',

  feelingLucky: function() {
    var num = Math.floor(colors.length*Math.random());
    this.
      querySelector('h2').
      style.color = colors[num];
  }
});
My first impression is that this is a ViewModel, not a Model. Certainly the feelingLucky() method is ViewModel code since it only interacts with the View. But if this is ViewModel, what does the Model look like?

In this case, I think it is nothing more than:
function Person(name) {
  this.name = name;
}
Again, this is a very simplistic Polymer element, so it makes sense that the Model is simple. As the code evolves, I could add first and last name properties, a full name computed method, and more. Before I get to that, how can I get this into the Polymer ViewModel? I kind of want to declare a you property as:
  // ...
  properties: {
    you: {
      type: Person
    }
  },
  //...
That will not work, however since properties can only be native types like String and Object. Well, the most basic model would be just an Object, so I try:
Polymer({
  is: 'hello-you',
  properties: {
    you: {
      type: Object
    }
  },
  // ...
}
In the View, I give direct access to this you Object:
    <h2>Hello <span>{{you.name}}</span></h2>
    <p>
      <input value="{{you.name::input}}">
      <input type=submit
             value="Fabulousize Me!"
             on-click="feelingLucky">
When I try to enter a name in the <input> field, however, I get:
Uncaught TypeError: Cannot set property 'name' of undefined
Hrm... even the simple Object version of the Model is not making this easy. Perhaps I need to initialize the you property?

I try this in the created() callback:
  // ...
  created: function(){
    this.you = {name: ''};
  }
  // ...
But I get the following when loading the element:
Uncaught SyntaxError: Unexpected identifier
I am unsure why that fails. I can get that working by changing to the ready() lifecycle method, but I eventually realize this is not how Polymer wants me to do this. I should be creating a default value in the properties declaration:
var colors = ['red', 'blue', 'green'];
Polymer({
  is: 'hello-you',

  properties: {
    you: {
      type: Object,
      value: {name: ''}
    }
  },

  feelingLucky: function() {
    var num = Math.floor(colors.length*Math.random());
    this.
      querySelector('h2').
      style.color = colors[num];
  }
});
With that, I have something moving toward MVVM. It does not feel like a real MVVM implementation—at least not without a method or two in the Model. Still, it is a baby step in the right direction. I am unsure how I want to get a real Model tomorrow, but I may start by creating another, UI-less Polymer object as the first try. If nothing else, this should make it easier to get observable properties and methods.


Day #23


No comments:

Post a Comment