All Projects → asika32764 → ivia

asika32764 / ivia

Licence: MIT License
A reactive & model-driven MVVM framework for jQuery with Vue-like interface.

Programming Languages

javascript
184084 projects - #8 most used programming language
HTML
75241 projects

Projects that are alternatives of or similar to ivia

jquery-imageuploader-js
A JQuery plugin that provides an interface for managing a set of files to upload. Files will have a thumbnail preview and can be removed before the upload step.
Stars: ✭ 18 (-73.91%)
Mutual labels:  jquery-plugin
Podcasts-UIKit
OUTDATED. A clone of Apple's Podcasts. UIKit version.
Stars: ✭ 145 (+110.14%)
Mutual labels:  mvvm
RxSwiftDemo
RxSwift Demo
Stars: ✭ 19 (-72.46%)
Mutual labels:  mvvm
jquery-alphaindex
jQuery plugin to create alphabetical indexes for your lists
Stars: ✭ 12 (-82.61%)
Mutual labels:  jquery-plugin
iOS-Clean-Architecture-Example
An iOS app designed using clean architecture and MVVM.
Stars: ✭ 50 (-27.54%)
Mutual labels:  mvvm
MVVM
MVVM - POP & OOP
Stars: ✭ 13 (-81.16%)
Mutual labels:  mvvm
jqlouds
☁️ An awesome yet simple plugin for jquery that let's you create clouds on the fly.
Stars: ✭ 54 (-21.74%)
Mutual labels:  jquery-plugin
Clother
Clother is an Android client-server app for swapping unused clothes.
Stars: ✭ 22 (-68.12%)
Mutual labels:  mvvm
Eyepetizer
An unofficial Eyepetizer(开眼视频) App built using Ijkplayer, RxJava2, Retrofit2, Dagger2, Room , DataBinding and Clean-MVVM Architecture.
Stars: ✭ 22 (-68.12%)
Mutual labels:  mvvm
Noted-Android
Noted app for android
Stars: ✭ 78 (+13.04%)
Mutual labels:  mvvm
jquery-regexp-classes
$(elem).removeClass(regExp) and $(elem).hasClass(regExp)
Stars: ✭ 18 (-73.91%)
Mutual labels:  jquery-plugin
intl-tel-input-rails
intl-tel-input for the Rails asset pipeline
Stars: ✭ 35 (-49.28%)
Mutual labels:  jquery-plugin
Askme
Social media app to ask and answer user questions and interact with users
Stars: ✭ 16 (-76.81%)
Mutual labels:  mvvm
lazeemenu
Multi-level sidebar menu - JQuery plugin
Stars: ✭ 40 (-42.03%)
Mutual labels:  jquery-plugin
ml-stack-nav
Customizable, responsive, accessible, easy-to-use multi-level stack navigation menu with slide effect.
Stars: ✭ 20 (-71.01%)
Mutual labels:  jquery-plugin
selectr
✅ The coolest jQuery select plugin you've never seen
Stars: ✭ 19 (-72.46%)
Mutual labels:  jquery-plugin
MVVM-Sample
Swift MVVM Sample project. Made with ReactiveCocoa, Swinject and Routers
Stars: ✭ 21 (-69.57%)
Mutual labels:  mvvm
HandyWinGet
GUI for installing apps through WinGet and Creating Yaml file
Stars: ✭ 305 (+342.03%)
Mutual labels:  mvvm
Mp3ID3Tagger
🎶🎵A macOS application to edit the ID3 tag of your mp3 files. Developed with RxSwift and RxCocoa. 🎸🎼
Stars: ✭ 17 (-75.36%)
Mutual labels:  mvvm
PlayAndroid
✌️✊👋玩安卓Mvvm组件化客户端,整合Jetpack组件DataBinding、ViewModel以及LiveData;屏幕适配✔️状态栏沉浸式✔️黑夜模式✔️,无数据、加载失败状态页;骨架屏、Koin依赖注入等
Stars: ✭ 193 (+179.71%)
Mutual labels:  mvvm

Ivia.js

A reactivity MVVM framework for jQuery with Vue-like interface.

Table of Contents

Getting Started

Installation

Install by npm or yarn.

npm install ivia --save

yarn add ivia

Install by bower

bower install ivia --save

Download single file for browser.

<script src="js/jquery.min.js"></script>
<script src="js/ivia.min.js"></script>

CDN

<script src="//unpkg.com/ivia/dist/ivia.min.js"></script>

If you are using ES6 syntax to import module, you must inject jQuery or Zepto first.

import jQuery from 'jquery';
import Ivia from 'ivia';

Ivia.$ = jQuery;

// Or Zepto
Ivia.$ = Zepto;

// Use _name to know which is in Ivia later
Ivia.$._name; // `jquery` or `zepto`

Make sure your environment has window and document object, Ivia does not support server environment now.

Create Ivia Instance

Ivia's interface is very similar to Vue, we can create an instance and add elin option to select element.

<div id="app"></div>

<script>
var sp = new Ivia({
  el: '#app', // You can also use $('#app')
  data: { ... },
  methods: {
    ...
  }
});
</script>

Sometimes you may need to put your code before HTML <body>, you can add domready option.

<head>
  <script>
  var sp = new Ivia({
    el: '#app',
    domready: true
  });
  </script>
</head>
<body>
  <div id="app"></div>
</body>

Or use jQuery plugin pattern.

$(document).ready(function () {
  var sp = $('#app').ivia({ ... });
});

Create Custom Plugin

Sometimes you may need to handle a big project with many re-usable widgets, you can use Ivia to help you create jQuery plugins.

// Do not pass `el` option
// This options will be default options.

Ivia.plugin('flower', {
  data: { ... },
  methods: { ... }
});

Now, use this plugin everywhere:

$('.flower-widget').flower();

// Or add custom options
$('.flower-widget').flower({
  data: {
    foo: 'bar'
  },
  method: {
    extraMethod: function () {}
  },
  extraOptions: {
    foo: 'bar',
    ajaxUrl: '/post'
  }
});

The new options will recursively merge with original one, so you can override any data. Use $options later in Ivia instance to get your extra options.

this.$options.extraOptions.ajaxUrl; // /post

Binding Data (One-way-binding)

Simple Binding

  • API: this.$bind(selector: string, dataPath: string, handler: string|Function)
  • Handler: function ($element: jQuery, value: mixed, oldValue: mixed, arrayControl: Object = null)

Ivia has a reactive data structure, you can bind an HTML element to a data value then if this data changed, Ivia will notice your handler to change DOM.

<div id="app">
  <p id="flower"></p>
</div>

<script>
var sp = new Ivia({
  el: '#app',
  data: {
    flower: {
      name: 'sakura',
      nmuber: 25 
    }
  },
  configure: function () {
    this.$bind('#flower', 'flower.name', ':text');
    this.$bind('#flower', 'flower.number', 'data-number');
  }
});
</script>

$bind() method help us bin a value to element we selected, don't worry to double selected, Ivia will cache it. the third argument tells Ivia to bind the attribute to this data. This is similar to Vue v-bind:data-number directive.

An attribute name start with : is a special control, currently we supports :text, :html and :value, which will auto use jquery .text(), html() or val() to bind data, otherwise will use attr().

The rendered result will be:

<p id="flower" data-number="25">sakura</p>

Now you can change data:

sp.flower.name = 'rose';

the text in <p> will change to rose, try this in playground

NOTE: All selector must under the root element which you set in el: option, Ivia will use this.$find() to find elements only contains in root element, if you have to control outside element, inject a custom element object. this.$bind(this.$('.my-outside-item'), 'flower')

Custom Handler

You will hope to do some extra action when data changed, just bind a callback.

this.$bind('#flower', 'flower.name', function ($ele, value, oldValue) {
  $ele.text(name);
  
  // Push new data to remote
  this.$.ajax(...); // this.$ is a jQuery obj reference 
  
  // Add new child to other element
  this.$find('.logs').append(
    // Ivia provides createElement() method
    // You can still use $(`<li>...</li>`) to create element.
    Ivia.createElement('li', {'data-number': this.flower.number}, value)  
  );
  
  // Show/hide
  if (value) {
    $ele.slideDown();
  } else {
    $ele.slideUp();
  }
});

Array Operation

If data is an array and changed by push, unshift and splice, there will be forth argument with control info, it includes method and args to help you know what has been inserted.

var sp = new Ivia({
  el: '#app',
  data: {
    items: ['a', 'b', 'c']
  },
  configure: function () {
    this.$bind('#flower', 'items', function ($ele, value, oldValue, ctrl) {
      if (ctrl) {
        switch (ctrl.method) {
          case 'push':
            ctrl.args.forEach(e => $ele.append(this.$(`<li>${e}</li>`)));
            break;
          case 'splice':
            ...
            break;
          case 'unshift':
            ...
            break;
        }
        return;
      }
      
      // Not array inserted, just refresh all
      $ele.empty();
      value.forEach(e => $ele.append(this.$(`<li>${e}</li>`)));
    });
  }
});

// Let's push a new item
sp.items.push('d');

DOM Events

  • API: this.$on(selector: string, eventName: string, handler: Function, delegate: boolean = false)
  • Handler: function ($ele, event)

Use $on() to bind DOM events to your data.

<div id="app">
  <input id="flower-input" type="text" />
  <p id="flower-output"></p>
</div>

<script>
var sp = new Ivia({
  el: '#app',
  data: {
    flower: ''
  },
  configure: function () {
    this.$on('#flower-input', 'input', function ($ele, event) {
      this.flower = $ele.val();
    });
    
    this.$bind('#flower-output', 'flower', ':text');
  }
});
</script>

See example

If your elements are not always on page, your JS will add or remove them, you can add true to forth argument to use jQuery delegate event binding, then jQuery will help you watch your element.

this.$on('#flower-input', 'input', $ele => this.flower = $ele.val(), true);

Two-Way Binding

  • API: this.$model(selector: string|Element, dataPath: string, delegate: boolean = false)

Two-way binding can only works for input, textarea and select now.

<div id="app">
  <input id="flower-input" type="text" />
  <p id="flower-output"></p>
</div>

<script>
var sp = new Ivia({
  el: '#app',
  data: {
    flower: 'initial data'
  },
  configure: function () {
    this.$model('#flower-input', 'flower')
      .$bind('#flower-output', 'flower', ':text');
  }
});
</script>

See Example

Show/Hide

  • API: this.$show(selector: string|Element, dataPath: string, onShow: string|Function, onHide: string|Function)

Use $show() to control an element show/hide depend on data, if value is true, not empty string or bigger then 0, this element will be visible on page.

<div id="app">
  <select name="" id="list">
    <option value="0">Zero</option>
    <option value="1">One</option>
    <option value="2">Two</option>
    <option value="-1">Negative One</option>
  </select>
  
  <div id="list-show">
    <ul>
      <li>A</li>
      <li>B</li>
      <li>C</li>
    </ul>
  </div>
</div>

<script>
var sp = new Ivia({
  el: '#app',
  data: {
    list: '1'
  },
  configure: function () {
    this.$show('#list-show li', 'list');
    
    this.$model('#list', 'list');
  }
});
</script>

See Example

Hide element

Use $hide(), the show/hide value will be reversed from $show().

Custom Show/Hide Handler

Use string as jQuery method.

// Same as $ele.show()
this.$show('#list-show li', 'list', 'show', 'hide');

// Same as $ele.fadeIn()
this.$show('#list-show li', 'list', 'fadeIn', 'fadeOut');

// Samse as $ele.slideDown()
this.$show('#list-show li', 'list', 'slideDown', 'slideUp');

If you only provide first handler, is will use as toggle handler.

this.$show('#list-show li', 'list', 'toggle'); // toggle show/hide

this.$show('#list-show li', 'list', 'fadeToggle'); // toggle fadeIn/fadeOut

Use callback:

this.$show('#list-show li', 'list', function ($ele, v, old, ctrl) {
  $ele.slideDown();
}, function ($ele, v, old, ctrl){
  $ele.slideUp();  
});

// Use ES6 arrow function
this.$show('#list-show li', 'list', $ele => $ele.slideDown(), $ele => $ele.slideUp());

See Example

Find Element

Use $find(selector) to find all children element in the root element. Ivia will cache selector so you won't have multiple select, if you want to refresh this select, add true at second argument.

The $bind(), $model and $show() method will cache target elements too, if you add some elements matches their selector, you must call $find(selector, true) to refresh cache. $on() method can simply add true at last argument to enable delegation.

Wrap

Simply use $wrap so you don't need always type same selector:

this.$wrap('#flower-selector', function ($ele, wrapper) {
  this.$bind('flower.name', ':text');
  this.$model('flower.name');
  
  // wrapper is same as this, useful for arrow function
  wrapper.$show('flower.show', 'fadeToggle');
});

Only supports $bind, $show, $model, $on methods.

Methods / Watchers and Computed

methods, watches and computed is same as Vue.

var sp = new Ivia({
  el: '#app',
  data: {
    firstName: 'john',
    lastName: 'williams'
  },
  configure: function () {
    this.$model('#first', 'firstName');
    this.$model('#last', 'lastName');
    
    this.$bind('#full-name', 'fullName', ':text');
  },
  methods: {
    capitalize: function (text) {
      return text.charAt(0).toUpperCase() + text.slice(1);
    }
  },
  computed: {
    fullName: function () {
      return this.capitalize(this.firstName) + ' ' + this.capitalize(this.lastName);
    }
  },
  watch: {
    foo: function (value, oldValue, ctrl) {
      // Do some manually update
    }
  }
});

Also support $watch() method:

this.$watch('foo.bar', function (value, oldValue, ctrl) {
    // Do some manually update
});

See Example and Vue: Computed amd Watchers

Events

Ivia events is also similar to Vue, the only different is that we change $on() method to $listen:

this.$listen('event-name', (arg) => {
    console.log(arg); // Hello
});

this.$emit('event-name', 'Hello');

this.$off('event-name');

Supported methods: $listen(), $emit(), $off(), $once().

Setters

You must also use $set() and $delete() to operate some object in data that Ivia can add reactive observer.

this.$set(this.$data.foo, 'bar', 'value');
this.$delete(this.$data.foo, 'bar', 'value');

See Vue doc: reavtivity

Async/Sync

nextTick

Ivia also supports $nextTick() after all watchers updated the DOM:

this.foo = 'new data';

this.$nextTick(function () {
  // Do something after DOM updated.
});

See Vue nextTick

Async DOM Operation

DOM operation is synchronous, Ivia provides an $async() method to help us do something asynchronous and return Promise/A+ object.

this.$async(function () {
  for (...) {
    $element.append(...); // Run 1000 times, here will not block your process
  }
}).then(function () {
  // Do something after DOM appended
});

// The below code will continue execute

this.$async(function () {
  for (...) {
    $element.append(...); // Run 1000 times, here will not block your process
  }
}).then(function () {
  // Do something after DOM appended
});

// These 2 tasks will not block each other.

Also you can use Promise.all() or Promise.race(), this example we convert jQuery ajax deferred object to Promise:

Ivia.Promise.all([
  this.$async(() => this.$.get(url1)),
  this.$async(() => this.$.post(url2, data)),
  this.$async(() => this.$.get(url3)),
]).then(() => {
    // will wait 3 ajax completed
});

Promise A/+

Ivia provides a Promise object in Ivia.Promise, if browser supports native Promise, this object will be reference of native Promise. If browser not support, Ivia will wrap jQuery.Deferred and adapt it to fit Promise A/+ interface.

new Ivia.Promise(resolce => {
    // Do something
    
    resolve(...);
});

Element Creation

In the original way, we use $ to create element:

Ivia.$('<div class="foo">Text</div>');

Ivia has a createElement() method to help you generate DOM element:

Ivia.createElement('div', {class: 'foo'}, 'Text');

We can add more children in the third argument.

Ivia.createElement('ul', {class: 'nav'}, [
    {nodeName: 'li', attributes: {class: 'nav-item active'}, children: 'Text'},
    {nodeName: 'li', attributes: {class: 'nav-item'}, children: 'Text'},
    {nodeName: 'li', attributes: {class: 'nav-item'}, children: 'Text'},
]);

Result:

<ul class="nav">
  <li class="nav-item active">Text</li>
  <li class="nav-item">Text</li>
  <li class="nav-item">Text</li>
</ul>

This is useful if we return Ajax JSON data to generate HTML.

TODO

  • Component system
  • Props
  • State control

Credit

This project is inspired by Evan You's Vue.js and the reactive structure is heavily referenced of Vue.

License

MIT

Copyright (c) 2017, Simon Asika

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].