componentjs-mvc
v1.0.7
Published
Abstract Classes for Model-View-Controller Component Roles
Downloads
2
Maintainers
Readme
ComponentJS MVC
Model-View-Controller Component Roles.
About
ComponentJS is a generic hierarchical component orchestration library. It has no direct notion of any component roles, although it was designed with the roles of Ralf S. Engelschall's Model-View-Controller/Component-Tree (MVC/CT) pattern in mind. ComponentJS MVC is an opinionated companion library to ComponentJS, providing ECMAScript 6 abstract classes for implementing the classes of ComponentJS backing objects by using the distinct Model, View or Controller roles of the MVC/CT pattern. The abstract classes map the major and essential parts of the ComponentJS API onto local methods which then provide the ComponentJS functionality in a way which is slightly adjusted for convenience reasons and their distinct usage under MVC/CT. ComponentJS MVC is intended to simplify the programming of HTML5 Single-Page-Apps (SPA) in an Object-Oriented Programming (OOP) manner.
Installation
# with the help of the NPM package manager (primary approach)
$ npm install componentjs-mvc
# with the help of the YARN package manager (alternative approach)
$ yarn add componentjs-mvc
# with the help of the Bower package manager (obsolete approach)
$ bower install componentjs-mvc
Usage
Main Procedure:
import $ from "jquery"
import cs from "componentjs"
import "componentjs/component.plugin.vue.js"
import mvc from "componentjs-mvc"
import SV from "./service"
class App {
static main () {
/* fire up ComponentJS */
cs.bootstrap()
cs("/").property("sv", new SV())
/* fire up ComponentJS MVC */
mvc.jQuery = $
mvc.ComponentJS = cs
mvc.Plugin()
mvc.latch("mask:vue-options", ({ id, options }) => {
/* example: provide id to Vue-I18Next Vue plugin */
options.i18nextNamespace = id
})
mvc.latch("mask:vue-result", ({ id, mask }) => {
/* example: integrate Perfect-Scroll-Bar jQuery plugin */
$(".perfect-scroll-bar", mask.$el).perfectScrollbar({})
})
}
}
Dialog Component:
import mvc from "componentjs-mvc"
class View extends mvc.View {
...
}
class Model extends mvc.Model {
...
}
class Controller extends mvc.Controller {
...
}
Application Programming Interface (API)
Globals:
import mvc from "componentjs-mvc"
: Load the ComponentJS-MVC library.mvc.ComponentJS = ...
: Configure ComponentJS-MVC to use a particular instance of the mandatory ComponentJS framework. By default ComponentJS-MVC uses the global symbolComponentJS
.mvc.jQuery = ...
: Configure ComponentJS-MVC to use a particular instance of the optional jQuery library. By default ComponentJS-MVC uses the global symboljQuery
. jQuery is necessary only if themvc.View::$()
method should be used. jQuery and jQuery-Markup are necessary only if themvc.View::mask()
method should be used with neithertemplate
norrender
options.mvc.Plugin()
: Hook into ComponentJS as anmvc
plugin to automatically mark dynamically created ComponentJS-MVC-based components with the proper marker for the ComponentJS debugger without having to override thecreate()
method and this way cause asuper.create()
burden on the users of the ComponentJS-MVC classes. Call this once in your main procedure if you want proper colors in the ComponentJS debugger for ComponentJS-MVC-based components.
Hooks:
mvc.latch("mask:vue-options", (({ id, options }) => { ... })
: Hook into themvc.View::mask()
method just beforemask = ComponentJS::vue(options)
is called internally. Use this to set particular options for VueJS. For instance, usemvc.latch("mask:vue-options", ({ id, options }) => { options.i18nextNamespace = id })
to set the I18N namespace when using VueJS and the Vue-I18Next plugin together.mvc.latch("mask:vue-result", (({ id, result }) => { ... })
: Hook into themvc.View::mask()
method just aftermask = ComponentJS::vue(options)
is called internally. Use this to post-process the VueJS instance. For instance, usemvc.latch("mask:vue-result", (({ id, result }) => { $(".perfect-scroll-bar", mask.$el).perfectScrollbar({ ... }) })
to apply the jQuery Perfect-Scrollbar plugin.mvc.latch("establish:post-create", ({ id, comp, obj }) => { ... })
: Hook into themvc.Component::establish()
method after each component was created. Use this to post-adjust created components. Thecomp
is the created component,obj
the attached backing object derived from the arguments tomvc.Component::establish()
.mvc.latch("establish:pre-destroy", ({ id, comp, obj }) => { ... })
: Hook into themvc.Component::demolish()
method before each component will be destroyed. Use this to pre-process components. Thecomp
is the created component,obj
the attached backing object derived from the arguments tomvc.Component::establish()
.
Classes:
class Component extends mvc.Component { ... }
: Define an application Component class based on ComponentJS-MVC's Component class (usually not directly used in regular applications, but exposed for completeness reasons).class View extends mvc.View { ... }
: Define an application View class based on ComponentJS-MVC's View class. Themvc.View
class inherits from themvc.Component
class.class Model extends mvc.Model { ... }
: Define an application Model class based on ComponentJS-MVC's Model class. Themvc.Model
class inherits from themvc.Component
class.class Controller extends mvc.Controller
: Define an application Controller class based on ComponentJS-MVC's Controller class. Themvc.Controller
class inherits from themvc.Component
class.
Methods (Component/View/Model/Controller Classes):
mvc.Component::cs(...): ComponentJS
: Use the configured ComponentJS by just passing-through execution to ComponentJS. Use this in case you want to directly access the ComponentJS API from within a ComponentJS-MVC component.mvc.Component::establish(anchor?: Component, tree: String, classes: Object|Object[], autoincrease?: Boolean = true, autodecrease?: Boolean = false): mvc.Component
: This is a convenience wrapper aroundComponentJS::create()
. It internally basically callsmvc.ComponentJS(this[, anchor]).create(tree, classes.map((Clz) => typeof Clz === "function" ? new Clz() : Clz))
and remembers the created components by their id for forthcoming use viamvc.Component::my()
. It also callsComponentJS::state_auto_increase(autoincrease)
andComponentJS::state_auto_decrease(autodecrease)
on all created components.mvc.Component::demolish(id?: String): mvc.Component
: This is a convenience wrapper aroundComponentJS::destroy()
. It either destroys just the specified component (by id) or all components previously created withmvc.Component::establish()
.mvc.Component::my(id: String): ComponentJS
: This fetches (by id) a particular component previously created withmvc.Component::establish()
.mvc.Component::exists(id: String): ComponentJS
: This checks (by id) whether a particular component previously was created withmvc.Component::establish()
and was still not destroyed withmvc.Component::demolish()
.mvc.Component::state(...args: any[]): ComponentJS
: This is a convenience wrapper aroundComponentJS::state()
. Theargs
positional arguments are just passed-through. The return value is the value ofComponentJS::state()
.mvc.Component::guard(...args: any[]): ComponentJS
: This is a convenience wrapper aroundComponentJS::guard()
. Theargs
positional arguments are just passed-through. The return value is the value ofComponentJS::guard()
.mvc.Component::await(...args: any[]): ComponentJS
: This is a convenience wrapper aroundComponentJS::await()
. Theargs
positional arguments are just passed-through. The return value is the value ofComponentJS::await()
.mvc.Component::spool(func: Function, options?: Object): mvc.Component
: This is a convenience wrapper aroundComponentJS::spool()
. It internally basically callsmvc.ComponentJS(this).spool({ name: mvc.ComponentJS(this).state(), ctx: this, func: func, ...options })
. The twist of this wrapper is that it automatically uses the component state based spool.mvc.Component::observe(name: String|String[], func: Function, options?: Object): mvc.Component
: This is a convenience wrapper aroundComponentJS::observe()
. It internally basically callsmvc.ComponentJS(this[, "model"]) .observe({ name, func, spool: mvc.ComponentJS(this).state(), noevent: true, ...options })
. The twists of this wrapper are: it performs automatical spooling, uses no event argument in thefunc
callback and automatically steps down to amodel
child ifthis
is amvc.Controller
. Additionally, instead of a singlename
, it allows you to pass an array of names.mvc.Component::value(...args: any[]): any
: This is a convenience wrapper aroundComponentJS::value()
. It internally basically callsmvc.ComponentJS(this[, "model"]) .value(...args)
. The main twist of this wrapper is: it automatically steps down to amodel
child ifthis
is amvc.Controller
.mvc.Component::touch(...args: any[]): mvc.Component
: This is a convenience wrapper aroundComponentJS::touch()
. It internally basically callsmvc.ComponentJS(this[, "model"]) .touch(...args)
. The main twist of this wrapper is: it automatically steps down to amodel
child ifthis
is amvc.Controller
.mvc.Component::subscribe(name: String|String[], func: Function, options?: Object): mvc.Component
: This is a convenience wrapper aroundComponentJS::subscribe()
. It internally basically callsmvc.ComponentJS(this).subscribe({ name, func, spool: mvc.ComponentJS(this).state(), noevent: true, capturing: false, spreading: false, bubbling: true, ...options })
. The twists of this wrapper are: it performs automatical spooling, uses no event argument in thefunc
callback, disables capturing and spreading, and enables bubbling. Additionally, instead of a singlename
, it allows you to pass an array of names.mvc.Component::publish(name: String, args: any[], options?: Object): any
: This is a convenience wrapper aroundComponentJS::publish()
. It internally basically callsmvc.ComponentJS(this).publish({ name, args, directresult: true, capturing: true, spreading: false, bubbling: true, ...options })
. The twists of this wrapper are: enables direct results, enables capturing and bubbling and disables spreading.mvc.Component::register(name: String|String[], func: Function, options?: Object): mvc.Component
: This is a convenience wrapper aroundComponentJS::register()
. It internally basically callsmvc.ComponentJS(this).register({ name, func, spool: mvc.ComponentJS(this).state(), capturing: false, spreading: false, bubbling: true, ...options })
. The twists of this wrapper are: it performs automatical spooling, disables capturing and spreading, and enables bubbling. Additionally, instead of a singlename
, it allows you to pass an array of names.mvc.Component::call(name: String, args: any[], options?: Object): any
: This is a convenience wrapper aroundComponentJS::call()
. It internally basically callsmvc.ComponentJS(this).call({ name, args, capturing: true, spreading: false, bubbling: true, ...options })
. The twists of this wrapper are: it enables capturing, disabled spreading and enables bubbling.
Methods (View Class):
mvc.View::$(selector: String, baseElement?: DOMElement): jQuery
: Use the configured jQuery by just passing-through execution to jQuery. Use this in case you want to directly manipulate the DOM via jQuery from within a View component. Use with caution, as jQuery and VueJS can conflict.mvc.View::mask(id: String, options?: any): VueJS
: Create a UI Mask with the help of VueJS. Theid
has to be unique within the UI, although it is not directly used by ComponentJS-MVC itself. It is intended to be passed through to themask:vue-options
hook and used there accordingly. Theoptions
are just passed-through to theComponentJS::vue()
method. Hence, this method requires the ComponentJSvue
plugin to be loaded first.mvc.View::socket(ctx: Object, plug: Function, unplug: Function): Number
:mvc.View::socket(options: { ctx: Object, plug?: Function, unplug?: Function, ... }): Number
: This is a convenience wrapper aroundComponentJS::socket()
. Thectx
,plug
andunplug
positional arguments are just converted to theoptions
form of method calling. If theoptions
argument has nospool
field, it is automatically created with the value ofmvc.ComponentJS(this).state()
. The return value is the value ofComponentJS::socket()
.mvc.View::link(target: Object, socket: String): Number
:mvc.View::link(options: { target: Object, socket: String, ... }): Number
: This is a convenience wrapper aroundComponentJS::link()
. Thetarget
andsocket
positional arguments are just converted to theoptions
form of method calling. If theoptions
argument has nospool
field, it is automatically created with the value ofmvc.ComponentJS(this).state()
. The return value is the value ofComponentJS::link()
.mvc.View::plug(object: Object): Number
:mvc.View::plug(options: { object: Object, ... }): Number
: This is a convenience wrapper aroundComponentJS::plug()
. Theobject
positional argument is just converted to theoptions
form of method calling. If theoptions
argument has nospool
field, it is automatically created with the value ofmvc.ComponentJS(this).state()
. The return value is the value ofComponentJS::plug()
.
Methods (Model Class):
mvc.Model::model(spec: Object): Void
: This just passes-throughspec
to theComponentJS::model()
method.
Methods (Controller Class):
mvc.Controller::sv(): Object
: This is a short-hand formvc.ComponentJS(this).property("sv")
and simplifys the fetching of the underlying Service API, which beforehand should be placed into a property on the root component viamvc.ComponentJS("/").property("sv", value)
.
License
Copyright (c) 2016-2018 Ralf S. Engelschall (http://engelschall.com/)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.