Code Monkey home page Code Monkey logo

egjs-component's Introduction

egjs-component npm version Build Status Coverage Status

A class used to manage events and options in a component

Documents

Download and Installation

Download dist files from repo directly or install it via npm.

For development (Uncompressed)

You can download the uncompressed files for development

For production (Compressed)

You can download the compressed files for production

Installation with npm

The following command shows how to install egjs-component using npm.

$ npm install @egjs/component

Supported Browsers

The following are the supported browsers.

Internet Explorer Chrome Firefox Safari iOS Android
7+ Latest Latest Latest 7+ 2.1+(except 3.x)

How to Use

1. Load component.js

<script src="../dist/component.js"></script>

2. Use Component

import Component from "@egjs/component";

class Some extends Component {
  hi() {
    alert("hi");
  }
  thing() {
    this.once("hi", this.hi);
  }
}

var some = new Some();
some.thing();
some.trigger("hi");
// fire alert("hi");
some.trigger("hi");
// Nothing happens

3. Typescript Support

You can use Typescript Generics to define events that component can emit.

import Component, { ComponentEvent } from "@egjs/component";

interface Events {
  event1: {
    prop1: number;
    prop2: string;
  };
  // You can also define like this if there're more than one arguments for a event
  event2: (arg0: number, arg1: string, arg2: boolean) => void;
  // If there're no event props
  event3: void;
  // If you want additional properties like `currentTarget`, `eventType`, `stop`
  // You can use ComponentEvent with additional properties
  event4: ComponentEvent<{ a: number; b: string }>;
}

class SomeClass extends Component<Events> {
  thing() {
    this.on("event4", e => {
      // These properties will be available for typescript-enabled environment
      e.a; // number
      e.b; // string
      e.currentTarget; // SomeClass(this)
      e.eventType; // string("event4")
      e.stop(); // () => void
      e.isCanceled(); // Will return true when "stop()" is called.
    });
  }
}

// You can trigger events like this
const component = new SomeClass();
component.trigger("event1", {
  prop1: 1,
  prop2: "abc"
});
component.trigger(new ComponentEvent("event4", { a: 123, b: "abcd" }));

Bug Report

If you find a bug, please report it to us using the Issues page on GitHub.

License

@egjs/component is released under the MIT license.

Copyright (c) 2015 NAVER Corp.

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.

egjs-component's People

Contributors

daybrush avatar haeguri avatar happyhj avatar jaeyeophan avatar kishu avatar malangfox avatar mixed avatar sculove avatar uyeong avatar woodneck avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

egjs-component's Issues

Manage Event

When I use many Components I tired to manage events.

Change return type in `component.d.ts`

Description

In 2.1.0 version

declare class Component {
  // ...
  on(eventName: string, handlerToAttach: (event: { [key: string]: any }) => any): Component;
  // ...
}

I think this part should be changed as follows, I will ask you to review that part.

declare class Component {
  // ...
  on(eventName: string, handlerToAttach: (event: { [key: string]: any }) => any): this;
  // ...
}

=> : Componentis to be : this

In 2.1.0-snapshot, already has this part applied.
By the way, should not namespace part remain the same?

AS-IS

declare namespace Component {
  // ...
  function on(eventName: string, handlerToAttach: (event: { [key: string]: any }) => any): this;
  // ...
}

TO-BE

declare namespace Component {
  // ...
  function on(eventName: string, handlerToAttach: (event: { [key: string]: any }) => any): Component;
  // ...
}

I will request a review of that part. Thanks.

More flexible event structures

Description

As-is:

  • Always have to use objects for event definitions

To-be:

  • Can use any types for the event definitions

Static method getInstace & destroy

class A extends Component{
}

class B extends Component{
}

new A();
new B();

Component.getInstanceAll();
// [instanceA, instanceB]
class A extends Component{
}

class B extends Component{
}

const a = new A();
new B();

Component.getInstanceAll();
// [instanceA, instanceB]

a.destory();

Component.getInstanceAll();
// [instanceB]

Modifying common js behavior in node environment

Description

Before

import Component from "./Component";
import ComponentEvent from "./ComponentEvent";

(Component as any).ComponentEvent = ComponentEvent;

export default Component;

After

import Component, * as modules from './index';

for (const name in modules) {
  (Component as any)[name] = (modules as any)[name];
}

declare const module: any;
module.exports = Component;
export default Component;
export * from './index';

Steps to check or reproduce

Reusing trigger customEvent object

@vreality64 commented on Mon Feb 20 2017

I'll mention 3 things.

  1. Situation I been through.
  2. The thing what I expected.
  3. Solution.

Situation I been through

I have one controller and two modules. everything is class inherited from eg.Component.

// Controller, Module A, Module B
var instance = new eg.Class.extend(eg.Component, {
  // ...
});

I tried handle custom event state between modules and controller using customEvent.stop() function. But stop was not worked.

Trigger Docs: If the stop() method is called by a custom event handler, it will return false and prevent the event from occurring.

Here are code.

// Controller has Module A. Module A has Module B.
// every callback function is wrapped `$.proxy` with `this` context.
// this.module.on('eventName', $.proxy(callback, this));

// In Controller
moduleA.on('beforeSelect', function (event) {
  event.stop();
});

// In Module A
moduleB.on('beforeSelect', function (event) {
  this.trigger('beforeSelect', event);	// reuse event object
});

// In Module B
this.on('click', function (event) {
  if (this.trigger('beforeSelect')) {
    this.select('data');
  }
})

The thing what I expected

Code describes that if controller calls event.stop() then doesn't execute moduleB.select(). because stop() makes trigger return false.

I think reusing custom event is common case. normally custom event has data, so it was broadcasted to system.

But when it reused, isCanceled variable was overridden.

trigger: function(eventName, customEvent) {
  // event(data) Object are alive.
  customEvent = customEvent || {};
  
  // ...
  // isCanceled variable is overridden.
  var isCanceled = false;
  
  // ...
  return !isCanceled;
},

If intended, it is notable.

Solution

I changed middle module. not re-used event object

// [Before] In Module A
moduleB.on('beforeSelect', function (event) {
  this.trigger('beforeSelect', event);	// reuse event object
}
           
// [Changed] In Module A
moduleB.on('beforeSelect', function (event) {
  var stop = !this.trigger("beforeSelect");	// not re-used event object
  if (stop) {
    event.stop();
  }
}

I made JS fiddle example. you can reproduce my situation.

Question

I can't sure what is right, so just want to know decision history. what philosophy / thoughts leads this conclusion?


@mixed commented on Mon Feb 20 2017

@vreality64
Thank you for your report. I am going to anwser it soon. ๐Ÿ˜„


@mixed commented on Tue Feb 21 2017

I think that This case is very important issue. Sometimes Component has not to fire event. So if trigger add option like boolean for event fire condition. I think differently It is duplicate feature(stop method). So. It need more idea.

Impl to currentTarget attribute of event.

When Component triggered event, It has not found component instance in event listener.
So, Below case is inconvenience.

to-be

class Test {
	constructor(){
		this.bind();
	}
	bind(){
		const scroll = new ScrollComponent();
		scroll.on("change",this.change.bind(this));
	}
	change(e){
		// I can not found scroll instance.
	}
}

as-is

class Test {
	constructor(){
		this.bind();
	}
	bind(){
		const scroll = new ScrollComponent();
		scroll.on("change",this.change.bind(this));
	}
	change({currentTarget}){
		currentTarget; // It is scroll instance.
	}
}

Why we decided to use currentTarget

Almost Component design is affected by DOM interface. ref So. We decided to use currentTarget of DOM interface.

Returns the object whose event listener's callback is currently being invoked.

subscribe/unsubscribe method

Description

Sometimes I have to listen all event in component.

Steps to check or reproduce

class Test extends Component{
	foo(){
		this.trigger("foo",1);
	}
	bar(){
		this.trigger("bar",2);
	}
}

const test = new Test();

test.subscribe( data => {
	console.log(data);
	// {"name":"foo","data":1}
	// {"name":"bar","data":2}
});

test.foo();
test.bar();

Event Propagation

there are suggestion that is event propagation.
It is not absolutely necessary, but I think it would be good to apply.

First, let me share my experience.
the experience was limited to UI development.

i think the UI has a hierarchical structure in general, whether it is physical or conceptual.
so when i applied this component to UI, events also had to be designed to follow the hierarchy.

when, i designed to

SearchPage (independent component)
   ใ„ด SearchInput
   ใ„ด Filter
   ใ„ด Category

the event structure was as follow,

SearchPage (independent) - onSubmit, onClear, onFilter, onSort,...
   ใ„ด SearchInput - onSubmit, onClear
   ใ„ด Filter - onFilter
   ใ„ด Sort - onSort

and implemented this as below.

class SearchPage extends eg.Component {
    constructor() {
        super();
        this.context = {};
        this.searchInput = new SearchInput(); // child component
        this.filter = new Filter();  // child component
        // intercepts event
        this.searchInput.on('submit', (event) => {
            // bubbling
            event.pageContext = context;
            this.trigger(event.eventType, event)
        });
    }
}

const searchPage = new SearchPage();

searchPage.on('submit', (event) => {
    // todo - external module
})

but I thought it would be better if the component itself provided it systematically.
because the deeper the component, the more it will equal callback hell.

thanks for reading.

deprecated option, add prop.

[WIP]

I think that Currently option method is unnecessary feature. Because Almost Component update attribute directly option object.

Sometimes Component have to know changing value of options. I suggest event and method.

class SomeThing extends Component{
	constructor(){
		// setup prop
		this.prop({
			witdh : 100,
			height : 200
		});
	}
}

const someThing = new SomeThing();
someThing.on("propChange", (data) => {
	console.log(data);
	/*
	{
		width : {
			prevValue : 100,
			newValue : 200
		}
	}
	*/
});
someThing.prop("width",200);

This idea affected by DOM.

Supporting esModule

Description

In the @egjs/flicking module, this @egjs/component is imported as follows for the type definition.

import * as Component from "@egjs/component";

Currently, we use esModuleInterop ts-compiler option in TypeScript 2.7.x version. So, the current version of typscript and import method are not appropriate.

How about supporting esModule like @egjs/axes?

eventType is overwritten by event props

Description

If eventType is given inside event props, it will overwrite actual event name

Steps to check or reproduce

const evt = new ComponentEvent("correct_name", {
  eventType: "wrong_name"
});

evt.eventType // "wrong_name"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.