konnekt

0.8.9 • Public • Published

Konnekt

Multi layer component based binding framework

NPM version Gitter

Table of Contents

What is it?

This is a framework that allows you to comprise an app based on modular components in an atom based approach using a simple(HTML, CSS, JS) component folder architecture. Each file is comprised in the given way:

  • HTML (the html for just that component)
  • CSS (the css just for that component)
  • JS (the data, methods, and events associated with that component)

Each component is comprised in a way to allow for easy and friendly development by using an MVVM modular binding approach so components can be plug and play and independent of everything else. This allows easy reusability through out the entire app.

Why you should use it?

  • Simple Provides many built in functionality that allows you to create an app quickly and efficiently
  • Modular By splitting up the app into components You can reuse them across the app, which saves you development time
  • Designer Friendly This framework has been created with designers in mind to allow easy and friendly development
  • Two Way Binding One of the few frameworks to have true two way bindings with the dom

Installation

This libray can be installed directly using NPM or Bower

  • First step:

    • npm i konnekt --save

All cli commands come packaged along with konnekt.

Usage

These chapters are meant to be useful in learning more about how to work with the different powerful functionalities that konnekt provides

Getting started

Creating a component is as simple as using the built in gulp create command

  • First step: (using cmd prompt)

    • gulp create
    • Follow in console prompts eg. (name, description, contributor)

Using the konnekt library in your project is as simple as initiating it

<script src="/node_modules/konnekt/min/konnekt.min.js" type="text/javascript"></script>
<script>
  var konnektjs = Konnekt();
</script>

There are two ways to get started using your components

  • First choice (using built in hashrouting)

This automatically replaces \<body> content with your desired component

var konnektjs = Konnekt({
  base:'Name-Of-The-Component-You-Want-To-Load-Into-Body',
  prefix:'prefix-for-url-if-using-local-routing'
});
konnektjs.hashRouting(true);
  • Second choice (using konnekt built in parser)

The element will be replaced with your desired component, this can be used multiple times

<body>
  <mycomponent></mycomponent>
  <script>
    konnektjs(document.querySelector(mycomponent));
  </script>
</body>

Now Go Make Your App!

File structure

The file structure of a konnekt app can be comprised of a single html entry point, your pre-installed libraries and a components folder to house all your components. Each component houses three main files, a HTML,CSS, and JS file, the build folder houses compiled files of your component to be loaded using different environments in a production scenario.

├── components/
│   ├── component/
│   │   ├── build/
│   │   │   ├── qa
│   │   │   │   ├── component.js
│   │   │   │   ├── component.min.js
│   │   │   ├── stage
│   │   │   ├── prod
│   │   ├── component.html
│   │   ├── component.css
│   │   ├── component.js
├── node_modules/
│   │   ├── konnekt
├── index.html

Components

Components are a single html tag that is then replaced by its corresponding template, css, and code associated with it.

By simply refrencing in your html:

  <foo></foo>

this will build out to your componnt such as:

  <div>
    <h1>Foo</h1>
    <div>
      <p>some text about foo</p>
    </div>
  </div>

Components can also be comprised of other components which gets into the atom based approach of starting off with small component parts and building up to larger ones. Such as:

  <bar></bar>

equals

  <div>
    <foo></foo>
    <foo></foo>
  </div>

Each component builds out to its corresponding component set.

Basics

All files of a component can be connected using binds, the basic bind syntax uses {{}} Bracket notation, bindings house extra functionality called filters represented after the | symbol in a bind. filters allow changing of a value/syntax prior to being placed on the page.

HTML
<div class="mycomponent" onclick="{{clickaction}}" attr="{{attrbinding}}">
  <div class="innerdiv">{{textbinding | toUpperCase}}</div>
</div>
CSS
  .{{localidbinding}} .mycomponent {
    background:{{backgroundstylebinding}}px
  }
  .innerdiv {
    color:{{colorstylebinding}}
  }
JS
 function mycomponent(element)
 {
    /* closure */
    var self = this;
    
    /* Your properties */
    this.attrbinding = "test";
    this.textbinding = "mytext";
    this.backgroundstylebinding = "#000";
    this.colorstylebinding = "#FFF";
    
    /* your filter for changing text before it appends to your element */
    this.filters.toUpperCase = function(v)
    {
      return v.toUpperCase();
    }
    
    /* your event bind */
    this.clickaction = function(e)
    {
      self.someaction();
    }
 }
 
 /* any extra methods on your prototype */
 mycomponent.prototype.someaction = function()
 {
    this.colorstylebinding = "#F00";
 }

Gulp tools

There are 3 main tools for working with konnekt components the built in cli commands.

Each command has built in prompts in cmd that are easy to follow, You can also use shorthand to skip the prompts

  1. Create Tool gulp create (used fo rcreating components)
  2. Build Tool gulp build (used for building components to different environments)
  3. Local Server gulp server (used for easy local environment development in dev environment, see: Environments)

Shorthand example: gulp server --port 8080 or gulp server -p 8080

Bindings

Bindings in components can almost be placed anywhere, Any text in your css file can be binded, and any attribute and inner text in the html can be binded.

There are some pre built in methods that add great functionality to binds to go over.

Standard Bind

A standard bind is comprised of a {{}} notation with a name refrencing a data point in your js file, The js file we will call the viewmodel of your component. The following example is a standard binding:

  • Text Binding
  <div>{{mytext}}</div>
  • Attr Binding
  <div class="{{myclassname}}"
  • CSS Binding
  .class {
    background:#{{mybackgroundcolor}}
  }

These bindings can also be in line with another and sets of text

  <div>Hello, my name is {{firstname}} {{lastname}}</div>

Binds attach to thier corresponding data set in the js code, such as the following example:

  • HTML
  <div>Hello, my name is {{firstname}} {{lastname}}</div>
  • JS
  function user()
  {
    this.firstname = "Foo";
    this.lastname = "Bar";
  }

Bind keys can also be bound to properties further down in a chain by using the . syntax, example:

  • HTML
  <div>The {{animals.0.name}} likes to {{animals.0.sound}}</div>
  <div>he {{animals.1.name}} likes to {{animals.1.sound}}</div>
  • JS
  function species()
  {
    this.animals = [
      {name:'cat',sound:'meow'},
      {name:'dog',sound'bark'}
    ];
  }
A special note on binds, if a the bind does not have any other text or binds asociated it acts as a **two-way** bind, if the attr or text changes for that bind it will update the viewmodel data to reflect that change.

Example:

HTML

  <div class="foo">{{foobind}}</div>

JS

  function foo(node)
  {
    var self = this;
    this.foobind = "bar";
    
    node.querySelector('.foo').onclick = function()
    {
      this.innerHTML = "something else";
      console.log(self.foobind);
    }
  }

this changes the html of the foo div to something else and in doing so the bind property of foobind will console log the same, this will not work when the bind is polluted, an example of bind pollution is:

  <div class="foo">some extra text {{foobind}}</div>

in this case You will be unable to change the html via your click event because your bind is polluted by other text.

Another scenario is passing binds to another component, these act as pointers to the original data.

Example:

HTML

  <div>
    <bar onbarclick="{{onFooClick}}"></bar>
  </div>

JS

  function foo()
  {
    var self = this;
    
    this.foobar = 'test';
    
    this.onFooClick = function(e)
    {
      console.log(self.foobar);
    }
  }

if the original onFooClick is changed this change will be reflected inside bar as well, same goes for changing it in bar as well.

Built in Binds

There are some built in binds that can be used throughout any component automatically, such examples are:

  • innerHTML A bind that inserts nodes passed

FOO Component HTML

  <div>
    <div>{{innerHTML}}</div>
  </div>

HTML before

  <div>
    <foo>
      <div>bar</div>
    </foo>
  </div>

HTML after

  <div>
    <div class="Wrapper Wrapper__Foo">
      <div>
        <div><div>bar</div></div>
      </div>
    </div>
  </div>
  • local a unique identifier for that placed component

Class styles are shared among all components of type foo, so if a style changed for one component, then all will change, local allows you to set a css rule as an individual instance to that component and not be shared to others

FOO CSS

  .{{local}} .fooclass {
    background:#{{foocolor}}
  }

HTML before

  <div>
    <foo></foo>
  </div>

HTML after

  <div>
    <div class="Wrapper Wrapper__foo foo_17364393747">
      <div class="fooclass">
        <div<bar</div>
      </div>
    </div>
  </div>

Filters

Filters help change the way data looks before it is put into the html or data, filters are assigned to a bind after the | char by name.

All binds can contain multiple filters by simply seperating them out using: , character like in a list

Example: {{bindname | foofilter,barfilter,(foovmfilter),[+storefilter]}}

Pre built in filters (Coming Soon)

There are currently four types of filters:

  1. Standard dom filter syntax:

{{bindname | foofilter}}

These filters are tied to Your vm on the this.filters object, these are methods that take in the value that is attempting to be set to the html and they must return the new value to be set after your done with it.

function foo()
{
  this.filters = function foofilter(v)
  {
    return v.toLowerCase();
  }
}

Note filters that are attached to binds on a component will also be passed to the child component for you.

  1. Data based filter in the case of non polluted binds: p.s. notice the '()' around the filter name

{{bindname | (foovmfilter)}}

These filters are placed in the same this.filters object but will only apply to changes coming from the html to the data direction of a non polluted binding, see: Two-way binds

function foo()
{
  this.filters = function foovmfilter(v)
  {
    return v.toUppercase();
  }
}
  1. Storage filters

Storage filters are designed to store points of data so it is remembered later, these use three types of syntax for 3 types of storage:

  • Model {{bindname | [~store.foo]}} stores in the model for use among other components when pages changes

  • Session {{bindname | [-store.foo]}} stores in session so in case the page gets refreshed or user navigates way then back it is remembered

  • Local {{bindname | [+store.foo]}} stores in local so that even when the browser is closed and reopened the value is remembered

Each of these three filters use the named string as their storage point, so keep this in mind when naming them.

note

inside filter methods this refers the vm data :)

Built in filters
  • toUpperCase Capitalize all letters
  • toLowerCase lowercase all letters, no caps
  • toFirstUpperCase Capitalize first letter only
  • toEveryUpperCase Capitalize first letter of every word
  • isHidden returns hide or block based on true for display styles
  • isNotHidden Opposite logic for isHidden same functionality

Events

Events can also be used as binds inside a component, to use an event bind You must bind the method to the appropriate attribute.

A side note, the attribute in this case is not actually used as this is bad practice, Your binded method will be moved to the appropriate property on the element.

Example of an event bind:

HTML

  <div>
    <div onclick="{{fooclick}}">{{status}}</div>
  </div>

JS

function foo()
{
  var self = this;
  this.status = "none";
  this.fooclick = function()
  {
    self.status = "clicked";
  }
}

note

your event this like all events will refer to the element, if You want to interact with your vm data You must create a closure, in this case it is the example of var self = this

Passing

Passing values from a parent component to another is very easy in just setting the attributes on the base component element, as an example we have the element foo as so:

HTML

  <div onclick="{{fooclick}}">{{status}}</div>

JS

  function foo()
  {
    this.status = "";
    this.fooclick = function(){};
  }

at this stage status and onclick are empty with nothing, to use these in a proper way we can pass to them from another component bar, current bind values can also be passed, but they do not act as binds and will not update the lower components if thiers is updated. HTML

  <div>
    <foo status="none" fooclick="{{barclick}}"></foo>
  </div>

JS

  function bar()
  {
    this.barclick = function()
    {
      alert("hello from foo");
    }
  }

Loops

Loop bindings allow iterating through data and creating a component for each iteration as in a list.

The format is: {{for key loop componentname}} for each item in the key property loop create the component, each data set is passed to the component.

The component:

HTML (before)

  <div>
    <ul>{{for animals loop listitem}}</ul>
  </div>

JS

  function foo()
  {
    this.animals = [
      {type:'cat',sound:'meow'},
      {type:'dog',sound:'ruff'}
    ];
  }

Listitem component:

HTML

  <li>The {{type}} likes to {{sound}}</li>

JS

  function listitem()
  {
    this.type = "";
    this.sound = "";
  }

End result:

HTML (after)

  <div>
    <ul>
      <div class="Wrapper Wrapper_listitem">
        <li>The cat likes to meow</li>
      </div>
      <div class="Wrapper Wrapper_listitem">
        <li>The dog likes to ruff</li>
      </div>
    </ul>
  </div>

One of the great things about looped items is that the data acts as a pointer between the parent array list and the child data points, by changing this.type the reflected change is shown in the list. The sme applies for array changes, by simplying array.sorting a list the order of Your elements will change.

Elements

You also have the ability to bind to element tag names as well, featuring the ability to hot swap out components based on a binding

Example

HTML

  <div>
    <{{foocomponent}}>
  </div>

JS

  function foo()
  {
    this.foocomponent = 'name of the component';
  }

this is a simple version that allows you to place a component by the name specified by foocomponent property, if foocomponent property is updated with a new value it will attempt to fetch that component and swap out the current component with the new one.

filters and inner elements as well as other binds can also be placed on these hot swappable components as well, beware though that these binds and inner elements will be applied to any component that is ever swapped in as well.

Example:

HTML

  <div>
    <{{foocomponent | isAllowed}} bar="{{fooname}}">
      <div>inner text></div>
    </{{foocomponent | isAllowed}}>
  </div>

JS

  function foo()
  {
    this.foocomponent = 'bar';
    this.fooname = 'foobar';
  }

this will pass the property and value of this.bar = 'foobar'; into any component that is swapped, as well as <div>inner text</div> will be the values placed to any bindings of {{innerHTML}}.

note Please note that the ending

Logic

There are also some built in logic operators with binds that automatically populate as filters to a bind, these include:

  • Boolean Operators

  • ==

  • >=

  • <=

  • >

  • <

  • Arithmatic Operators

    • +
    • -
    • *
    • /

These act in inline syntax to the key in the bind, Example:

  <div>{{foo+" new text"}}</div>
  <div>{{foo*200}}</div>
  <div>{{foo==true}}</div>
Inline value set

Besides logic operators there are also built in sets for binds as well, to set the default of a key inline You can specify the value with a single =

  <div>{{foo="bar"}}</div>
Single instance get/set

adding to the beginnig of a bind key either < or > specifies a single instanced get/set.

These get set or fetched once and are then erased from the html and never used again, these are used for setting data in either direction in a templating fashion

  • < means a single get and must include a = set value along with the key
  • > means a single set

Examples:

  <div>{{>foo}}</div>
  <div>{{<foo="bar"}}</div>

Viewmodel

Viewmodel is th heart of a components connection from html to logic, it specifies the values and actions connected to the html and styling of a component instance.

A standard Component Viewmodel:

function foo()
{
  /* Your bindable properties get added here */
  this.prop = "foo";
  
  this.data = [];
  
  /* Filters also get added here onto the filters object */
  this.filters.toUpperCase = function(v)
  {
    return v.toUpperCase();
  }
  
  /* Event binds are also added to this section */
  this.fooclick = function(e)
  {
    alert("clicked");
  }
}
 
/* You can add extra interactive methods via prototypes */
foo.prototypes.ajaxCall = function()
{
  var self = this;
  ajaxcall().then(function(data){
    this.set('data',data);
  });
}

NOTE

A key importance to remember when working with the vm data is if You plan to support older browsers such as IE10,IE11 You must add data using this.add or this.set new data is not automatically seen on these browsers due to lack of support for Proxies.

Many of the extra features for working with the viewmodel data can be found here KonnektDT

Built in Properties

There are a few built in properties that have extra uses when dealing with Your components and data

  • multiple(Boolean) The multiple property allows you to use a component recursively

  • onFinish(function) the onFinish is a property you can set a method to for when the component has finished building and being mapped

  • model(boolean) the model property tells whether to store this entire vm in the model data set for use on other pages, this stores by id on the vm

(WIP cant save functions, only data)

  • sessionStorage(boolean) This property tells to attempt to store the entire vm in session storage, though only data is stored and not any methods.

(WIP cant save functions, only data)

  • localStorage(boolean) Similiar to sessionStorage but persists even when the browser is closed and reopened.

  • parse(Function)(@Params json(string)) Allows You to parse a json string directly to the vm data model

  • listen(Function)(@Params localid|id|loopid(string), key(string), callback(function)) Allows listeneing to a key on a specific component based with an id

  • listen(Function)(@Params key(string), callback(Function)) Allows listeneing to a key, requires another component calling alert method with desired key

  • alert(Function)(@Params key(string), value(Any)) Allows firing all listeners attached to that key

  • filters(Object) Where all filter methods are attached including pre defined ones

Examples:

  • parse
  function foo()
  {
    var self = this;
    /* on click fetch your data */
    this.onclick = function()
    {
      self.getdata();
    }
  }
  
  foo.prototype.getdata = function()
  {
    var self = this;
    /* fetch your data somehow */
    ajaxrequest(function(jsondata){
      self.parse(jsondata);
    });
  }

Listeners

Listeners are great add ons that allow You to listen for alerts or changes of data in other components in the page.

  • listen @Params((id|local|loopid),key,function) or @Params(key,function)

This method allows listening for key alerts or for listening to specific components data changes

Example:

Foo

  <div>
    <bar id="foobar"></bar>
  </div>
  function foo()
  {
    /* key alerts */
    this.listen('key',function(e){
      /* fires when another component alerts with this key */
    });
    
    /* specific component data */
    this.listen('foobar','key',function(e){
      /* fires when this components data with the property 'key' changes */
    })
  }
  • unlisten @Params((id|local|loopid),key,function) or @Params(key,function)

This method removes listeners that were attached, note: just like events you must pass in the original method that was used with the listener for removal

  function foo()
  {
    function listenToKey(e)
    {
    
    }
  
    /* Example of key version */
    this.listen('key',listenToKey);
    this.unlisten('key',listenToKey);
    
    /* Example of component id version */
    this.listen('foobar','key',listenToKey);
    this.unlisten('foobar','key',listenToKey);
  }
  • alert @Params(key,value)

This method allows You to alert any listening components of that key and pass them a value

  function foo()
  {
    var self = this;
    /* an example using a click event */
    this.onclick = function()
    {
      /* Alert with a value */
      self.alert('key',500);
    }
  }

The passed objects into the listen methods that are passed are represented in two forms,

  • listen(key,function) only a value is passed

  • listen((id|local|loopid),key,function) The following object is pass:

    • key: The key that changed
    • value: the current value set
    • oldValue: the old value that was set
    • stopChange: whether a stopChange was applied on this value change or not
    • local: the local based object layer
    • kbref: the base object of the entire data set
    • initial: Whether this is an initial data fetch

note: when add a listen that connects to a component, that listen will be fired immediately containing the current value that is set, You can filter these out by reading the event.initial property that is passed when your function is fired

Built Ins

Built ins are extra functionality that can be used inside of components that are not necessariliy part of html or vm functionality

Device Detection

You can use some simple properties and css classes to help with device detection in your styling or code

Type of device being used:

  • Device
    • mobile
    • tablet
    • desktop

Orientation of the device:

  • Orientation
    • portrait
    • landscape

Type of browser being used:

  • Browser
    • ie
    • chrome
    • firefox
    • safari
    • unknown

if not a desktop, if the keyboard is open:

  • Keyboard
    • true/false

The actual window size of the current app, can differ when user resizes window say on a desktop:

  • ScreenSize
    • mobile-size
    • tablet-size
    • desktop-size

The actual window orientation of the current app, can differ when user resizes window say on a desktop:

  • OrientationSize
    • portrait-size
    • landscape-size

CSS:

  .desktop .foo {
    width:60px;
  }
  .mobile .foo {
    width:20px;
  }
  
  .keyboard .foo {
    width:100%;
  }

The JS based version can be retrieved off the base Konnekt method like via the device property:

JS:

  var device = Konnekt.device.type; //"mobile"
  var isKeyboard = Konnekt.device.keyboard; //true/false
  var screenSize = Konnekt.device.screenSize; //"mobile-size"

Routing

File Routing

Component routing is handled in two ways:

  • backend Nodejs based routing, allows dev environment
  • frontend based routing, does not allow dev environment

To turn on front end routing You must use the localRouting(true) method from Konnekt

Example:

JS

  var konnektjs = Konnekt()
  .localRouting(true);

note local routing is unable to provide the dev environment cause it requires concatinating files on the backend which is not possible in the front end, use qa env for debugging and building.

Backend routing uses the konnektrt module that can be installed via npm install konnektrt --save

then inside your nodejs router you can require it, it accepts, res,req,next

Example:

JS

  var konnektrt = require('konnektrt');
 
  module.exports = {
    Routes:[
  
      /* just call it wherever You want the method to be placed as a route, it accepts 3 params, res,req,next */
      konnektrt(),
 
      //other routes...
    ]};

Hash Routing

Konnekt also comes packed also with a hash router but by default this is turned off, a hash router means it routes components based on any #hash in the url bar

Example: http://yourwebsite.com/#componentname

to enable hash routing just simply call the hashRouting method on the main konnekt object

JS

  var konnektjs = Konnekt().hashRouting(true);

Hash routing requires a base to be specified in the config, this can either be in your local config, passed to the library constructor

Base component for base / url

Your config

  Konnekt.config({
    base:'foo'
  });

In constructor

  var konnektjs = Konnekt({
    base:'foo'
  })
  .hashRouting(true);

setting up custom routes:

setting up custom routes is easy as simply listening for a hash change on konnekt

Example:

JS

  var konnektjs = Konnekt({
    base:'foo'
  })
  .hashRouting(true)
  .addHashRouter(function(e){
    /* control hash or prevent default here */
    e.preventDefault();
  });

The event object for the hashrouter contains the following properties:

  • preventDefault(Function) this prevents the base swapper that repalces body tag content with the component refrenced by the hash
  • stopPropogation(Function) this prevents any future added routes from firing
  • hash(String) this is the hash name that was entered
  • url(String) this is the current full url that is being used in the url header

Environments

Konnekt alsow comes with built in environment levels, for proper production deployment. by default the prod environment is always loaded, this is what the end user will see

to access different environments You must pass queries in the url.

  • env
    • dev the base development environment, note reverts to no localRouting
    • qa the unit testing environment
    • stage the staged user testing environment
    • prod the end product

The debug option allows loading non minified versions of your components for easier debugging

  • debug
    • true
    • false

Example urls:

http://yoursite.com/?env=dev&debug=true

To speed up your development using the dev environment you must use your local environment, this is done through the gulp server command

Configs

You can set config data that is shared among all components inside your configs/config.js file, some examples of things You can set:

  • filters(Object) You can set default filters for every component
  • base(String) You can set the name of the base component that is loaded for default homepage
  • prefix(String) a prefix to place in front of all url requests
  • onFinish(Function)(@Params callback(function)) You can set a onFinish for all components

Examples

You can view an example implementation of an app using all the different types of functionality from these apps:

Adding Device detection throughout the app from the base component

Init

  var konnektjs = Konnekt({
    base:'base'
  })
  .hasRouting(true);

Base

  function base()
  {
    var self = this;
    
    /* add a resize listener to send out alerts for media device changes */
    window.addEventListener('resize',function(){
      self.alert('app_width',window.innerWidth);
      self.alert('app_height',window.innerHeight);
      self.alert('app_device',konnektjs.device.type);
      self.alert('app_orientation',konnektjs.device.orientation);
      self.alert('app_browser',konnektjs.device.browser);
      self.alert('app_keyboard',konnektjs.device.keyboard);
    });
  }

Another component

  function anothercomponent()
  {
    /* use initial value */
    this.showKeyboardInput = konnektjs.device.keyboard;
    
    /* listen for future updates */
    this.listen('app_keyboard',function(value){
      this.showKeyboardInput = value;
    });
  }

Development

If You would like to keep track of what is currently being developped on this framework, You can look at our Trello

Changelog

The changelog can be viewed here: Changelog

How to contribute

If You would like to contributem here are the steps

  1. Clone Repo: Konnekt Github Repo

  2. Install dependencies and necessary git repos: npm run installer

  3. To build the dependencies: gulp build require_lib option

  4. to test: gulp server -p 8080 -r test

  5. Make a request for your changes :)

License

You can view the license here: License

Package Sidebar

Install

npm i konnekt

Weekly Downloads

11

Version

0.8.9

License

MIT

Unpacked Size

391 kB

Total Files

12

Last publish

Collaborators

  • keleko34