Search code examples
jsviews

Convert back in a two way databound control


I'm trying to create a form that can let the user edit file properties.

I want to allow the user to change the basename of the file but the extension should remain readonly.

My linked object has a fileName property which contains the actual filename including the extension.

My template use converters to split the filename parts and to reconstruct the full file name after the user edited the field.

However, I did not found a way to pass arguments to my convert back converter. Specifically, I don't know how to pass the extension to the convert back method.

Here is a small repro:

(function($) {
  var getBaseName = function(value) {
    if (!value || (typeof value !== 'string')) return null;
    var lastDot = value.lastIndexOf('.');
    if (lastDot >= 0) {
      return value.substring(0, lastDot);
    }
    return value;
  };
  var getFileExtension = function(value) {
    if (!value || (typeof value !== 'string')) return null;
    var lastDot = value.lastIndexOf('.');
    if (lastDot >= 0) {
      return value.substring(lastDot);
    }
    return null;
  };
  var toFileName = function(value) {
    // how to get the extension here ?
    return value;
  };

  var dataVm = {
    fileName: "somefile.png"
  };
  $.views.converters({
    getBaseName: getBaseName,
    getFileExtension: getFileExtension,
    toFileName: toFileName
  });

  $.templates("#mainTemplate").link("#container", dataVm);
})(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsviews/0.9.90/jsviews.min.js"></script>


<script id="mainTemplate" type="text/x-jsrender">
  <p>Actual file name :
    <b data-link="fileName"></b></p>
  <br/>

  <input data-link="{getBaseName:fileName:toFileName}" />
  <span data-link="{getFileExtension:fileName}"></span>
</script>

<div id="container">

</div>

Especially, at this line, <input data-link="{getBaseName:fileName:toFileName}" /> how to provide arguments to toFileName ?

I tried some syntax from the jsRender pi, but I did not succeed.

As a workaround though, I was able to make it work if I persist the extension in the dataVm :

  var dataVm = {
    fileName: "somefile.png",
    extension: getFileExtension("somefile.png")
  };

And access the context in the converter:

  var toFileName = function(value) {        
    return value + this.ctx.root.extension;
  };

This is actually working, but it create a dependency between the converter and the data viewmodel. Moreover, this shouldn't be applicable in a grid. Ultimately, the converter should be part of a converter collection that is not aware of its usage.


Solution

  • The this pointer of the converters is the tag instance - see http://www.jsviews.com/#convertersapi.

    Even with data-link="{cvt:...:cvtBack}" you have an instance of the {: ...} tag. It enables you to get to useful properties via this.tagCtx, this.linkCtx etc. Here you can use this.tagCtx.args[0] to get the full filename string.

    Even simpler, you can store the fileExtension as a property in the tag instance:

    (function($) {
      var getBaseName = function(value) {
        if (!value || (typeof value !== 'string')) return null;
        var lastDot = value.lastIndexOf('.');
        if (lastDot >= 0) {
          // store fileExtension on tag instance
          this.fileExtension = value.substring(lastDot);
          return value.substring(0, lastDot);
        }
        return value;
      };
      var getFileExtension = function(value) {
        if (!value || (typeof value !== 'string')) return null;
        var lastDot = value.lastIndexOf('.');
        if (lastDot >= 0) {
          return value.substring(lastDot);
        }
        return null;
      };
      var toFileName = function(value) {
        return value + this.fileExtension;
      };
    
      var dataVm = {
        fileName: "somefile.png"
      };
      $.views.converters({
        getBaseName: getBaseName,
        getFileExtension: getFileExtension,
        toFileName: toFileName
      });
    
      $.templates("#mainTemplate").link("#container", dataVm);
    })(jQuery);
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jsviews/0.9.90/jsviews.min.js"></script>
    
    
    <script id="mainTemplate" type="text/x-jsrender">
      <p>Actual file name :
        <b data-link="fileName"></b></p>
      <br/>
    
      <input data-link="{getBaseName:fileName:toFileName}" />
      <span data-link="{getFileExtension:fileName}"></span>
    </script>
    
    <div id="container">
    
    </div>