Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[jsfm] support component methods in another way #1604

Merged
merged 3 commits into from
Nov 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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