Skip to content

Commit

Permalink
Merge pull request #1604 from Jinjiang/jsfm-feature-component-methods
Browse files Browse the repository at this point in the history
[jsfm] support component methods in another way
  • Loading branch information
Hanks10100 committed Nov 22, 2016
2 parents 6e32354 + 4d6497f commit 9773f7d
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 9 deletions.
15 changes: 15 additions & 0 deletions html5/runtime/init.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { registerElement } from './vdom/element-types'

let frameworks

const versionRegExp = /^\s*\/\/ *(\{[^}]*\}) *\r?\n/
Expand Down Expand Up @@ -59,6 +61,9 @@ const methods = {
*/
function genInit (methodName) {
methods[methodName] = function (...args) {
if (methodName === 'registerComponents') {
checkComponentMethods(args[0])
}
for (const name in frameworks) {
const framework = frameworks[name]
if (framework && framework[methodName]) {
Expand All @@ -68,6 +73,16 @@ function genInit (methodName) {
}
}

function checkComponentMethods (components) {
if (Array.isArray(components)) {
components.forEach((name) => {
if (name && name.type && name.methods) {
registerElement(name.type, name.methods)
}
})
}
}

/**
* Register methods which will be called for each instance.
* @param {string} methodName
Expand Down
16 changes: 16 additions & 0 deletions html5/runtime/listener.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,22 @@ Object.assign(Listener.prototype, {
return this.addActions(createAction('removeEvent', [ref, type]))
},

/**
* Call a component method with args.
* @param {string} ref
* @param {string} type
* @param {string} method
* @param {array} args
* @return {undefined | number} the signal sent by native
*/
callComponentMethod (ref, type, method, args) {
return this.addActions({
component: type,
method,
args: [ref, ...args]
})
},

/**
* Default handler.
* @param {object | array} actions
Expand Down
57 changes: 57 additions & 0 deletions html5/runtime/vdom/element-types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { getListener } from './operation'

let Element

export function setElement (El) {
Element = El
}

/**
* A map which stores all type of elements.
* @type {Object}
*/
export const elementTypes = {}

/**
* Register an extended element type with component methods.
* @param {string} type component type
* @param {array} methods a list of method names
*/
export function registerElement (type, methods) {
// Skip when no special component methods.
if (!methods || !methods.length) {
return
}

// Init constructor.
const XElement = function (props) {
Element.call(this, type, props, true)
}

// Init prototype.
XElement.prototype = Object.create(Element.prototype)
XElement.prototype.constructor = Element

// Add methods to prototype.
methods.forEach(methodName => {
XElement.prototype[methodName] = function (...args) {
const listener = getListener(this.docId)
if (listener) {
listener.callComponentMethod(this.ref, type, methodName, args)
}
}
})

// Add to element type map.
elementTypes[type] = XElement
}

/**
* Clear all element types. Only for testing.
*/
export function clearElementTypes () {
for (const type in elementTypes) {
delete elementTypes[type]
}
}

12 changes: 11 additions & 1 deletion html5/runtime/vdom/element.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,18 @@ import {
moveIndex,
removeIndex
} from './operation'
import {
elementTypes,
setElement
} from './element-types'

const DEFAULT_TAG_NAME = 'div'

export default function Element (type = DEFAULT_TAG_NAME, props) {
export default function Element (type = DEFAULT_TAG_NAME, props, isExtended) {
const XElement = elementTypes[type]
if (XElement && !isExtended) {
return new XElement(props)
}
props = props || {}
this.nodeType = 1
this.nodeId = uniqueId()
Expand All @@ -41,6 +49,8 @@ function registerNode (docId, node) {
doc.nodeMap[node.nodeId] = node
}

setElement(Element)

Object.assign(Element.prototype, {
/**
* Append a child node.
Expand Down
8 changes: 7 additions & 1 deletion html5/runtime/vdom/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import Node from './node'
import Comment from './comment'
import Element from './element'
import Comment from './comment'
import Document from './document'

export {
elementTypes,
registerElement,
clearElementTypes
} from './element-types'

export {
Document,
Node,
Expand Down
60 changes: 53 additions & 7 deletions html5/test/unit/vdom/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ global.callAddElement = function () {}
import {
Document,
Element,
Comment
Comment,
elementTypes,
registerElement,
clearElementTypes
} from '../../../runtime/vdom'

global.callNative = function () {}
Expand Down Expand Up @@ -38,6 +41,49 @@ describe('document constructor', () => {
})
})

describe('component methods management', () => {
before(() => {
registerElement('x', ['foo', 'bar'])
registerElement('y', [])
registerElement('z')
})

after(() => {
clearElementTypes()
})

it('has registered element types', () => {
expect(Object.keys(elementTypes)).eql(['x'])
})

it('will call component method', () => {
const spy = sinon.spy()
const doc = new Document('test', '', spy)
const x = new Element('x')
const y = new Element('y')
const z = new Element('z')
const n = new Element('n')
expect(x.foo).is.function
expect(x.bar).is.function
expect(x.baz).is.undefined
expect(y.foo).is.undefined
expect(z.foo).is.undefined
expect(n.foo).is.undefined

doc.createBody('r')
doc.documentElement.appendChild(doc.body)
doc.body.appendChild(x)
doc.body.appendChild(y)
doc.body.appendChild(z)
doc.body.appendChild(n)
expect(spy.args.length).eql(5)

x.foo(1, 2, 3)
expect(spy.args.length).eql(6)
expect(spy.args[5]).eql([[{ component: 'x', method: 'foo', args: [x.ref, 1, 2, 3] }]])
})
})

describe('document methods', () => {
let doc

Expand Down Expand Up @@ -435,12 +481,12 @@ describe('complicated situations', () => {
doc = new Document('foo', '', spy)
doc.createBody('r')
doc.documentElement.appendChild(doc.body)
el = new Element('bar', null, doc)
el2 = new Element('baz', null, doc)
el3 = new Element('qux', null, doc)
c = new Comment('aaa', doc)
c2 = new Comment('bbb', doc)
c3 = new Comment('ccc', doc)
el = new Element('bar')
el2 = new Element('baz')
el3 = new Element('qux')
c = new Comment('aaa')
c2 = new Comment('bbb')
c3 = new Comment('ccc')
})

afterEach(() => {
Expand Down

0 comments on commit 9773f7d

Please sign in to comment.