Coder Social home page Coder Social logo

tamb / domponent Goto Github PK

View Code? Open in Web Editor NEW
75.0 2.0 2.0 38.6 MB

Build UI Components with the HTML You Already Have

Home Page: https://tamb.github.io/domponent/

License: MIT License

JavaScript 100.00%
stimulusjs lifecycle-methods component-lifecycle stateless-components react components

domponent's Introduction

๐Ÿ”Œ</> DOMponent

Build UI Components with the HTML You Already Have.
2kb gzipped and 6kb minified! ๐Ÿ‘Œ

How To:

  1. Drop a few data attributes into your existing HTML ๐Ÿ’ป
<div data-component="Counter">
  <p data-bind="state:Counter.count">0</p>
  <button data-action="click->Counter.decrement">
    -1
  </button>
  <button data-action="click->Counter.increment">
    +1
  </button>
</div>
  1. Write a JavaScript class component ๐Ÿ”Œ
import { Component } from "domponent";

export default class Counter extends Component {
  constructor(el) {
    super(el);
  }

  increment() {
    this.setState({ count: this.state.count + 1 });
  }

  decrement() {
    this.setState({ count: this.state.count - 1 });
  }
}
  1. Initialize the App โšก
import { Init } from "domponent";
import Counter from "./Counter.js";

const config = {
  selector: document.getElementById("root"),
  components: {
    Counter
  },
  appCreated: callbackFunction
};

new Init(config);

And you're good to go!!

Checkout a practical example now:

Docs ๐Ÿ“–

Purpose โœ”๏ธ

What does this do?

This library sets up a clean and modern way to turn prerendered HTML into UI components. You can easily implement some data-binding, handle scope, pass data around, and create components by using some of the conventions in this script. It's meant to be a very very lightweight alternative to StimulusJS with a bit of a React flavor (lifecycle methods, props and component state).

What does this library not do?

DOMponent does not handle client-side rendering out of the box, does not create virtual DOM, does not diff DOM (though it does diff state and props). It's not meant to handle routing or entire application state. It's meant to take HTML fragments (Thymeleaf, Rails, Pug, whatever template engine you use) and create reusable functionality in the form of Components.

Differences to KnockoutJS

DOMponent is similar to Knockout in some ways:

  • it is template language-agnostic
  • the syntax looks similar
  • ... that's kinda about it.

Unlike KnockoutJS, DOMponent:

  • is component-driven
  • has option for namespaced components
  • can isolate scope
  • is only 6kb (that's a 50kb savings)
  • is highly declarative
  • allows for highly specific DOM references
  • has lifecycle methods
  • performs insanely fast

Knockout

HTML

<p>First name: <input data-bind="value: firstName" /></p>
<p>Last name: <input data-bind="value: lastName" /></p>
<h2>Hello, <span data-bind="text: fullName"> </span>!</h2>

JS

var ViewModel = function(first, last) {
  this.firstName = ko.observable(first);
  this.lastName = ko.observable(last);

  this.fullName = ko.pureComputed(function() {
    return `${this.firstName()} ${this.lastName()}`;
  }, this);
};

ko.applyBindings(new ViewModel("Planet", "Earth"));

DOMponent

HTML

<div data-component="Hello" data-state="{"firstName": "Planet", "lastName": "Earth"}">
  <p>First name: <input data-action="input->Hello.setFirstName" /></p>
  <p>Last name: <input data-action="input->Hello.setLastName"/></p>
  <h2>Hello, <span data-bind="state:Hello.fullName"> </span>!</h2>
</div>

JS

import { Component } from "domponent";

export default class Hello extends Component {
  constructor(conf) {
    super(conf);
  }

  setFirstName(event) {
    this.setState({ firstName: event.target.value }, () => {
      this.setFullName();
    });
  }
  setLastName(event) {
    this.setState({ lastName: event.target.value }, () => {
      this.setFullName();
    });
  }

  setFullName() {
    this.setState({
      fullName: `${this.state.firstName} ${this.state.lastName}`
    });
  }
}

Demo ๐Ÿค–

https://tamb.github.io/domponent/

Todo List: https://codesandbox.io/embed/domponent-todo-with-undo-redo-sp3s2?fontsize=14

Local Demo ๐Ÿ˜‰

  1. git clone this repo
  2. npm install
  3. npm run build:html-dev or npm run build:html-prod

Install ๐Ÿ“ฅ

npm

npm install --save domponent

You can use an ES5 version by importing this file domponent/dist/domponent.es5.production.min.js

If you're not using a transpiler, it's recommended to use the ES5 UMD. So here's the JSDelvr link:

// production
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/domponent@VERSION/dist/domponent.es5.production.min.js" defer></script>

// development
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/domponent@VERSION/dist/domponent.es5.development.min.js" defer></script>

data API ๐Ÿ’ฝ

Note: Use as much or as little of this library as you want. You can use this for just data-component, data-ref and data-ref-array attributes and make your DOM selection a lot easier. You can make stateless components with the Exponent class. The sky's the limit. At its core, Domponent is a set of utility classes for your HTML.

data-component

We use this bad boy to match the component name to its corresponding class in the Init configuration object

example: if your HTML is data-component="Counter" | you must have a component in your config called Counter

data-bind

Binds state or props to the textContent of an element First you specify if you want to bind state or props data-bind="state:Counter.count" or data-bind="props:Counter.count" The left half of the : tells the component what object to bind to (state or props), the right half tells the component what key within the state or props to read from

data-action

Binds a DOM event with a component method. Consider the following:

<button data-action="click->Counter.increment">
  +1
</button>

The left half of the : represents the literal string for the DOM event to listen for. The right half corresponds to the component method

Note: You can add multiple listeners with a pipe | example:

<button data-action="click->Counter.increment|mouseover->Counter.anotherMethod">
  +1
</button>

You can pass eventListener options in as well. Options must be after a . after the class method. The options must be separated by a comma ,.

<button
  data-action="click->Counter.increment.passive,capture|mouseover->Counter.anotherMethod.once,passive"
>
  +1
</button>

data-state

If you want to instantiate your component with a particular state in memory you must attach a data-state attribute to the root element of the component example:

<div data-component="Counter" data-state='{"count":24, "isEven": true}'>
  ...
</div>

That's right. data-state takes any valid JSON object.

data-ref

If you need to reference DOM elements, you can use data-ref like so:

<div data-ref="Counter.myElement"></div>

You need to preface which component the element is on. It is then stored in the components $refs object.

You can then access the element in Counter using this.$refs.myElement within the Component instance.

data-ref-array

You can create an array of elements in your component this way:

<div data-ref-array="Counter.elements"></div>
<div data-ref-array="Counter.elements"></div>

It is then stored in the components $refs object. You can access the array of elements in your component with this.$refs.elements.

data-key

This is totally optional. It's a unique string for each component instance.
This is used internally to bind props. Therefore you must know the $key of the component you are receiving props from.

<div data-component="Counter" data-key="aUniqueKey">
  ...
</div>

Let's say you're looping over this in your templating language. You should ensure your keys are unique.

# for (let i=0; i<10; i++){
<div data-component="Counter" key="`aUniqueKey${i}`">...</div>
}

If you don't use this attribute, a unique key will be assigned to each component instance automatically. It can be accessed via this.$key

data-props

You can share state from a parent component as props in a child component. The markup would look like this

<div data-component="Counter" key="parentCounter">
  <div
    data-props="myAwesomeProp<-parentCounter:ofFive"
    data-component="DisplayAnything"
  ></div>
</div>

The left side of the arrow <- is the name of the prop in the DisplayAnything component. The Right side of the arrow is $key of the parent component, a colon : and the name of the piece of state to inherit.

You can then use the lifecycle methods propsWillUpdate and propsDidUpdate to make changes within your child component.


Extending the Component class ๐Ÿ“

Let's continue with Counter. The minimum js needed to create a component is below:

class Counter extends Component {
  constructor(conf) {
    super(conf);
  }
}

super adds the base methods and properties your component needs.


Managing Component State ๐Ÿ•น๏ธ

Don't mutate the state directly. Call this.setState

setState(stateObject, callbackFunction);

This is similar in concept to React's setState - although it's implemented differently.

You can add default states to your JS component and override them in the DOM

export default class Counter extends Component {
  constructor(conf) {
    super(conf);
    this.state = {
      count: parseInt(this.state.count) || 0,
      isEven: this.state.count
        ? this.state.count % 2 === 0
          ? true
          : false
        : true,
      stateFieldFromDOM: this.state.stateFieldFromDOM || "default cat",
      stateFieldDefault: "default iPhone 11"
    };
    this.setState(this.state);
  }
<div data-component="Counter" data-state="{"count": 4, "isEven":true, "stateFieldFromDOM": "some value here"}"

The above state fields will override the default JS state fields.

Rendering HTML from props and state

The value binding from setState will always be to textContent. If you wish to use state/props to render HTML, you can add a watcher for that value and update the $refs node that will house the new HTML.

watch(){
   return {
     count: {
      post(newCount){
        this.$refs.exclaimCount.innerHTML = `<div class="uppercase">${newcount}!</div>`;
      }
     }
   }
}

LifeCycle Methods ๐ŸŒณ

The following are methods you can use to access components at various points in their lifecycle

Lifecycle Method Context Description
connecting Component/Exponent Before the library wires up any of your Component/Exponent and you have access to other methods
connected Component/Exponent After your Component/Exponent is wired up and all eventListeners are in place
disconnecting Component/Exponent Before removing eventListeners and deleting Component/Exponent from memory
propsWillUpdate Component/Exponent Before the props are updated within your component, no DOM mutations have happened
propsDidUpdate Component/Exponent After the props have updated and the DOM has changed
stateWillUpdate Component Before the state of the current component or any of its dependents' props have changed
stateDidUpdate Component Child components with inherited props have done their DOM manipulations and state and props have changed

Watchers ๐Ÿ‘€

Component and Exponent classes have a watch method that must return an object. Watchers allow you to hook into specific state or props value changes during the component lifecyle. This allows your state logic to be isolated instead of clumping it all in with stateWillUpdate, stateDidUpdate, propsWillUpdate or propsDidUpdate. This is meant to closely mimic watchers in Vue.JS. Note: Do NOT name your state and props fields the same. This is bad practice and will break the watchers.

watch(){
  return {
    myField: {
      pre(newValue, oldValue){
        // my logic
      },
      post(newValue){
        // my logic
      }
    }
  }
}

You can view your watched state fields in the components $watchers object.


Stateless Components ๐Ÿ˜

Extend the Exponent class to create a component with only props This is slightly lighterweight than a Component. Quicker to wire up and takes up less memory.

import { Exponent } from 'domponent'

class StatelessThing extends Exponent{
  constructor(conf){
    super(conf);
  }
}

You will then only have access to:

  • propsWillUpdate
  • propsDidUpdate

Why Exponent??

Because it simply interprets or expounds the data that it is given... and it sounds like Component.


Component Fields ๐ŸŒต

Components or Exponents will be given the following fields.

Field Name Type Access Context Description
$app object public Component/Exponent The entire Domponent application
$b array private Component/Exponent eventListener bindings for internal use
$d object private Component The parent components references to its children
$key string public Component/Exponent Unique identifier for the component instance
$name string public Component/Exponent The name of the component type
$p object private Component/Exponent Internal collection of props and its DOM references
props object public Component/Exponent Key/Value pairs of data passed
$root element public Component/Exponent The root DOM Node of the component
$s object private Component Internal collection of state and its DOM references
state object public Component Key/Value pairs of data which can be updated
$watchers object public Component stored change functions and their respective state and prop key

Init function ๐Ÿ‡

This function creates the app and registers all the components. This takes a config object as required argument:

const config = {
  selector: document.getElementById("root"),
  components: { Counter },
  appCreated: callbackFunction
};

const App = new Init(config);

It then exposes the following methods:

  • createComponent
  • deleteComponent
  • register
  • unregister

And the following objects:

  • component - all base classes for components in the app
  • createdComponents - all instances of app components

You can also exclude the components object of the configuration and create an app without any components to begin with.


Adding and removing components ๐Ÿคผ

Adding components

createComponent

@params:

  • {Element} a DOM element to create the component instance
  • {Function} optional callback function
App.createComponent(document.getElementById("added-html"), callback);
register

@params

  • {Component} a component definition
  • {Function} optional callback function
App.register(NewComponent, callback);

Deleting components

deleteComponent

@params:

  • {String} - key of the component instance you want to delete, can be assigned via data-key or accessed inside component via this.$key
  • {Function} optional callback function
App.deleteComponent("my-component-instance-key", callback);
unregister

@params:

  • {String} - The name of the key you used to register your component on app Init.
  • {Function} optional callback function
App.unregister("NewComponent", callback);

Namespacing data attributes ๐Ÿ“‡

To avoid data- attributes clashing with other selectors, libraries, etc. you can override the default attribute names in the app config object:

Init({
  selector: getElementById('root),
  components: { Counter },
  dataAttributes: {
    component: 'mynamespace-component',
    state: 'cool-state',
  }
});

This means that your HTML will look like this:

<div data-mynamespace-component="Counter" data-cool-state='{"count":12}'>
  ...
</div>

Custom Syntax ๐Ÿ”ง

You can optionally customize the syntax you use in your HTML. The following items can be customized.

INHERITS_FROM: '<-',
FROM_COMPONENT: '.',
KEY_VALUE: ':',
MULTIPLE_VALUES: "|",
METHOD_CALL: "->",
LIST: ","

This means that in your config you can add:

{
   customSyntax: {
      LIST: "!",
      METHOD_CALL: "#"
  }
}

And your HTML can use this!


Development Mode ๐Ÿค“

When developing with Domponent, using the development build adds helpful errors and logs to your console from Development Dom (this guy->) ๐Ÿค“

The easiest way to use this is with Webpack Aliases:

resolve: argv.mode === 'development'? {
      alias: {
        domponent: 'domponent/dist/domponent.development.js'
      }
    }: {},

This way your development build of webpack will swap out the production version of Domponent for the version sprinkled with help from Dom.


Syntax Examples ๐Ÿ”ค

You can write your component HTML for various templating engines and include them as partials/fragments/whatever your engine refers to as "chunks of HTML".

Here are some examples of how you might use Domponent.

Note: Despite these syntax differences in the markup, remember that the component is simply a JS class โœŒ๏ธ

Pug Syntax Example ๐Ÿถ

// counter.pug
mixin counter(count)
 div(data-component="Counter" data-state=`
    {
      "count": count,
      "isEven": count % 2 === 0
    }
  `)
   p(data-bind="state:Counter.count") #{count}
   button(data-action="click->Counter.increment") +1
   button(data-action="click->Counter.decrement") -1

// usage
+counter(101119)
+counter(61316)

Thymeleaf Syntax Example ๐Ÿƒ

// counter.html
<div
  data-component="Counter"
  th:fragment="Counter"
  th:data-state='|{"count":${count}, "isEven": ${count % 2 == 0}}|'
>
  <p data-bind="state:Counter.count" th:text="${count}"></p>
  <button data-action="click->Counter.increment">
    +1
  </button>
  <button data-action="click->Counter.decrement">
    -1
  </button>
</div>

// usage
<th:block th:replace="./counter.html  :: Counter(count: 1289)" />
<th:block th:replace="./counter.html  :: Counter(count: 491)" />

Razor Syntax Example โš”๏ธ coming soon...

Ruby on Rails Syntax Example ๐Ÿ’Ž coming soon...

Mustache Syntax Example ๐Ÿ‘บ coming soon...


Component Lifecycle ๐Ÿ•ต๏ธโ€โ™‚๏ธ

updating component

Contact ๐Ÿค™

  • Email: domponent [at] gmail [dot] com (Please use the subject Domponent Support or we will not respond)
  • Twitter: @domponent

domponent's People

Contributors

barteksc avatar dependabot[bot] avatar tamb 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

Watchers

 avatar  avatar

domponent's Issues

Consider Client Rendered Components

Was considering client-side components.

class MyCounter extends Renderer

and it allows you to pass a custom render method which must return a string of html.
Then it fires the wiring up automatically.
So your actual DOM can look like this:

<my-counter data-props="..." data-key="..." data-state="..."/>

One concern is with nested custom elements:

<my-card>
  <my-counter/>
</my-card>

Ideally this could work. And you could pass attributes and state just like regular DOM-based components.

Ideas:

  • I could use the library common-tags and use the safeHTML tag. This way I can force the render method to be a tagged template literal. This would safely allow HMTL to be created. And you could create templates.
  • I could allow for a template literal to be returned from the render method and behind the scenes hydrate the template before appending it to the DOM using this.state.field or this.field, then I would wire up the component as a regular Component instance.
render(){
   const state = {
       name: "Mario"
   };
   return (
     `<div data-component="SayHello" 
             data-state='${JSON.stringify(state)}'>
          Hello, {this.name}!
     </div>`
   );
}

At this point should the user consider Polymer? React?

integration tests

Need to test that state and props and dependents wire up correctly.
Would prefer to stick with Jest.

create dynamic DOM refs

Support data-ref="fieldName" in html.

Then access it in the Component via this.fieldName.

This will simplify selecting. I guess it's nice to have.

Allow `$root` to accept `data-action`

Currently this.$root elements cannot accept a data-action.
Components must be wrapped in a div or another element and component $root needs to be the wrapper div in order to attach event listeners.
Rewrite helper functions to check $root node for additional data- attributes.

Hooks

Trying to figure out a sane way to incorporate Hooks into the project.

Hooks should:

  • be reusable
  • be prototypical
  • have access to same DOM nodes?
  • have access to same props?
  • have access to same state?

Should this be baked into the library or treated as something to be sprinkled into components?

class Counter extends Component{
  constructor(props){
    super(props)
  }
  ...myHookObject
}

This should simply be doable without code changes and would require documentation changes.

Create `ref-array` for arrays of items

Create ref-array to call document.querySelectorAll() and return as an array.
Converts nodelist to array.

Renaming the data attribute should be added as well.

Rerender Performance Test

Currently working on a Pug + Domponent test to rerender 124 divs individually at 10ms intervals:

Should finished tests against React, Inferno, Preact

Post results on README.md

Tests are in this repo https://github.com/tamb/pug-domponent

Operations per second. JS execution time.
Page load time.

Custom Syntax

Add an option to have custom syntax in addition the option for custom data attributes.

Example:

{
   actions: '=>',
   stateAndProps: '.',
   inheritProps: '<=',
}

Then the syntax is customizable

data-bind="state.Counter.count"
data-action="click=>Counter.increment"

create mixins method

Problem
How to share variables or reusable methods between components.

Solution:
Mixins

Basic idea is to add a this.$mixins to Scope or Exponent class, exposing it to everything. The mixin property will store references to different mixin objects and change the execution context of that object when one of the methods are called upon.

Possible issues are with this within a mixin method.

Possible solutions:

Possibly a $mixinCall method to wrap every property and method reference in? (Talk about performance hit there)

Creating this[mixinKey] within the class. It would equal either a value or the mixin method with bind. Mixins are shared. Mixin methods take new execution context. Mixin properties are copied by value.

data-attr-bind

Thinking in v3 of adding data-attr-bind

To be used like this:

<p data-attr-bind=attributeName:Component.field|attributeName2:Component.field>
<p>

Any HTMLElement attribute that exists can be set this way.
class attribute will have to function by removing first value and then adding new value.
This will only support int, string and DOMTokenList values

Code Examples in Demo Site

Add a component to toggle the display of HTML and JS for that component. Code previews should have syntax highlighting.

Should there be an API to update refs, ref-arrays, actions?

Exponent could be given API to update, add, or remove data-ref.
Exponent could be given API to update, add, or remove data-ref-array.
Exponent could be given API to update, add, or remove data-action.

This would increase the size of the framework.

helper methods for creating refs, etc could be added to actual Exponent class and could accept query string parameter for updating only specific refs, etc.

This could apply to adding/removing event handlers (data-action) methods to nodes.

V3 goals

  • TypeScript support
  • decorators? (functional components)

create IIFE for CDN

Need to add something similar

"main": 
"dist/preact.js",
  "module": "dist/preact.module.js",
  "umd:main": "dist/preact.umd.js",

Need to transpile CDN code to es5

Merge state from DOM with default Component state

DOM state should overwrite component state. But the component should have a fallback state to whatever is specified in the constructor

data-state="{"count": 5}"

overwrites

this.state = {
  count: 4
};

data-state="{"count": 5}"

and

this.state = {
  count: 4,
  name: 'Joe'
};

Should retain default of Joe and have count of 5.

This could be written off as "it's a feature not a flaw", but the decision needs to be made DOM > JS. The DOM passes the props and state.

Demo ES5 Global Object version

Docs need example of es5 version

var App = new Domponent.Init({
  selector: document.body,
  components: {}
});

Then adding, removing, registering, unregistering components.

add render method??

Proposal:

Exponent and Component should contain or allow composition for an optional render method that outputs HTML. The HTML should be template agnostic (handlebars, hyperstache, mustache, html, etc).

The benefit would be that the developer could leverage client-side templating and still have a small library available to bind data changes and events.

// handlebars example

render(props, state){

     // compile the template
     const template = Handlebars.compile(`
        <div data-component="MyComponent" name<-parentComponent: name>
          <p data-bind="state:MyComponent.count"> {{state.count}} </p>
          <p data-bind="props:MyComponent.count"> {{props.name}} </p>
          <p>  {{additionalData}} </p>
          <button type="button" data-action="click->MyComponent.increment">
            +1
          </button>
        </div>
     `);

     return template({
        props, 
        state, 
        additionalData: 'Howdy', 
        somethingElse: 'aloha' 
     });
}

This would be fired before the component is wired up:

  1. Fire render method if it exists
  2. Follow through with the rest of the constructor

** Must be compatible with mustache and handlebars! **
handlebars: https://handlebarsjs.com/guide/#installation
mustache: http://mustache.github.io/mustache.5.html

things to consider:

  • library weight (maybe this should be { render } imported separately.
  • How to handle loops?
    • Are we going to expect that loops will be within components (lists??). Need a working example
  • registering custom elements in App config?
    • tags: [ 'my-component', 'another-thing']
  • this will require a change in the order of the constructor functions.
  • maybe I set render to null and allow it to be overwritten by a utility? I can use call to apply the class instance
  • maybe the library will not get much bigger. But it will require and additional check at runtime. render will be it's own function.
  • This would require further lifecycle hooks for didMount willMount willUnmount and didUnmount

using custom tags:

<!-- potential usage-->
<my-component data-state='{name: "Michael Scott"}'></my-component>


<!-- JSP example -->
<c:forEach items="${dunderMifflinEmployees}" var="employee">
    <my-component data-state='{name: ${employee.name}}'></my-component>
</c:forEach>

This would then have to match up with a MyComponent class. The class could then contain a render method.

arguments against this proposal:

The purpose of this library is to leverage template-languages already in use. It's meant to add a declarative syntax to your HTML in order to bind data changes and events. Implementing client-side rendering would slow down the library and add to size. Also, we can easily call the templates before we initiate the components or the application.

Adding support for client-side rendering will possibly only add to the complexity of the library without much gain. If templating is already happening on the server, is there a need for this?

Feedback

I am working on a similar thing called dom99 The docs are a bit out of date, but check the examples.

After skimming through your project:

data-state

You basically evented yet another data format, consider instead using something native of the platform like

data-state="{count:24, isEven:true}"

or

data-state-count="24" data-state-isEven="true"

data-key

it is optional, must be unique , but what does it do ?

Component Fields

why prefix things with $ ?

do not commit domponent-1.0.0-alpha.18.tgz

Otherwise very interesting project, I give more detailed feedback later

scopeElements utils memory leak

Scoping works with nested components of same type.
Refs and ref arrays get picked up from other components.
This becomes an issue with larger applications. But there is an initialization hit in performance for resolving this.

Possible solutions:

  • new query selector which accounts for the prefixed component name
  • enhanced filtering in scoped method
  • possible config option to include this, perhaps tradeoffs are not worth it? Requires some analysis.

Create Components WITHOUT Init method

Ideally version 2 should have much simpler constructors for Component and Exponent classes.
They should accept the root node only.

Use of $app should be entirely optional.

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.