203 Lotus blogs updated hourly. Who will post next? Home | Blogs | Search | About 
 
Latest 7 Posts
Thoughts on TypeScript
Wed, Sep 6th 2017 4
Replacing Lotus is…. complex
Fri, Sep 1st 2017 7
Web Component Thoughts….
Fri, Aug 25th 2017 3
Just…. NO,NO,NO
Fri, Aug 11th 2017 5
Goodbye Evernote
Tue, Jan 17th 2017 4
Merry Christmas!
Sun, Dec 25th 2016 4
Visual Studio Code Editor
Fri, Nov 11th 2016 4
Top 10
Work with Rich Text from DDS
Fri, Oct 21st 2016 10
Re-usability is the Goal!
Tue, Jun 14th 2016 7
Polymer app-layout Elements
Fri, Jul 1st 2016 7
Domino svg support
Fri, Aug 12th 2016 7
Replacing Lotus is…. complex
Fri, Sep 1st 2017 7
On SocialBizUg: Modern Domino: Bootstrap 3 Inheritable Layout
Fri, May 30th 2014 5
My first impressions of using Titanium Appcelerator
Sun, Dec 8th 2013 5
New Polycast is out!
Wed, Jun 15th 2016 5
I’m speaking at MWLUG 2016
Tue, Jun 28th 2016 5
Red Pill is the MWLUG Sponsor of the Week
Fri, Aug 12th 2016 5


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
8 hits



Recent Blog Posts
4
Thoughts on TypeScript
Wed, Sep 6th 2017 9:25p   Keith Strickland
Over the past few months I’ve started working pretty extensively with TypeScript. For those of you who don’t know what TypeScript is: TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. It provides strong types to JavaScript. It allows for the creation of classes and enforces those classes in your code. If you define a Redpill.Widget class, you can then use that class in your code and the editor enforces the rules you define within that class. For example,
7
Replacing Lotus is…. complex
Fri, Sep 1st 2017 9:44p   Keith Strickland
If you followed Peter’s series on replacing Lotus he outlined some of the pitfalls, processes and decision points to undertake for success. I wanted to point out the technical side to a lot of those decisions. The short answer is that you need a tool to surface your domino data en-masse until such a time when decisions are made on each application. I have been working on that solution for quite some time now and I have to say, it’s complex. First you need to make a fundamental decisi
3
Web Component Thoughts….
Fri, Aug 25th 2017 8:55p   Keith Strickland
The past 1.5 years I’ve been working exclusively with Web Components and specifically Polymer. The more I use this technology the more convinced I am that this is the technology I should be using. Now, I’m not saying that Web Components and Polymer are hammers and every problem/project is a nail. However it’s quite refreshing that Polymer’s goal is to make itself irrelevant. What does that mean, Polymer is there temporarily until the browsers decide upon common standards
5
Just…. NO,NO,NO
Fri, Aug 11th 2017 7:45p   Keith Strickland
This week I attended MWLUG in Alexandria, VA. This was an awesome event, so many good speakers, good content and excellent camaraderie. I can’t say it enough, but Richard Moy and his team put on such a good event. So, I spoke to a couple of developers who are writing client JavaScript in Domino Designer. While Domino Designer is capable of allowing you to write JavaScript, all I can say is STOP! Stop torturing yourself, Stop making it harder on yourself to write good JavaScript. There are
4
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
4
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




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