Code Monkey home page Code Monkey logo

regular's Introduction

regularjs

Regularjs

Build Status

Regularjs is a living template engine that helps us to create data-driven components.

Features

  • String-based template makes it flexible to write your component;
  • data-binding based on dirty-check: experience from AngularJS-like frameworks also makes sense to regularjs;
  • self-contained and well-defined encapsulation makes it more easily integrated with other frameworks;
  • composite components: components can be used as "custom elements";
  • directive, filter, event and animation... all you need is provided out of the box with concise API.

Quick Start

Example 1: define a simple Note Component

var Note = Regular.extend({
  template:
    "<input {#if !disabled} r-model='hello' {#else} disabled {/if} > {hello} \
    <button on-click={disabled = !disabled}>{disabled? 'active': 'disable'} it</button>"
});

// inject component into #app , you can also inject at 'before' , 'after', 'top'.
var note = new Note().$inject("#app");

Example1 on codepen.io

This example is dead simple, but you can find the directive and attribute is easily switched by statement 'if', which is difficult with other mvvm frameworks.

Example 2: define a List Component

var NoteList = Regular.extend({
  template:
    "<ul>{#list notes as nt}" +
      "<li class={nt.done? 'done': ''} on-click={nt.done= !nt.done}>{{nt.content}}</li>" +
    "{/list}</ul>"
});

var list = new NoteList({
  data: {
    notes: [
      {content: 'playgame'},
      {content: 'homework'}
    ]
  }
}).$inject("#app");

In this Example, we create a ListView with the statement list.

Example2 on codepen.io

Example 3: combine Note with NoteList

We need to refactor Note to make it composable.

var Note = Regular.extend({
  name: 'note',  // register component during the definition of Component
  template:
   "<input r-model={draft}> <button on-click={this.post()}> post</button>",
  post: function(){
    var data = this.data;
    this.$emit('post', data.draft);
    data.draft = ""; //clear the draft
  }
});

Regular.component('list', NoteList);  // manual register a component

When 'Enter' is pressed, Note will emit a 'post' event with draft as the $event object.

The keyword this in the template refers to the component itself.

Then, let's define the core component: NoteApp.

var NoteApp = Regular.extend({
  template:
    "<note on-post={notes.push({ content: $event} )}/>"+
    "<list notes ={notes}></list>"
});

var noteapp = new NoteApp({
    data: {notes:[]}
});

noteapp.$inject('#app');

you can register a component (via attribute name or method Component.component) to make it composable in other components.

Example3 on codepen.io

See more on Guide: Quick Start

Resources

Browser Compatibility

IE7+ and other modern browsers.

Installation

bower

bower install regularjs

dist/regular.js has been packaged as a standard UMD, and therefore you can use it in AMD, commonjs or global.

npm (browserify or other based on commonjs)

$ npm install regularjs

use

var Regular = require('regularjs');

component

$ component install regularjs/regular

use

var Regular = require('regularjs/regular');

Direct download

  1. regular.js
  2. regular.min.js

Who are using?

  1. NetEase: the operator of the famous website www.163.com.

Community

Contributing

regularjs is still under heavy development, and please help us with feedback. Contributing to this project is also welcome.

  • Please open an issue before sending pull request
  • if needed, add your testcase in test/specs folder. Always make sure the gulp test is passed, and the test/runner/index.html is passed in every target browser (if a certain browser is not installed, list that in gulpfile's karmaConfig)

LICENSE

MIT.

TODO

remove log code in production mode;

regular's People

Contributors

alfredmou avatar aweiu avatar capasky avatar fengzilong avatar imhype avatar infinnie avatar jabez128 avatar leeluolee avatar lleohao avatar maiff avatar mekto avatar rainfore avatar thesadabc avatar ystarlongzi avatar yuanfux avatar zxc0328 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  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  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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

regular's Issues

r-hide={true}会报错

r-hide等指令中需要判断传进去值的类型,否则直接$watch会报错。

建议在指令机制中统一处理类型判断。

modify '{{}}' to '{}' or not? I need suggestion

I have done the work to config the END_TAG and BEGIN_TAG using

Regular.config({
   END: ']]',
  BEGINE: '[['
})

take this opportunity , I hesitate to change the default '{{}}' to '{}'。
double {{ obviously redundant。

changes looks small, but have big issue。

So It is really a big problem, it is all beacuse of my wrong decision for syntax。

plan to create bootstrap-regularjs for providing common component

A component should based particular html+css. using bootstrap beacuse of its Popularity

  1. version: bootstrap 3.x. lastest version. only ·css+html· is depended
  2. support: all component list in bootstrap/javascript and component that commonly used(date-picker, progress, notify.. etc)
  3. customize: providing building script (gulp command) for customizing package.

从null或undefined变成[]的时候,不会被更新

Demo如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>

<script src="http://rawgit.com/sindresorhus/multiline/master/browser.js"></script>
<script src="http://rawgit.com/regularjs/regular/master/dist/regular.min.js"></script>
<script>
var ListView = Regular.extend({
  name: 'listView',
  config: function() {
    var self = this;
    setTimeout(function() {
      self.$update('list1', []);
      self.$update('list2', [{name: 1}]);
    }, 200);
  },
  template: multiline(function(){/*
    {#list list as item}
      <div>{item.name}</div>
    {/list}
  */})
});

var App = Regular.extend({
  config: function() {
    // this.data.children1 = undefined;
    // this.data.children2 = undefined;
  },
  template: multiline(function(){/*
    <listView list1={children1} list2={children2} />
    {#if children1}Length: {children1.length}{/if}
    {#if children2}Length: {children2.length}{/if}
  */})
});

(new App()).$inject('body');

</script>
</body>
</html>

换行导致模板无法解析

  • 编辑器Web Storm 8
  • 操作系统 Mac OS X
  • 浏览器Chrome

模板内容

<input type="text"  class="form-control"
                               id="username" name="username" value="">

错误内容

Uncaught Error: Parse Error: expect [>]" -> got "[UNQ]:
16> ..."form-control"

将模板内容移到一行之后就正常了,按下enter换行以后就会报错

accessing DOM elements generated by regular

I'm looking for a way to access DOM elements generated by regular. I haven't found it in the documentation and the source code. But I think it is sometimes neccessery, especially when creating subcomponents using d3 or similar library.

Just by replacing

combine.node(this);

in Regular constructor with

this.$el = combine.node(this);

allows my to access DOM elements, e.g.:

this.$el.querySelector('svg.chart');

which is what I need.

I know that the regular instance has parentNode, but my components somethimes share same parent, so the selector above could select a node from different component.

Another point is that combine.node can return an array instead of an Element. This is what I really like in Regular, that it's components do not need to have a container. For example my component can generate two paragraphs and they can be injected after another two paragraphs and they all share the same parent. In this case this.$el will be an array of Elements, not one single Element. I think it is fine, because you are in control of what your template looks like and know if it at the top level returns one element or an array. Of cource this property could be named like $elements or $nodes and always return an array, not depending on a template.

One thing I haven't found solution yet, is when and how to update the $el property when the top level nodes change. In template

<p>One</p>
{{#if showSecond}}
<p>Two</p>
{{/if}}

the $el property will be an element or an array depending on the value of showSecond variable on init. If the value changes, the $el won't be updated, which is wrong. I think it is a corner case, but it still needs a solution. Please note, that I this condition would be inside one of these paragraphs, not at the top level, the $el wouldn't need any update at all.

<r-content/> not working

When inserting <r-content/> tag inside component it raises "Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'."

Looking at the source code I found that the test in browser-nested.js testing this tag is commented out. Is this deprecated or so?

source code comments need provided

  1. complete source code comments need provided help developer reading Regularjs
  2. api document can be generated though it

will provided them before 0.3.0

SVG elements are created without svg namespace

Children elements of SVG element are created without proper namespace and they are not displayed by the browser.

Looking at the code I've noticed that children elements are created before the <svg> element itself and before this._ns_ is set.

嵌套组件调用外层组件的方法this没有指向外层组件

波神, 最近写了一个简单的ajax组件,调用方式类似这样

<ajax url="data.json" dataType="json" onSuccess={this.handleData}></ajax>

直接将请求成功或者失败的方法作为属性, 但这样调用的外层组件的方法内的this不是指向外层组件的.

把它放到模板内:

    var Demo = Regular.extend({
        template: '<div class="content">{user}<ajax url="data.json" dataType="json" onSuccess={this.handleData}></ajax></div>',
        data: {
            user: ''
        },
        handleData: function(result) {
            var html = 'name: ' + result.name + ', gender: ' + result.gender;
            this.data.user = html;  //这里的this指向了window, 报错
            this.$update();  
        }
    });

    var demo = new Demo();

    demo.$inject('#app')

Use es6 backtick for multiline string

This project is pretty cool :)

Have you considered using es6 backtrick strings in order to get multiline instead of simple quote with backslash on every end of line?

  template: `
    <b>blabla
      <span>blabla</span>
    </span>
  `

Note: If you are using jsx already you can add --harmony=true flag to enable many es6 features including backtick strings.

item in list destroy all $context's event

{{#list todos as todo}}
      <div class='a-{{todo_index}}'>{{todo.content}}</div>
{{/list}}

if one item is completely changed, for example

component.data.todos[0] = {content: "haha"}

will wrongly delete the $context's _handles.

Some builtin stuff must be provided

current version, regularjs only support some basic directive like r-model. we plan to support some important directve, custom event and filter before version 0.3.0

there are the todolist:

  1. on-tap: tap support
  2. filter: format: basic date-format?
  3. fitler: sort: array sorter
  4. ...

if you need something to be supported, comment this issue.

调用$update方法报错

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
  <div id="wrap-comment">
    <script id="t-comment" type="text/regular">
    {#list items as el}
    123<br>
    {/list}
    </script>
  </div>

  <script src="regular.min.js"></script>
  <script type="text/javascript">
    var data = [{}];
    vComment = Regular.extend({
      template: '#t-comment'
    });
    vmComment = new vComment({
      data: {
        items: data
      , aaa: [{}]
      }
    });
    vmComment.$inject('#wrap-comment');

    setTimeout(function() {
      //vmComment.data.items = [{}, {}];
      vmComment.$update('items');
      //vmComment.$update('aaa');
    }, 100);
  </script>
</body>
</html>

报错:Uncaught TypeError: Cannot read property 'length' of undefined

组件怎么传事件?

<div class="ui large icon input {searchState.loading?'loading':''} {searchState.error?'error':''}">
  <input r-model="month" type="text" placeholder="Search month..." readonly="readonly">
  <i class="search icon" on-click="{this.search(month)}"></i>
</div>


<app-datepicker searchState={searchDaily} search={@(this.search)}></app-datepicker>

组件里面有个onclick时间,怎么传递?

ref r-mode同时使用,不能读取对象的值?

<input r-model="month" ref="datepicker" type="text" placeholder="Search month..." readonly="readonly">
    <i class="search icon" on-click="{this.search(month)}"></i>

month 总是undifine

search: function(month) {
        var component;
        component = this;
        if (month) {
          component.data.search = {
            error: false,
            loading: true
          };
          return $http.get('/api/v1.0/orders/daily', {
            month: month
          }, function(rep) {
            component.data.orders = rep;
            component.data.search = {
              loading: false
            };
            return component.$update();
          });
        } else {
          return component.data.search = {
            error: true
          };
        }
      }

accessing element which triggered an event

On events there is a special temporary variable called $event, but I am still unable to find the element which triggered the event. In traditional event handler the trigger element is binded as this, but regular overrides it.

How about providing another variable besides $event called $node or $trigger or accessed by $event.$trigger or something like this?

Not only a component library now. combine stateman to create SPA use Regularjs

I have completed the work to creating Single Page Application use Regularjs, and have test it in production , thanks for my patient colleague :).

The routing function is supported by stateman which is a nested state-based routing library.

final router.js looks like:

  .state("app", Application, "")
    .state('app.knowledge', Knowledge)
    .state('app.knowledge.list', KnowledgeList, '')
  .state('app.knowledge.manage', KnowledgeManage,  "manage/:type/:id")
  .state('app.question', Question)
    .state('app.question.list', QuestionList, 'list/:type')
    .state('app.question.manage', QuestionManage, "manage/:type/:method/:id")
  .state('app.testmanagement', TestManagement)
    .state('app.testmanagement.list', TestManagementList,'')
    .state('app.testmanagement.manage', TestManage, "manage/:type/:id")
    .state('app.testmanagement.rank', TestManageableRank,  "rank/:id")
    .state('app.testmanagement.profile', TestManagementProfile, "profile/:tid/:uid" )
  .state('app.preview', TestManagementPreview, "preview/:type/:id" )
  .state('app.group', Group)
    .state('app.group.list', GroupList, '')
    .state('app.group.manage', GroupManage, 'manage/:type/:id')
    .state('app.group.members', GroupMembers, 'members/:id/:type')
    .state('app.group.member', GroupMember, 'member/:id/:type')
  .state('app.judge', Judge)
  .state('app.judge.list', JudgeList, '')
  .state('app.judge.manage', JudgeManage, 'manage/:type/:id')
  .state('app.testlist', TestList)

  // test level1
  .state('app.test', Test )
    .state('app.test.list', TestList, '')
    .state('app.test.rank', TestRank, ':id/rank')
    .state('app.test.view', TestView, ':id/view')
  .state('app.exam', TestExam, '/test/:id')
    // choice or judge.
    .state('app.exam.judge', ExamSimple)
    .state('app.exam.choice', ExamSimple)
    .state('app.exam.blank', ExamSimple)
    .state('app.exam.list', ExamList, ":type(coding|complement)")
    // coding complement 
    .state('app.exam.complex', ExamComplex, ':type(coding|complement)/:qid'  )
    .state('app.exam.complex.detail', ExamComplexDetail, '')  //question id
    .state('app.exam.complex.submit', ExamComplexSubmit)
    // need preview or &preview or sid  result?sid&preview
    .state('app.exam.complex.result', ExamComplexResult)
 .....

The code is from the application that still in developing. router is much bigger now, but works fine.

the second param passed to state is standard Regularjs Component, the only thing you must do is to mark a view tag in each parent state.

take app.exam for example, its template need create a ref named view

...
<div>
  <div class="m-emn" ref=view></div>
</div>
...

then the child state app.exam.judge will automately inject its template to the ref view and so on

html entity isn't converted

beacuse regularjs is use textNode to accpet the textContent(not innerHTML) , so the html entity is not converted

for example

<p> &nbsp; </p>

will output

&nbsp;
;

svg elements have incorrect namespaceURI inside if/list controls

Inside {{#if ..}} and {{#list ..}} controls svg elements are created with wrong namespaceURI. Like in example below:

<svg viewBox="0 0 100 100">

  <!-- this element is displayed correctly -->
  <line y1="100" y2="100" stroke="#fff"/>

  {{#if true}}
  <!-- this element gets xhtml namespace and is not displayed by the browser -->
  <line y1="90" y2="90" stroke="#0f0"/>
  {{/if}}

</svg>

sub component need initial state

子component应该在创建时候获取到初试数据, 因为子组件是直接创建而不是build之后,这导致子元素会在父元素之前init。所以可能会出现undefined的情况,所以应该传入初始值

 var Demo = Regular.extend({name: "demo", data: {}, template: "<input r-model = 'demo.name' title={{demo.name}}>"})
  var DemoApp = Regular.extend({name: "todoapp", data: {demos: [{name:1}] }, template: "{{#list demos as demo}}<demo demo={{demo}}/>{{/list}}"});

console.log(new DemoApp().$inject(document.body)); //throw error


Animation events on:enter and on:leave should be propagated to descendant elements

I have some issues with Regular animations. Very often the animations are not called, because the element which I want to animate is wrapped in another element, like in example below:

{#if show}
<!-- this element will be animated as expected -->
<div class="box" r-animation="on:enter;slide:in"></div>

<div>
  <!-- this element won't be animated -->
  <div class="box" r-animation="on:enter;slide:in"></div>
</div>
{/if}

I find this behavior unreliable, especially when I have some animations in components, sometimes they are fired, sometimes not. Also there are some elements you don't want to animate, like <tr> when it is shown, but you want to animate elements inside of it, but how to do this in Regular?

The similar goes to on:leave animation. Element should be removed from the DOM only when all of it's descendant elements have stopped being animated. Now it is just removed immediately.

I think that is also could be useful, although less important, to provide additional directives for on:enter and on:leave animations, some kind of shortcuts, because they are quite common. For example Ractive has intro and outro directives which are very useful and commonly used.

I would also like to mention, that the animation directive could be also used for something different than animating, eg.

<!-- in Ractive.js -->
<input type="text" intro="focus">

<!-- in Regular.js -->
<input type="text" r-animation="on:enter;focus">

which is very simple to create:

Regular.animation('focus', function(t) {
  return function(complete) {
    t.element.focus();
    complete();
  };
});

Unfortunately because of the problem with wrapping elements, this event in Regular in most cases won't be fired.

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.