BorisMoore / jsviews

Interactive data-driven views, MVVM and MVP, built on top of JsRender templates
http://www.jsviews.com/#jsviews
MIT License
856 stars 130 forks source link

Dynamic data-link? #385

Closed veteze closed 7 years ago

veteze commented 7 years ago

Hi Boris. I have been using jsviews quite extensively since about 2012. I love it! I'm working on a new data management piece where each text attribute is actually ten possible values. I'll try to demonstrate. I won't get into the specifics into why the data is organized this way, i know it looks strange, but it is necessary in this case.

We have:

{title:
    {original: "The Original Title"},
    {yyz: "Toronto Title"},
    {yvr: "Vancouver Title"},
    {yyc: ""},
    {yow: null},
}

Now we set a variable to indicate which title set we're working in and we pass it in to the view as a helper var.

<input type="text" data-link="title[~helperVarWhichTitle]">

And this works when it first renders. It displays the correct attribute. And when we change the val that ~helperVarWhichTitle and refresh the view then it displays the correct attribute and corresponding value.

But there seems to be an issue with saving the value. When we edit the value the title object loses the sub attributes and simple becomes:

{title:"Whatever I changed the title text too"}

What do you think? Is this even possible?

BorisMoore commented 7 years ago

Your data doesn't quite makes sense (not valid JSON), but I think I follow what you intended.

There's a few ways you can do that. Here is a jsfiddle showing different approaches:

https://jsfiddle.net/BorisMoore/85q22ny3/

Let me know if that makes sense to you, and if you have additional questions...

BorisMoore commented 7 years ago

Here is the code:

<script id="myTmpl" type="text/x-jsrender">
  Version: <input data-link="version" /><br/><br/>

  Simple approach if Version does not change dynamically:<br/>
  <input data-link="title.{{:version}}" /><br/><br/>

  Specific computed property (on data):<br/>
  <input data-link="ttl()" /><br/><br/>

  Specific computed property (as helper):<br/>
  <input data-link="~ttl()" /><br/><br/>

  Generic tag:<br/>
  {^{textbox title version/}}<br/><br/>
</script>

<div id="page"></div>
$.views.tags({
  textbox: { // {{textbox}} tag which optionally takes second version argument (for fields with multiple versions)
    template: "<input/>",
    linkedElement: "input",
    convert: function(field, version) {
      if (version === undefined) {
        return field;
      } else {
        return field[version] || "";
      }
    },
    convertBack: function(val) {
      var args = this.tagCtx.args;
      if (args[1] !== undefined) {
        $.observable(args[0]).setProperty(args[1], val);
      } else {
        return val;
      }
    },
    depends: function() {
      var args = this.tagCtx.args;
      return [args[0], args[1]];
    },
    onUpdate: false
  }
});

function ttl() {
  var data = this;
  return data.title[data.version];
}

ttl.set = function(val) {
  var data = this;
  $.observable(data.title).setProperty(data.version, val);
};

ttl.depends = "**";

function ttlHelper() {
  var data = this.data;
  return data.title[data.version];
}

ttlHelper.set = function(val) {
  var data = this.data;
  $.observable(data.title).setProperty(data.version, val);
};

ttlHelper.depends = "**";

var myTmpl = $.templates("#myTmpl"),
  data = {
    ttl: ttl,
    title: {
      original: "The Original Title",
      yyz: "Toronto Title",
      yvr: "Vancouver Title",
      yyc: "",
      yow: null
    },
    version: "yyz"
  },
  helpers = {
    ttl: ttlHelper
  };

myTmpl.link("#page", data, helpers);
veteze commented 7 years ago

Wow. this is exceptional. Thanks!

veteze commented 7 years ago

what does 'depends = "**"' do in those cases?

BorisMoore commented 7 years ago

It's explained here: http://www.jsviews.com/#observe@all

See also other references or samples using it: http://www.jsviews.com/#search?s=**

veteze commented 7 years ago

okay. the observable wildcards i'm familiar with. the depends stuff not so much. got homework to do. thanks again.

BorisMoore commented 7 years ago

Sure. You can take a look here: See http://www.jsviews.com/#computed@depends

veteze commented 7 years ago

In the case of these elements being part of a list, Is it possible to pass the {{:#index}} into the data-link or the helper? It doesn't seem to be.

as in: data-link="gatewayItemHeading({{:#index}})" or: data-link="~gatewayItemHeading({{:#index}})"

neither seems to send over the index. just get undefined.

BorisMoore commented 7 years ago

Depends what you mean by 'part of a list'. Can you make an example (code or jsfiddle) where #index seems to show as undefined?

veteze commented 7 years ago

here you go - https://jsfiddle.net/9d2bbLne/

BorisMoore commented 7 years ago

Here is a possible approach, with ttl() as a helper function. (Passed in as in the example, or registered as a helper if you prefer).

https://jsfiddle.net/BorisMoore/cj0tunq8/

veteze commented 7 years ago

thanks boris. i will look at this next week. appreciate it.

BorisMoore commented 7 years ago

I'll close this now. I think the ttl() as helper approach should cover your scenarios well...