193 Lotus blogs updated hourly. Who will post next? Home | Blogs | Search | About 
 
Latest 7 Posts
Goodbye Evernote
Tue, Jan 17th 2017 5
Merry Christmas!
Sun, Dec 25th 2016 8
Visual Studio Code Editor
Fri, Nov 11th 2016 10
Work with Rich Text from DDS
Fri, Oct 21st 2016 7
A Polymer Avatar component
Wed, Oct 12th 2016 6
MWLUG Recap
Sat, Aug 20th 2016 10
Red Pill is the MWLUG Sponsor of the Week
Fri, Aug 12th 2016 9
Top 10
MWLUG Recap
Sat, Aug 20th 2016 10
Visual Studio Code Editor
Fri, Nov 11th 2016 10
Red Pill is the MWLUG Sponsor of the Week
Fri, Aug 12th 2016 9
My first impressions of using Titanium Appcelerator
Sun, Dec 8th 2013 8
Re-usability is the Goal!
Tue, Jun 14th 2016 8
sortablejs – Drag-n-Drop without jQuery UI
Thu, Jul 21st 2016 8
Merry Christmas!
Sun, Dec 25th 2016 8
IBM Please fix Domino authentication
Tue, May 24th 2016 7
I’m speaking at MWLUG 2016
Tue, Jun 28th 2016 7
Polymer app-layout Elements
Fri, Jul 1st 2016 7


Re-usability is the Goal!
Twitter Google+ Facebook LinkedIn Addthis Email Gmail Flipboard Reddit Tumblr WhatsApp StumbleUpon Yammer Evernote Delicious
Keith Strickland    

When developing modern web applications, we want to build components that are re-usable. The more reusable it is, the better. When things are re-usable we get a lot of benefits going forward with future applications, mainly speed of development. But we also get the ability to update a component in one place and the next update to any app that uses that component, we just get the new version.

One thing that is pretty popular in Domino apps and some modern web apps is a picker. A list of items on the left which you select from, then move the things which are selected into another list on the right. With web components we can create a web component for this and then use that component in multiple applications. In this article I am going to walk you through building a picker component that we can use in any application which needs that sort of functionality. This component will be built with Polymer and be infinitely re-usable. I will not go through the process of setting up your development workstation in this article (maybe that should be a future article).

When creating a component it has a pretty generic pattern of construction. I’ve setup a snippet in Sublime Text to help with this pattern just to help speed things up a bit. The pattern of a polymer web component is as follows:

<link rel="import" href="../polymer/polymer.html">
<dom-module id="now-standalone-picker">
	<template>
		<style is="custom-style">
			:host {
				display: block;
				box-sizing: border-box;
			}
		</style>
		<!-- HTML Goes Here -->
	</template>
	<script>
Polymer({
	is: 'now-standalone-picker'
});
	</script>
</dom-module>

 

All components should start this way. We’ve got a few things going on here:

  1. We import polymer to get the Polymer library
  2. We define our component. The id will be what the tag name we want to use will be
  3. We define a style for our component. This will be constrained to just this component and will not bleed out into other parts of the application
  4. We have a place to enter HTML which will be the construction of our component
  5. Finally we define our component in JavaScript which will contain all the functionality of our component

Next up, we want to define the HTML for our component. This is how it will look. We don’t need to do anything special here, we can use Polymer Elements, plain-ole html, CSS, etc. We’ll replace the ‘HTML Goes Here’ line with our html:

<link rel="import" href="../polymer/polymer.html">

<link rel="import" href="../iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="../iron-icon/iron-icon.html">
<link rel="import" href="../iron-input/iron-input.html">

<link rel="import" href="../paper-icon-button/paper-icon-button.html">
<link rel="import" href="../paper-item/paper-item.html">
<link rel="import" href="../paper-menu/paper-menu.html">

<dom-module id="now-standalone-picker">
	<template>
		<style is="custom-style" include="iron-flex iron-flex-alignment">
			:host {
				display: block;
				box-sizing: border-box;
			}
		</style>
		<div class="layout horizontal">
			<div class="layout vertical flex selectionContainer">
				<div class="searchFieldContainer layout horizontal">
					<input
						id="selectionSearch"
						is="iron-input"
						bind-value="{{searchValue}}"
						name="searchValue"
						placeholder="Search Items">
					</input>
					<span on-tap="_clearSearch" hidden$="{{!searchValue}}">X</span>
					<div id="searchIconContainer" class="searchIconContainer" title="Perform the search">
						<iron-icon icon="search"></iron-icon>
						<paper-ripple></paper-ripple>
					</div>
				</div>
				<div class="selectionList">
					<paper-menu
						id="selectionMenu"
						multi>
						<template is="dom-repeat" items="{{selectionList}}" as="item">
							<paper-item
								value="[[item.title]]">
									[[item.title]]
							</paper-item>
						</template>
					</paper-menu>
				</div>
			</div>
			<div class="layout vertical self-center addRemoveButtonList">
				<paper-icon-button id="addSelected" icon="arrow-forward" on-tap="_addSelected" title="Add Selections"></paper-icon-button>
				<paper-icon-button id="removeSelected" icon="arrow-back" on-tap="_removeSelected" title="Remove Selections"></paper-icon-button>
			</div>
			<div class="layout vertical flex selectedContainer">
				<span class="selectionTitle">
					{{rightSideTitle}}
				</span>
				<div class="selectedList">
					<paper-menu
						id="selectedMenu"
						multi>
						<template is="dom-repeat" items="{{selectedList}}" as="selectedItem">
							<paper-item
								value="[[selectedItem.title]]">
								[[selectedItem.title]]
							</paper-item>
						</template>
					</paper-menu>
				</div>
			</div>
		</div>
	</template>
	<script>
Polymer({
	is: 'now-standalone-picker'
});
	</script>
</dom-module>

Ok, we’ve added the html but there’s some other stuff going on here. I’ve highlighted the pieces we’ve modified:

  1. At the top, notice we have a bunch of link tags with a ‘rel’ of ‘import’? Those are actually including the Polymer components we want to use in our component. We have to load them so that they are available for us to use.
  2. The opening style tag now also includes some weirdness. We’ve added an attribute called ‘include’ with some stuff there. This is importing Polymer’s Flex Layout classes so we can use those in our html also. If you look at the html you’ll find style classes like ‘layout horizontal’, ‘layout vertical’, ‘self-center’, etc. The layout classes provide that functionality for us.
  3. In the html you’ll notice stuff like ‘{{foo}}’ or ‘[[foo]]’. What’s that all about. Well, those will end up being properties of this component. We can bind to those properties and things will just update when they change. Polymer’s Data Binding is one of the biggest reasons to use Polymer in my mind. It’s very similar to Angular’s data binding at least in it’s feel, no clue if they’re similar in structure. Also notice that there are these ‘template’ tags with an ‘is’ attribute of ‘dom-repeat’. Think of these as an XPages repeat component. You give them a list and they’ll just iterate that list stamping out a DOM element for each item in the list. One thing of note with Polymer. The items inside an element that reside inside a template, you can not get to that element using Polymer’s DOM query functions (i.e. this.$.someElemId).
  4. Also notice that there is an ‘on-tap’ attribute on some of the elements? This is another way to define an event handler. In this case, on-tap corresponds to a tap on a touch device or a click from a mouse.

We should now have something that looks like this when we view this component in a browser. Not very exciting huh?

now-standalone-picker-no-styles

Next step is to add some styles, I won’t go over the styles except for the stuff that’s not what you would normally expect to see in CSS.

<link rel="import" href="../polymer/polymer.html">

<link rel="import" href="../iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="../iron-icon/iron-icon.html">
<link rel="import" href="../iron-input/iron-input.html">

<link rel="import" href="../paper-icon-button/paper-icon-button.html">
<link rel="import" href="../paper-item/paper-item.html">
<link rel="import" href="../paper-menu/paper-menu.html">

<dom-module id="now-standalone-picker">
  <template>
    <style is="custom-style" include="iron-flex iron-flex-alignment">
      :host {
        display: block;
        box-sizing: border-box;
      }
      .selectionList {
        border: 1px solid #afafaf;
        margin-right: 5px;
        height: 150px;
        overflow: auto;
        background-color: white;
      }
      .selectionList {
        margin-top: 10px;
      }
      .selectionList paper-menu paper-item, .selectedList paper-menu paper-item {
        font-size: 12px;
        min-height: 20px;
      }
      .selectionList paper-menu paper-item:hover {
        cursor: pointer;
        background-color: #efefef;
      }
      .selectedList {
        border: 1px solid #afafaf;
        height: 150px;
        overflow: auto;
        margin-top: 16px;
        background-color: white;
      }
      .selectionContainer input {
        background-color: white;
        border: 1px solid var(--default-primary-color);
        line-height: 20px;
        padding: 2px 2px 2px 10px;
        width: 100%;
        font-size: 12px;
      }
      .selectionContainer input:focus {
        outline: none;
        border-color: var(--default-primary-color);
        box-shadow: 0 0 10px var(--default-primary-color);
      }
      .selectionContainer input::-webkit-input-placeholder {
        font-size: 12px;
      }
      .selectionContainer input::moz-placeholder {
        font-size: 12px;
      }
      .selectionContainer input:moz-placeholder {
        font-size: 12px;
      }
      .selectionTitle {
        font-size: 12px;
        font-weight: bold;
        line-height: 20px;
      }
      .selectionContainer input:-ms-input-placeholder {
        font-size: 12px;
      }
      .searchFieldContainer span {
        position: absolute;
        display: block;
        top: 23px;
        left: 233px;
        width: 16px;
        height: 16px;
        cursor: pointer;
      }
      .searchIconContainer {
        height: 26px;
        background-color: var(--default-primary-color);
        position: relative;
      }
      .searchIconContainer:hover {
        cursor: pointer;
      }
      .searchIconContainer iron-icon {
        height: inherit;
        background-color: var(--default-primary-color);
        border: 1px solid var(--divider-color);
        border: 0;
        color: white;
      }
    </style>
    <div class="layout horizontal">
      <div class="layout vertical flex selectionContainer">
        <div class="searchFieldContainer layout horizontal">
          <input
            id="selectionSearch"
            is="iron-input"
            bind-value="{{searchValue}}"
            name="searchValue"
            placeholder="Search Replicas">
          </input>
          <span on-tap="_clearSearch" hidden$="{{!searchValue}}">X</span>
          <div id="searchIconContainer" class="searchIconContainer" title="Perform the search">
            <iron-icon icon="search"></iron-icon>
            <paper-ripple></paper-ripple>
          </div>
        </div>
        <div class="selectionList">
          <paper-menu
            id="selectionMenu"
            multi>
            <template is="dom-repeat" items="{{selectionList}}" as="item">
              <paper-item
                value="[[item.title]]">
                  [[item.title]]
              </paper-item>
            </template>
          </paper-menu>
        </div>
      </div>
      <div class="layout vertical self-center addRemoveButtonList">
        <paper-icon-button id="addSelected" icon="arrow-forward" on-tap="_addSelected"></paper-icon-button>
        <paper-icon-button id="removeSelected" icon="arrow-back" on-tap="_removeSelected"></paper-icon-button>
      </div>
      <div class="layout vertical flex selectedContainer">
        <span class="selectionTitle">
          {{rightSideTitle}}
        </span>
        <div class="selectedList">
          <paper-menu
            id="selectedMenu"
            multi>
            <template is="dom-repeat" items="{{selectedList}}" as="selectedItem">
              <paper-item
                value="[[selectedItem.title]]">
                [[selectedItem.title]]
              </paper-item>
            </template>
          </paper-menu>
        </div>
      </div>
    </div>
  </template>
  <script>
Polymer({
  is: 'now-standalone-picker'
});
  </script>
</dom-module>

So pretty straight forward on the CSS we added. But wait a second… what are those ‘var(–default-primary-color)’ statements all about? With Polymer we get the ability to define CSS variables. I can’t find where the ‘default’ variable names are defined by Polymer, it may have just come with using the custom styles. But the variables allow us to define colors (i.e. default-primary-color) assigned to a variable name and then use them anywhere. You can also define mixins but I do not have an example of that here. But it’s very similar to using the variables. So with the styles added we should now see something like this:

now-standalone-picker-styled

Notice that right side box is higher than the left because we’ve left an area to place a title over that right side box. Since there isn’t a title there, the right side has moved up to fill up the space. Once a title is put in there, the boxes will line up perfectly.

We are now ready to define the properties of our component. The properties are what those data binding variables defined in our html will use to display data. We’ll also want to define a keyup listener to our search field along with the method to handle the keyup event. So we should now end up with something like this:

<link rel="import" href="../polymer/polymer.html">

<link rel="import" href="../iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="../iron-icon/iron-icon.html">
<link rel="import" href="../iron-input/iron-input.html">

<link rel="import" href="../paper-icon-button/paper-icon-button.html">
<link rel="import" href="../paper-item/paper-item.html">
<link rel="import" href="../paper-menu/paper-menu.html">

<dom-module id="now-standalone-picker">
  <template>
    <style is="custom-style" include="iron-flex iron-flex-alignment">
      :host {
        display: block;
        box-sizing: border-box;
      }
      .selectionList {
        border: 1px solid #afafaf;
        margin-right: 5px;
        height: 150px;
        overflow: auto;
        background-color: white;
      }
      .selectionList {
        margin-top: 10px;
      }
      .selectionList paper-menu paper-item, .selectedList paper-menu paper-item {
        font-size: 12px;
        min-height: 20px;
      }
      .selectionList paper-menu paper-item:hover {
        cursor: pointer;
        background-color: #efefef;
      }
      .selectedList {
        border: 1px solid #afafaf;
        height: 150px;
        overflow: auto;
        margin-top: 16px;
        background-color: white;
      }
      .selectionContainer input {
        background-color: white;
        border: 1px solid var(--default-primary-color);
        line-height: 20px;
        padding: 2px 2px 2px 10px;
        width: 100%;
        font-size: 12px;
      }
      .selectionContainer input:focus {
        outline: none;
        border-color: var(--default-primary-color);
        box-shadow: 0 0 10px var(--default-primary-color);
      }
      .selectionContainer input::-webkit-input-placeholder {
        font-size: 12px;
      }
      .selectionContainer input::moz-placeholder {
        font-size: 12px;
      }
      .selectionContainer input:moz-placeholder {
        font-size: 12px;
      }
      .selectionTitle {
        font-size: 12px;
        font-weight: bold;
        line-height: 20px;
      }
      .selectionContainer input:-ms-input-placeholder {
        font-size: 12px;
      }
      .searchFieldContainer span {
        position: absolute;
        display: block;
        top: 23px;
        left: 233px;
        width: 16px;
        height: 16px;
        cursor: pointer;
      }
      .searchIconContainer {
        height: 26px;
        background-color: var(--default-primary-color);
        position: relative;
      }
      .searchIconContainer:hover {
        cursor: pointer;
      }
      .searchIconContainer iron-icon {
        height: inherit;
        background-color: var(--default-primary-color);
        border: 1px solid var(--divider-color);
        border: 0;
        color: white;
      }
    </style>
    <div class="layout horizontal">
      <div class="layout vertical flex selectionContainer">
        <div class="searchFieldContainer layout horizontal">
          <input
            id="selectionSearch"
            is="iron-input"
            bind-value="{{searchValue}}"
            name="searchValue"
            placeholder="Search Replicas">
          </input>
          <span on-tap="_clearSearch" hidden$="{{!searchValue}}">X</span>
          <div id="searchIconContainer" class="searchIconContainer" title="Perform the search">
            <iron-icon icon="search"></iron-icon>
            <paper-ripple></paper-ripple>
          </div>
        </div>
        <div class="selectionList">
          <paper-menu
            id="selectionMenu"
            multi>
            <template is="dom-repeat" items="{{selectionList}}" as="item">
              <paper-item
                value="[[item.title]]">
                  [[item.title]]
              </paper-item>
            </template>
          </paper-menu>
        </div>
      </div>
      <div class="layout vertical self-center addRemoveButtonList">
        <paper-icon-button id="addSelected" icon="arrow-forward" on-tap="_addSelected"></paper-icon-button>
        <paper-icon-button id="removeSelected" icon="arrow-back" on-tap="_removeSelected"></paper-icon-button>
      </div>
      <div class="layout vertical flex selectedContainer">
        <span class="selectionTitle">
          {{rightSideTitle}}
        </span>
        <div class="selectedList">
          <paper-menu
            id="selectedMenu"
            multi>
            <template is="dom-repeat" items="{{selectedList}}" as="selectedItem">
              <paper-item
                value="[[selectedItem.title]]">
                [[selectedItem.title]]
              </paper-item>
            </template>
          </paper-menu>
        </div>
      </div>
    </div>
  </template>
  <script>
Polymer({
  is: 'now-standalone-picker',
  properties: {
    selectionList: Array,
    selectedList: {
      type: Array,
      notify: true,
      value: []
    },
    searchValue: String,
    rightSideTitle: String,
    _origSelectionList: Array
  },
  listeners: {
    'selectionSearch.keyup': '_onSearchKeyup'
  },
  _onSearchKeyup: function(evt) {
    if (!this._origSelectionList) {
      this._origSelectionList = this.selectionList.slice();
    }
    if (this.searchValue.length > 2) {
      var searchVal = this.searchValue.toLowerCase();
      if (this.selectionList.length === 0) {
        this.selectionList = this._origSelectionList;
      }
      this.selectionList = this.selectionList.filter(function(item) {
        var title = item.title.toLowerCase();
        return title.indexOf(searchVal) > -1;
      });
    }else if (!this.searchValue) {
      this._clearSearch();
    }
  },
  _clearSearch: function(evt) {
    this.searchValue = null;
    this.selectionList = this._origSelectionList;
    delete this._origSelectionList;
  }
});
  </script>
</dom-module>

When defining properties there are a couple of different ways to do so. One is a propertyName: type (i.e. String, Object, Array, etc) the other is as an object (see selectedList above). Once these properties are defined they are now available to be used both inside this component and outside. Notice that the ‘selectedList’ property has ‘notify’ set to ‘true’. This will allow this property to be propagated up and down the DOM tree and all concerned elements will be notified of it’s change.

Also notice our ‘listeners’ property. Inside that object is a property name whose definition matches the pattern <ElementId>.<eventName> then the method to call when that event fires (‘_onSearchKeyup’).

We can now define our functionality of the right/left arrow buttons and what happens as this thing is used. Our usage pattern will be:

  1. Select an item(s) on the left/right
  2. Click the ‘right’/’left’ arrow icon
  3. This will move the items selected on the left, to the right side or vice-versa.

So we need a function to handle adding to the selection, removing from the selection and a couple of maintenance type methods to find the index of an item, deselecting items and selecting items programmatically.

<link rel="import" href="../polymer/polymer.html">

<link rel="import" href="../iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="../iron-icon/iron-icon.html">
<link rel="import" href="../iron-input/iron-input.html">

<link rel="import" href="../paper-icon-button/paper-icon-button.html">
<link rel="import" href="../paper-item/paper-item.html">
<link rel="import" href="../paper-menu/paper-menu.html">

<dom-module id="now-standalone-picker">
  <template>
    <style is="custom-style" include="iron-flex iron-flex-alignment">
      :host {
        display: block;
        box-sizing: border-box;
      }
      .selectionList {
        border: 1px solid #afafaf;
        margin-right: 5px;
        height: 150px;
        overflow: auto;
        background-color: white;
      }
      .selectionList {
        margin-top: 10px;
      }
      .selectionList paper-menu paper-item, .selectedList paper-menu paper-item {
        font-size: 12px;
        min-height: 20px;
      }
      .selectionList paper-menu paper-item:hover {
        cursor: pointer;
        background-color: #efefef;
      }
      .selectedList {
        border: 1px solid #afafaf;
        height: 150px;
        overflow: auto;
        margin-top: 16px;
        background-color: white;
      }
      .selectionContainer input {
        background-color: white;
        border: 1px solid var(--default-primary-color);
        line-height: 20px;
        padding: 2px 2px 2px 10px;
        width: 100%;
        font-size: 12px;
      }
      .selectionContainer input:focus {
        outline: none;
        border-color: var(--default-primary-color);
        box-shadow: 0 0 10px var(--default-primary-color);
      }
      .selectionContainer input::-webkit-input-placeholder {
        font-size: 12px;
      }
      .selectionContainer input::moz-placeholder {
        font-size: 12px;
      }
      .selectionContainer input:moz-placeholder {
        font-size: 12px;
      }
      .selectionTitle {
        font-size: 12px;
        font-weight: bold;
        line-height: 20px;
      }
      .selectionContainer input:-ms-input-placeholder {
        font-size: 12px;
      }
      .searchFieldContainer span {
        position: absolute;
        display: block;
        top: 23px;
        left: 233px;
        width: 16px;
        height: 16px;
        cursor: pointer;
      }
      .searchIconContainer {
        height: 26px;
        background-color: var(--default-primary-color);
        position: relative;
      }
      .searchIconContainer:hover {
        cursor: pointer;
      }
      .searchIconContainer iron-icon {
        height: inherit;
        background-color: var(--default-primary-color);
        border: 1px solid var(--divider-color);
        border: 0;
        color: white;
      }
    </style>
    <div class="layout horizontal">
      <div class="layout vertical flex selectionContainer">
        <div class="searchFieldContainer layout horizontal">
          <input
            id="selectionSearch"
            is="iron-input"
            bind-value="{{searchValue}}"
            name="searchValue"
            placeholder="Search Replicas">
          </input>
          <span on-tap="_clearSearch" hidden$="{{!searchValue}}">X</span>
          <div id="searchIconContainer" class="searchIconContainer" title="Perform the search">
            <iron-icon icon="search"></iron-icon>
            <paper-ripple></paper-ripple>
          </div>
        </div>
        <div class="selectionList">
          <paper-menu
            id="selectionMenu"
            multi>
            <template is="dom-repeat" items="{{selectionList}}" as="item">
              <paper-item
                value="[[item.title]]">
                  [[item.title]]
              </paper-item>
            </template>
          </paper-menu>
        </div>
      </div>
      <div class="layout vertical self-center addRemoveButtonList">
        <paper-icon-button id="addSelected" icon="arrow-forward" on-tap="_addSelected"></paper-icon-button>
        <paper-icon-button id="removeSelected" icon="arrow-back" on-tap="_removeSelected"></paper-icon-button>
      </div>
      <div class="layout vertical flex selectedContainer">
        <span class="selectionTitle">
          {{rightSideTitle}}
        </span>
        <div class="selectedList">
          <paper-menu
            id="selectedMenu"
            multi>
            <template is="dom-repeat" items="{{selectedList}}" as="selectedItem">
              <paper-item
                value="[[selectedItem.title]]">
                [[selectedItem.title]]
              </paper-item>
            </template>
          </paper-menu>
        </div>
      </div>
    </div>
  </template>
  <script>
Polymer({
  is: 'now-standalone-picker',
  properties: {
    selectionList: Array,
    selectedList: {
      type: Array,
      notify: true,
      value: []
    },
    searchValue: String,
    rightSideTitle: String,
    _origSelectionList: Array
  },
  listeners: {
    'selectionSearch.keyup': '_onSearchKeyup'
  },
  _onSearchKeyup: function(evt) {
    if (!this._origSelectionList) {
      this._origSelectionList = this.selectionList.slice();
    }
    if (this.searchValue.length > 2) {
      var searchVal = this.searchValue.toLowerCase();
      if (this.selectionList.length === 0) {
        this.selectionList = this._origSelectionList;
      }
      this.selectionList = this.selectionList.filter(function(item) {
        var title = item.title.toLowerCase();
        return title.indexOf(searchVal) > -1;
      });
    }else if (!this.searchValue) {
      this._clearSearch();
    }
  },
  _clearSearch: function(evt) {
    this.searchValue = null;
    this.selectionList = this._origSelectionList;
    delete this._origSelectionList;
  },
  _addSelected: function(evt, detail) {
    var selectedItems = [];
    selectedItems = this.$.selectionMenu.selectedValues || [];
    var selectionListItems = this.selectionList;
    for (var i = 0; i < selectedItems.length; i++) {
      var pushVal = selectionListItems[selectedItems[i]];
      if (pushVal && this.selectedList.indexOf(pushVal) === -1) {
        this.push('selectedList', pushVal);
      }
    }
  },
  _removeSelected: function(evt, detail) {
    var selectedRemoveMenuValues = [];
    selectedRemoveMenuValues = this.$.selectedMenu.selectedValues || [];
    var selectedRemoveItems = [];
    for (var i = 0; i < selectedRemoveMenuValues.length; i++) {
      var selectedItem = selectedRemoveMenuValues[i];
      if (selectedItem || selectedItem === 0) {
        selectedRemoveItems.push(this.selectedList[selectedItem]);
      }
    }
    for (var j = 0; j < selectedRemoveItems.length; j++) {
      var removeItemIdx = this._findIndex('selected', selectedRemoveItems[j]);
      var removeItem = selectedRemoveItems[j];
      if (removeItemIdx > -1) {
        this.splice('selectedList', removeItemIdx, 1);
      }
      this._deselectRemovedNames(selectedRemoveItems);
      this.$.selectedMenu.selectedValues = [];
    }
  },
  _findIndex: function(menuType, item) {
    var menuItems = null;
    if (menuType === 'selection') {
      menuItems = this.selectionList;
    }else if (menuType === 'selected') {
      menuItems = this.selectedList;
    }
    return menuItems.indexOf(item);
  },
  _deselectRemovedNames: function(removedItems) {
    for (var i = 0; i < removedItems.length; i++) {
      var removeNameIdx = this._findIndex('selection', removedItems[i]);
      this.$.selectionMenu.select(removeNameIdx);
    }
  },
  _selectInSelectionList: function(itemId) {
    var selection = this.selectionList.find(function(item) {
      return item['@id'] === itemId;
    });
    var thisIdx = this._findIndex('selection', selection);
    this.$.selectionMenu.select(thisIdx);
    this._addSelected();
  }
});
  </script>
</dom-module>

We’ve now added functions to find the index of an item in either list and a way to select/deselect things programmatically. The biggest thing to remember here is that ‘this’ is the component. We also

OK, that should finish up our component. Which is all well and good, but how do you use it? Well that’s the great thing about web components. In order to use this thing we’ll just include it in the html of an application somewhere. Below is the html of the demo page for this component.

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
    <title>now-standalone-picker Demo</title>
    <script src="../../webcomponentsjs/webcomponents-lite.min.js"></script>
    <link rel="import" href="../now-standalone-picker.html">
    <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
    <link rel="import" href="../../iron-demo-helpers/demo-pages-shared-styles.html">
  </head>
  <body unresolved>

    <p>An example of <code>&lt;now-standalone-picker&gt;</code>:</p>

    <demo-snippet class="centered-demo">
      <template id="pickerDemo">
        <now-standalone-picker
          right-side-title="Now Picker: Selected Items"
          selected-list="{{selected}}">
        </now-standalone-picker>
        <script>
          var picker = document.querySelector('#pickerDemo');
          picker.selected = [];
          picker.selections = [
            {"id": "123", "title": "Selection Item 1"},
            {"id": "121", "title": "Selection Item 2"},
            {"id": "122", "title": "Selection Item 3"},
            {"id": "124", "title": "Selection Item 4"},
            {"id": "125", "title": "Selection Item 5"},
            {"id": "126", "title": "Selection Item 6"},
            {"id": "127", "title": "Selection Item 7"},
            {"id": "128", "title": "Selection Item 8"},
            {"id": "129", "title": "Selection Item 9"},
            {"id": "120", "title": "Selection Item 10"}
          ];
          var saPicker = document.querySelector('now-standalone-picker');
          saPicker.set('selectionList', picker.selections);
          saPicker.set('selectedList', picker.selected);
        </script>
      </template>
    </demo-snippet>
  </body>
</html>

Notice our ‘now-standalone-picker’ html tag? That’s our component. Also notice that we’ve defined some attributes that closely match our property names for ‘rightSideTitle’ and ‘selectedList’. In our property declarations we defined these properties using camel-case. When we define these properties via an html attribute, we have to use a dash at each capitalized letter. So ‘rightSideTitle’ ends up being ‘right-side-title’.

We now end up with something like below. Notice we’ve now got a selection list. The component so far is working as we expect.

now-standalone-picker-selections

Now we make some selections and click the ‘right’ arrow and it moves them to the right side.

now-standalone-picker-selections-selected

We can also search the selection items, make choices and select them.

now-standalone-picker-search

As we change the selected values around, we can see our changes via data binding by going out to a JavaScript console and typing picker.selected and we should see the items we’ve selected.

The biggest advantage to this is now we can re-use this in any application we want to. We can include it in a dialog to be fired when something else is clicked or just use it as is. Since our properties are public we can populate them via an AJAX request or however we want. There is arguably more functionality to be added here: for example fire an event when the selectedList changes. We could also add the ability to handle single or multiple selections. Currently we only handle multiple selections as I really don’t see a use for this if only a single selection is needed, just use a combo box of some sort.

Polymer web components allow you to build the pieces of an application and then put those pieces together to form the whole app. Each component is aware of itself and any components contained within itself and nothing else. It provides a great way to separate out the logic of our application down to the smallest possible piece. Which totally goes toward the goal of re-usability.

If you would like to use or contribute to this component please visit the repository at Github. To see the documentation and demo visit my github pages site.



---------------------
http://keithstric.me/2016/06/14/re-usability-is-the-goal/
Jun 14, 2016
9 hits



Recent Blog Posts
5
Goodbye Evernote
Tue, Jan 17th 2017 5:22p   Keith Strickland
I’ve been using Evernote for a few years now and have enjoyed it’s feature set and the ability to plan and document a complex project (namely home/shop projects) with shopping lists, ideas, etc. But recently every time I attempt to use Evernote to create a quick note or maybe just jot something down, I’m presented with a request to upgrade to a pay plan, or to update or just general advertising. I can no longer just open it and create a note. Because of this, I have now backed
8
Merry Christmas!
Sun, Dec 25th 2016 10:57p   Keith Strickland
Merry Christmas!! I hope everyone is having a great holiday. I thought now might be a good time to look back over the year and review some of the technology I’ve dealt with. Surface Pro 4: Last year I got a Surface Pro 4 tablet. I started the process of switching to it instead of my aging MacBook Pro. While I REALLY like the hardware and how everything works there were just a few issues which got on my nerves so bad I couldn’t ever completely make the switch. As far as performance, I
10
Visual Studio Code Editor
Fri, Nov 11th 2016 3:42p   Keith Strickland
I’ve been using the Visual Studio Code editor for the last couple of weeks and thought I would share my experience. I’ve mainly used this in a plain ‘ole polymer application which consists of html files. Using the editor this way has shown some of it’s shortcomings. Don’t get me wrong, I think it’s a fine editor and has a lot of features I really like. However with CSS, HTML and JavaScript all residing in the same file, a lot of the typeahead features just don
7
Work with Rich Text from DDS
Fri, Oct 21st 2016 5:33p   Keith Strickland
So you’ve created your shiny new web application using DDS and everything is really cool, except for the display of rich text. You’ve figured out that there is a multipart MIME object in the JSON delivered by DDS, and it has the HTML in that, but it still looks crappy. It has tags littered throughout and just doesn’t look good. Well, I think I’ve got the solution for you. If you look at that multipart MIME Object and find the ‘text/html’ entry you’ll not
6
A Polymer Avatar component
Wed, Oct 12th 2016 5:50p   Keith Strickland
At MWLUG a lot of people seemed surprised that I would create a component just for an avatar. My response to that is why wouldn’t you build an avatar component? So, in this post I’m going to show you how to build one for your applications. Let’s start out with the use case. We want to show an avatar for a person in various locations throughout our application. This avatar should do a few different things: Should show a picture of a person if one is attached to their person do
10
MWLUG Recap
Sat, Aug 20th 2016 11:27p   Keith Strickland
I had a great time at MWLUG this year. It was great to see so many familiar faces and friends, most of which I only see at the user groups. But, I just got home and thought I would share some of my thoughts and observations about the event and my take away about the state of our beloved Notes. As usual Richard Moy put together a great conference, so many thanks to him for making that event possible. The tone of the event was that most everyone is starting to realize that the Notes client is qui
9
Red Pill is the MWLUG Sponsor of the Week
Fri, Aug 12th 2016 12:21p   Keith Strickland
Red Pill Now is the Sponsor of the Week for MWLUG. Check out our video in the top right corner of the site. .huge-it-share-buttons { border:0px solid #0FB5D6; border-radius:5px; text-align:right; } #huge-it-share-buttons-top {margin-bottom:0px;} #huge-it-share-buttons-bottom {margin-top:0px;} .huge-it-share-buttons h3 { font-size:25px ; font-f
4
Domino svg support
Fri, Aug 12th 2016 12:09p   Keith Strickland
I’ve been messing with the Polymer vaadin-grid. If you enable hidable columns, a little graphic svg icon shows in the top right hand corner of the grid that produces a drop down menu of all the columns in the grid. You click one and it’ll hide that column. This was working great when running from my local gulp server. However when I put it on Domino, the little icon wasn’t showing, but the button was there (you couldn’t see it tho) and the menu worked when clicked. I di
5
Setting up a Polymer development environment
Fri, Jul 29th 2016 11:35a   Keith Strickland
When working with Polymer you’ll need a development environment. Google has created some great tools for doing this, mainly the Polymer-CLI. This is a command line interface for creating elements, applications, building applications (though I prefer a Gulp build system), a web server and some other misc. tools. This should be your starting point for setting up your development environment. To setup the polymer-cli you’ll need a few dependencies: Git Node.js (4.x) Bower Once those a
8
sortablejs – Drag-n-Drop without jQuery UI
Thu, Jul 21st 2016 4:59p   Keith Strickland
I had a need to enable drag-n-drop for a particular part of our portal. In the past I’ve always used jQuery-UI as it’s quite easy to enable drag-n-drop. Doing some research I came across a StackOverflow question about enabling drag-n-drop with Polymer. One of the answers mentioned sortablejs. This is a very minimalist library to enable drag-n-drop. Best part about this library is that it has a port for Polymer. BONUS! But there are several ports available: Angular, Knockout, Meteor,




Created and Maintained by Yancy Lent - About - Planet Lotus Blog - Advertising - Mobile Edition