GithubHelp home page GithubHelp logo

canjs / can-stache Goto Github PK

View Code? Open in Web Editor NEW
10.0 15.0 13.0 4.7 MB

Live binding handlebars templates

Home Page: https://canjs.com/doc/can-stache.html

License: MIT License

JavaScript 98.24% HTML 1.76%

can-stache's Introduction

can-stache's People

Contributors

andrejewski avatar bajix avatar bgando avatar bmomberger-bitovi avatar ccummings avatar chasenlehara avatar cherifgsoul avatar christopherjbaker avatar daffl avatar eben-roux avatar garrettreed avatar green3g avatar greenkeeper[bot] avatar greenkeeperio-bot avatar ilyavf avatar imaustink avatar janeori avatar justinbmeyer avatar leoj3n avatar m-mujica avatar macrofig avatar mattchewone avatar matthewp avatar michaelzcheng avatar nlundquist avatar phillipskevin avatar prashantsharmain avatar sinjhin avatar taai avatar thomaswilburn avatar

Stargazers

 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

can-stache's Issues

Test "no memory leaks with #each (#545)" fails randomly

Test "no memory leaks with #each (#545)" fails randomly.
https://github.com/canjs/can-stache/blob/master/test/stache-test.js#L2901

If you run tests again, it should pass the tests... I suspect it has something to do with setTimeout. Maybe the second setTimeout should be called from inside of the first setTimeout, because browsers doesn't guarantee the execution order of callbacks set with setTimeout? Proof:

Parsing a helper expression that uses the ~ operator throws

Test case in b91adc4

var exprData = expression.parse('each ~foo');

This throws with:

TypeError: Cannot read property '0' of undefined
    at Object.hydrateAst (http://localhost/can-stache/src/expression.js:659:47)
    at Object.hydrateAst (http://localhost/can-stache/src/expression.js:688:23)
    at Object.parse (http://localhost/can-stache/src/expression.js:647:19)
    at Object.<anonymous> (http://localhost/can-stache/test/expression-test.js:690:28)

Bracket expression with undefined value throws an error

If you have a template like:

<h1>{{place['place:name']}}</h1>

And place is undefined, an error options.get is not a function is thrown in helpers/core.js.

With non-bracket syntax this sort of thing is fine:

<h1>{{place.placeName}}</h1>

This is the same error I got when using colons in stache before switching to brackets ({{place.place:name}}.

Bracket syntax doesn't two-way bind

When passing a bracket expression as a two-way bound value like:

<my-clicker {(val)}="thing['other:thing']" />

Modifications to val in the my-clicker VM do not propagate to the original value.
If you change thing['other:thing'] to thing.otherThing the two-way binding works.

Bracket syntax doesn't work as a helper argument

If you use a bracket in a helper, the entire expression is turned into a bracket expression and does not work properly.

For example, using a helper like:
<p>{{myHelper ['name']}}</p>

Will create and AST of:

{
  "type":"Bracket",
  "root":{
    "type":"Lookup",
    "key":"myHelper"
  },
  "children":[
    {
      "type":"Literal",
      "value":"name"
    }
  ]
}

Which is the exact same AST that will be created if you had a template like:

<p>{{myHelper['name']}}</p>

The tokenizer strips out spaces so there's no way to differentiate between these two templates.

If that's the case, we should document this and suggest people use the call expression version of a helper if they are using brackets.

i.e <p>{{myHelper(['name'])}}</p>

Stache views which mutate tables may crash Internet Explorer 11

Internet Explorer 11 can not cope with DOM mutations to tables which do not have <tbody> elements and which are (directly or indirectly with subtree:true) being observed by a MutationObserver.

They cause a hard crash of the entire browser with a fault code similar to Unhandled exception at 0x05D424C1 (mshtml.dll) in iexplore.exe: 0xC0000005: Access violation reading location 0x00000008.

Stache should probably take care to ensure that <tbody> elements are always present, or should not use MutationObserver for IE 11. However, given that any other code operating on a page might still be registering mutation observers, the first (ensuring that <tbody> is always present) is probably a better idea.

Likewise, IE 11 has additional problems with crashes when using insertAdjacentHTML on a <tbody> element, which should probably be guarded against as well.

See e.g. meteor/meteor#6081 for Meteor, which ran into problems with this curiosity as well.

Alias (name) to `each` helper context - P(3) ~12

From: canjs/canjs#1735

{{#each readings}}
<some-component reading="{.}"/>
{{/each}}

Some times I want to wrap it with <another-component> and I have to change {.} to {../.} or even '{../../.}' for double wrap

{{#each readings}}
<another-component/>
  <and-another-component/>
   <some-component reading="{../../.}"/>
  </and-another-component/>
</another-component/>
{{/each}}

If one could have name for each heper context and refere to it by given name, it would be more convinient without need of backtrack paths.

{{#each readings "reading"}}
<another-component/>
  <and-another-component/>
    <some-component reading="{reading}"/>
  </and-another-component/>
</another-component/>
{{/each}}

emberjs does it using sintax something like like: {{#each readings as "reading"}}

Support converter helpers - P(2) ~8

Related:

can.stache.registerConverter("numberToString",{
  get: function(source){
    return source() + "";   
  },
  set: function(newVal, source){
    source(newVal === "" ? null : +newVal); 
  }
});
<input {($value)}="numberToString(~age)">

support 'section' level variables ~21

From: canjs/canjs#2200

We need a way to provide a local scope within a section. This will enable components within an each to pass data to each other:

{{#each items}}
  <date-picker {^*date}="*placeholder"/>
  <date-slider {^*date}="*placeholder"/>
  <date-saver {current}="./date" {suggested}="*placeholder"/>
{{/each}}

Get rid of @

No more converting things to computes, you always get the actual value, use ~ if you want a compute.

Converter naming convention

From private conversations, should converters have a special naming convention? For example:

<select {($value)}="string-to-any( ~index-to-selected(people, ~person) )">

These are converter helpers. They have both a getter which is used to set the element's value and a setter which is used to set the scope value in some way.

converter helpers are cool.

However, should they look differently from normal helpers (which are camelCase) to convey the fact that they are two-way rather than one-way like normal helpers? Does using kebab case aid in distinguishing them or hinder understanding?

context is not live-bindable - P(1) ~21

From:

. is not live-bindable so things like view bindings aren't updated correctly.

I think the solution is to make sure that most contexts are actually saved as a compute.

Bracket syntax inside attributes causes an error

A template like:

// Ex. 1
<p class="{{ foo[bar] }}"></p>
// Ex. 2
<p class="{{ foo['bar:baz'] }}"></p>

Will throw an error: Uncaught TypeError: attr.substr is not a function here

JSBin

I've tracked this down to helperAndValue which is attempting to find the key it should use to lookup the value in the scope.

In Ex. 1 the methodExpr is an instance of Lookup so the methodKey should be methodExpr.key.key

In Ex. 2 the methodExpr is an instance of Literal so the methodKey should be methodExpr.methodKey.key._value

Even with these changes in place, it doesn't look like the resolution of their values works inside attributes.

Allow stache partials to support custom context as a second parameter P(4) ~8

From: canjs/canjs#1331

Solution provided in the above issue:

nope. But to add it to stache, you need to get makeLiveBindingPartialRenderer (https://github.com/bitovi/canjs/blob/minor/view/stache/mustache_core.js#L441) called with expressionData similar to how makeLiveBindingBranchRenderer is (https://github.com/bitovi/canjs/blob/minor/view/stache/mustache_core.js#L531). I'm not sure why it isn't but I bet the fix is easy.

Then, you'll need (https://github.com/bitovi/canjs/blob/minor/view/stache/mustache_core.js#L456) to be passed something like scope.add(newContext).

To look up newContext, you can use

var exprData = core.expressionData(expression),

And b/c minor's exprData is much easier to use ... you should be able to something like:

exprData._args[0].value(scope, options)

Ping me on gitter if you want to help make this happen.

Support whitespace control

Provide an API to create a renderer from a template

<script type="text/stache" id="template">
  <span>{{name}}</span>
</script>

<script type="text/steal-module">
  var stache = require("can-stache");

  var render = stache.querySelector("#template");
</script>

Not sure on the name, here's some options:

  • .querySelector(selector): Matches the native API, but is long.
  • .select(selector): Shorter, but less clear maybe
  • .$(selector): jQuery-like, possibly confusing.
  • .from(selector)

Code for this function is essentially just:

function qs(selector) {
  var script = DOCUMENT().querySelector(selector);
  var txt = script.innerHTML;
  return stache(txt);
}

Add common converters

Related: #35

I think we need:

  • - <input type='checkbox' {($checked)}=inList(item, list)/>
  • - <select {($value)}="toString(~someValue)"> - converts null, undefined, 5, false and as much as possible to a string representation and vice versa.

Using bracket in a call expression doesn't work

If you use the bracket syntax in a call expression, the entire statement is turned into a bracket expression and does not work properly.

For example, using an expression like:
<p>{{myHelper(['name'], 'F')}}</p>

Will create and AST of:

{
  "type":"Bracket",
  "children":[
    {
      "type":"Literal",
      "value":"name"
    }
  ]
};

This should (probably) be a call expression with two children like this:

{
  "type":"Call",
  "method":{
    "type":"Lookup",
    "key":"@myHelper"
  },
  "children":[
    {
      "type":"Bracket",
      "children":[
        {
          "type":"Literal",
          "value":"name"
        }
      ]
    },
    {
      "type":"Literal",
      "value":"F"
    }
  ]
}

Support attribute callbacks that consume the subtemplate ~21

From:

This is partially an issue for: https://github.com/canjs/can-view-callbacks, but most of the implementation would need to exist in can-stache. I think the api should be something like:

var MyComp = Component.extend({
  tag: "my-comp"
});
viewCallbacks.attr("my-comp", function(el, attrData){
  return new MyComp(el, attrData);
}, true);

With the basics done, can-tag could be pretty easily implemented.

viewCallbacks.attr("my-comp", function(el, attrData){
  return new viewCallbacks.tag[attrData.attributeName](el, attrData);
}, true);

Stache - Inline Named Partials and Recursive Templates

Proposal for simple inline named partials which would enable simple, fast, recursive templates.

The syntax and a simple recursive example:

Template:

{{<partialName}}
  <li>
    {{.name}}
    <ul>
      {{#each .children}}
        {{>partialName .}}
      {{/each}}
    </ul>
  </li>
{{/partialName}}

<ul>
  {{>partialName initialScope}}
  <li>( {{message}} )</li>
</ul>

viewModel:

viewModel: {
  initialScope: {
    name: "Turtles",
    children: [
      {
        name: "Turtles",
        children: []
      },
      {
        name: "Turtles",
        children: []
      },
      {
        name: "Turtles",
        children: [
          {
            name: "Turtles",
            children: [
              {
                name: "Turtles",
                children: [
                  {
                    name: "Turtles",
                    children: []
                  }
                ]
              },
              {
                name: "Turtles",
                children: []
              },
              {
                name: "Turtles",
                children: []
              }
            ]
          },
          {
            name: "Turtles",
            children: []
          }
        ]
      }
    ]
  },
  message: "All the way down"
}

Output:

  • Turtles
    • Turtles
    • Turtles
    • Turtles
      • Turtles
        • Turtles
          • Turtles
        • Turtles
        • Turtles
      • Turtles
  • ( All the way down )

Named partials are also useful for logically separating complex templates - something .jsx does very well. Templates become much cleaner and easier to read when logically separated.

Useful for readability even without recursion:

{{<renderThumbnail}}
  <li class="{{#is . selectedImage}}selected{{/is}}"
    ($click)="updateSelectedImage( . )"
    ($mouseenter)="showPreview( . )"
    ($mouseleave)="hidePreview()"
  >
    <img {$src}=".thumbSrc" {$alt}=".title">
  </li>
{{/renderThumbnail}}

{{<renderIndicators}}
  <ul class="image-indicator-dots">
    {{#each .}}
      <li class="{{#is . selectedImage}}active-indicator{{/is}}"
        ($click)="updateSelectedImage( . )"
      ></li>
    {{/each}}
  </ul>
{{/renderIndicators}}

<div class="article-images">
  <ul class="vertical-thumbnail-selector">
    {{#allImages}} {{>renderThumbnail .}} {{/allImages}}
  </ul>
  <div class="main-image-wrapper">
    <img {$src}="selectedImage.fullSrc" {$alt}="selectedImage.title">
    <span class="main-image-title">{{selectedImage.title}}</span>
  </div>

  {{>renderIndicators allImages}}
</div>

That's maybe overkill for a small bit of markup but it demonstrates the idea for larger ones.

How do you feel about this?

stache compiled intermediate

using steal-tools to build a app with stache templates will produce an output like this

define('[email protected]#button-comp/[email protected]#steal-stache', [
    'module',
    'can-stache',
    'can-stache/src/mustache_core',
    '[email protected]#can-view-import',
    '[email protected]#can-stache-bindings'
], function (module, stache, mustacheCore) {
    var renderer = stache([
        {
            'tokenType': 'start',
            'args': [
                'span',
                false
            ]
        },
        {
            'tokenType': 'attrStart',
            'args': ['($click)']
        },
        {
            'tokenType': 'attrValue',
            'args': ['click()']
        },
        {
            'tokenType': 'attrEnd',
            'args': ['($click)']
        },
        {
            'tokenType': 'end',
            'args': [
                'span',
                false
            ]
        },
        {
            'tokenType': 'special',
            'args': ['name']
        },
        {
            'tokenType': 'close',
            'args': ['span']
        },
        {
            'tokenType': 'done',
            'args': []
        }
    ]);
    return function (scope, options, nodeList) {
        var moduleOptions = { module: module };
        if (!(options instanceof mustacheCore.Options)) {
            options = new mustacheCore.Options(options || {});
        }
        return renderer(scope, options.add(moduleOptions), nodeList);
    };
});

btw, this is a very short stache template!
<span ($click)="click()">{{name}}</span>

the issue
i see anytime tokenType and attrStart and attrValue and args
it's about minification of code. for example: if we use tT instead of tokenType we will reduce 6 byte. the minification tool in steal-tools cant minfiy that code!
if we have a big template, we can save a lot of traffic

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.