Skip to content

Commit

Permalink
tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dperrymorrow committed Sep 18, 2020
1 parent 0cacf71 commit 113bb36
Show file tree
Hide file tree
Showing 39 changed files with 1,031 additions and 3,248 deletions.
2 changes: 1 addition & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
registry=https://registry.npmjs.org/
save-exact=true
package-lock=false
package-lock=true
167 changes: 120 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@

# ReBars

> A simple, modern approach to Obvervables and DOM re-rendering and patching.
## [Documentation](https://dperrymorrow.github.io/re-bars) | [Examples](https://dperrymorrow.github.io/re-bars#example-simple)
## [Documentation](https://dperrymorrow.github.io/re-bars) | [Examples](https://dperrymorrow.github.io/re-bars/examples/advanced)

- [ReBars Introduction](#rebars)
- [A ReBars Application](#a-rebars-application)
Expand All @@ -15,9 +17,10 @@
- [Partials](#partials)
- [ReBars Helpers](#rebars-built-in-helpers)
- [watch (re-rendering)](#the-watch-helper)
- [concat (string building)](#the-concat-helper)
- [on (event handling)](#the-on-helper)
- [ref (element reference)](#the-ref-helper)
- [bind (data binding)](#the-bind-helper)
- [ref ($el reference)](#the-ref-helper)
- [key (pointer reference)](#the-key-helper)

---

Expand All @@ -43,12 +46,14 @@ ReBars started with the idea of so what do I _actually_ need from a Javascript f
ReBars re-renders tiny pieces of your application on change. You are in control of what re-renders and when. There is no...

- ❌ Virtual DOM
- ❌ JSX or anything else to pre-compile
- ❌ JSX or others that need pre-built to JS
- ❌ DOM diffing and patching
- ❌ Single File Components
- ❌ CSS pre-processing and extracting

**Your** code simply runs on **your** app.

> In fact there is zero DOM diffing / checking of any kind in ReBars. Marked elements are simply re-rendered when correlating data changes.
> In fact the only time ReBars will compare any DOM is when an Array is being patched. All other times ReBars simply calls the Handlebars method again, and replaces the HTML.
ReBars keeps your DOM in sync with your data using [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy), and gets out of your way. You can get back to just writing Javascript.

Expand Down Expand Up @@ -469,15 +474,19 @@ ReBars consists of a few very powerful Handlebars helpers. Of course you can add

The watch helper tells ReBars to re-render this block on change of the item you pass in as the second parameter.


Watch allows you to re-render a block of your template on change.
Watch takes an _optional_ arguments of what properties to watch. The arguments can be string or a regular expression. You may also as many as you like. When any change, the block will re-render.

In our explanation below, we will be referring to this data set.
```handlebars
{{#watch}}
My name is {{ name.first }} {{ name.last }}.
{{/watch}}
```

```javascript
{
data: {
open: false,
hobby: "running",
name: {
first: "David",
Expand All @@ -490,21 +499,30 @@ In our explanation below, we will be referring to this data set.
}
}
```
```html
{{#watch}}
My name is {{ name.first }} {{ name.last }}.
{{/watch}}
```

The above omits the what to watch. In this situation, ReBars will pre-render the block, and captures any references used. It would evaluate to the same as.



```html
```handlebars
{{#watch "name.first" "name.last" }}
```

> If you are unsure what to watch, ReBars traces out changes to the console when you pass `trace: true` to your application.
### Automatic Watch pitfalls

Sometimes automatically inferring what to watch will not have the desired effect.

```handlebars
{{#watch}}
My name is: {{ name.first }} {{ name.last }}
{{#if open }}
{{ hobby }}
{{/if}}
{{/watch}}
```

In the example above, only `name.first` `name.last` will be watched. This is because open was false and hobby was not referenced. When in doubt, be specific.

> If you are unsure what to watch, ReBars traces out changes to the console when you pass `trace: true` to your application. It's best to be explicit when telling ReBars what to watch.
| Argument Example | re-renders when |
| - | - |
Expand All @@ -531,46 +549,16 @@ export default {
{{#watch "name" tag="h3"}}
{{ name }}
{{/watch}}
<input type="text" value="{{ name }}" {{ on input="saveName" }}>
<input type="text" value="{{ name }}" {{ bind input="name" }}>
`,
data: {
name: "David",
},
methods: {
saveName({ event }) {
this.name = event.target.value;
},
},
};

```


## The Concat Helper

Sometimes you need to piece together something that is a combination of a dynamic value, and a static. Thats where this simple little helper comes in handy.

In this example we are looking to not re-render the entire Array on change of any of it's items. So we use the concat helper as a [sub expression](https://handlebarsjs.com/guide/expressions.html#subexpressions)

> Notice the `()` around the sub expression. You will get a syntax error without them!
```handlebars
{{#watch "todos.length" tag="ul"}}
{{#each todos as | todo | }}
{{#watch (concat "todos." @index "(.*)") tag="li" }}
{{ todo.name }}
{{/watch}}
{{/each}}
{{/watch}}
```

The above results in the equivalent of

```handlebars
{{#watch "todos.1(.*)" }}
```

## The on helper

This allows you to bind your component's methods to events in your template. The method will be called with the first param an Object as described [above](#methods) and any additional params that are passed to the helper.
Expand All @@ -592,12 +580,54 @@ methods: {

> Remember Handlebars requires params to be first, and then `key="val"` arguments second
You can also call multiple events on one invocation of the on helpers. For example.
You can also call multiple events on one use of the on helper. For example.

```html
<input {{ on focus="focused" blur="blurred" input="inputChange" >
```

## The Bind Helper

The bind helper is very simimar to the [on helper](#the-on-helper) with one exception. It saves you from having to write a method in your app when all you want to do is set a value.

For example:

```handlebars
<input type="text" {{ bind input="name.last" }} />
```

```javascript
data: {
name: {
first: "David",
last: "Morrow"
}
}
```

As opposed to:

```handlebars
<input type="text" {{ on input="updateLastName" }} />
```

```javascript
data: {
name: {
first: "David",
last: "Morrow"
}
},

methods: {
updateLastName({ event }) {
this.name.last = event.target.value;
}
}
```

On each input event of the text input, the last name will be updated to the input's current value. This is merely a convienance, and could be accomplished by defining a method. But is useful in many common cases.

## The ref helper

The ref helper gives you an alias to a DOM element in your template. The `$refs` method can be accessed in the context passed like other items in the context.
Expand All @@ -617,3 +647,46 @@ methods: {
}
```

## The Key Helper

This simple little helper marks individual items with a unique identifier you provide. The main use for this is when you have a `{{#watch}}` around an Array in your data.

```handlebars
{{#watch "friends" }}
<ul>
{{#each friends as |friend| }}
<li>{{ friend.name }}</li>
{{/each}}
</ul>
{{/watch}}
```

```javascript
{
data: {
friends: [
{ id: 1, name: "Fred" },
{ id: 2, name: "Mike" },
]
}
}
```

In the above example, on each change of any item in your todos, the entire UL block would re-render. This is not ideal, and ReBars is smart enough to determine which elements need changed.

Alternativly:

```handlebars
{{#watch "friends" }}
<ul>
{{#each friends as |friend| }}
<li {{ key friend.id }}>{{ friend.name }}</li>
{{/each}}
</ul>
{{/watch}}
```

Now when the Array friends is updated, ReBars will have a unique identifier to compare which items have changed and only update those items.

> Allthough it may work initially, using [@index](https://handlebarsjs.com/api-reference/data-variables.html#index) as your key value is not encouraged. Should you sort or reasign your Array, those indexes will no longer be a valid identifier for that item in the Array.
43 changes: 22 additions & 21 deletions dist/re-bars.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ var Dom = {
restoreState($target, activeRef) {
if (!activeRef) return;

const $input = this.findRef($target, activeRef.ref);
const $input = this.findAttr(attrs.ref, activeRef.ref, $target);
if (!$input) return;

$input.focus();
Expand Down Expand Up @@ -66,15 +66,11 @@ var Dom = {
// use this in place of all the others that are repeated eventually...
findAttr(attr, val, $target = null) {
const $container = $target || document;
if ($container.getAttribute(attr) === val) return $container;
// check top level
if ($target && $target.getAttribute(attr) === val) return $target;
return $container.querySelector(`[${attr}="${val}"]`);
},

findRef: ($target, ref) => {
if ($target.getAttribute(attrs.ref) === ref) return $target;
return $target.querySelector(`[${attrs.ref}="${ref}"]`);
},

findMethod: id => document.querySelector(`[${attrs.method}="${id}"]`),
findWatcher: id => document.querySelector(`[${attrs.watch}="${id}"]`),
isTextNode: $el => $el.nodeType === Node.TEXT_NODE,
Expand Down Expand Up @@ -232,17 +228,17 @@ var Helpers = {
instance.registerHelper("key", name => new instance.SafeString(`${attrs$1.key}="${name}"`));
instance.registerHelper("ref", name => new instance.SafeString(`${attrs$1.ref}="${name}"`));

instance.registerHelper("concat", function(...args) {
args.pop();
return args.join("");
});

instance.registerHelper("onlyIf", function(...args) {
args.pop();
const [condition, string] = args;
return new instance.SafeString(condition ? string : "");
});

instance.registerHelper("concat", function(...args) {
args.pop();
return new instance.SafeString(args.join(""));
});

instance.registerHelper("on", function(...args) {
const { hash } = args.pop();
const id = Utils.randomId();
Expand Down Expand Up @@ -273,6 +269,7 @@ var Helpers = {

instance.registerHelper("bind", function(...args) {
const { hash } = args.pop();
const [forceValue] = args;
const tplScope = this;
const id = Utils.randomId();

Expand All @@ -284,8 +281,11 @@ var Helpers = {

Object.entries(hash).forEach(([eventType, path]) => {
function handler(event) {
let value = event.target.value;
value = value === "" ? null : value;

try {
Utils.setPath(tplScope, path, event.target.value || null);
Utils.setPath(tplScope, path, forceValue || value);
} catch (err) {
instance.log(3, `ReBars: could not set path ${path}`, $el);
}
Expand Down Expand Up @@ -460,18 +460,20 @@ const ReBars = {
Handlebars = window ? window.Handlebars : null,
trace = false,
}) {
const instance = Handlebars.create();

const store = { renders: {}, handlers: {} };
if (!Handlebars) throw new Error("ReBars: needs Handlebars in order to run");

const instance = Handlebars.create();
const store = { renders: {}, handlers: {} };
Config.setTrace(trace);

return {
store,
instance,
async render(selector) {
const $app = document.querySelector(selector);
// takes an element or a selector
const $app = selector.nodeType === Node.ELEMENT_NODE ? selector : document.querySelector(selector);

if (!$app) throw new Error(`ReBars: document.querySelector("${selector}") could not be found on the document`);

// have to make sure they are resolved first
await Promise.all(Object.values(partials));
Expand All @@ -482,13 +484,10 @@ const ReBars = {
// must be compiled after the partials
const templateFn = instance.compile(template instanceof Promise ? await template : template);

if (!$app)
return instance.log(3, `ReBars: document.querySelector("${selector}") could not be found on the document`);

const scope = {
$app,
methods,
data,
data: typeof data === "function" ? data() : data,
};

Utils.registerHelpers({ instance, helpers, scope });
Expand All @@ -510,6 +509,8 @@ const ReBars = {
if (hooks.beforeRender) await hooks.beforeRender.call(scope.data, context);
$app.innerHTML = templateFn(scope.data);
if (hooks.afterRender) await hooks.afterRender.call(scope.data, context);

return context;
},
};
},
Expand Down
Loading

0 comments on commit 113bb36

Please sign in to comment.