Accessing content in a custom field

Honestly folks, this is so frustrating!!!

screen1

This is my setup now after installing parcel.js and running npm run dev as Bastian suggests in his video above.

index.php

<?php

Kirby::plugin('mshoestrng/tableview', [
    'fields' => [
      'tableview' => [

      ]
    ]
]);

?>

src/index.js

// import TableView from "./components/tableView.vue";

panel.plugin('mshoestrng/tableview', {
  fields: {
    // tableview: TableView
    tableview: {
      template: "<p>hello wor</p>"
    }
  }
});

src/components/tableView.vue

<template>
  <h1>hello world</h1>
</template>

<script>

</script>

<style>

</style>

/site/blueprints/pages/warteliste.yml

...
tabellenansicht:
    fields:
      tableview:
        type: tableview
...

And all I get is this ridiculous error message:

Are you serious?? How is anyone supposed to do any proper dev work if the error message doesn’t even show the filename and line number where the error occurs??? @bastianallgeier @texnixe

When I uncomment the lines in index.js and remove the “old” tableview instead the same error persists.

When I move my index.js (as posted above) to the main directory of my plugin it works just fine.

So what’s the problem here?? How do I get the tableView.vue to work properly?

Avoid closing PHP tags at the end of files, it can cause unwanted output.

Removing ?> at the end of index.php will fix your issue and make everything work as intended.

No, I’m afraid, it’s still the same error :cry:

Huh, that was the only thing that went wrong when I tried to reproduce your issue.
These steps :

  1. Download the pluginkit panel zip
  2. Unzip in site/plugins
  3. Copy/paste your snippets (with import) in index.php, src/index.js and src/components/tableView.vue
  4. Run npm run build

Give me a h1 “hello world” in the panel…

I did exactly what you wrote above (I presume). Same error… Any hints would be greatly appreciated.

Also how I can debug this to find the cause for the error? As I have never worked with Vue.js before and also don’t want to get a lot into it just because I want to use the new version of Kirby any clues would be of great help!

P.S. During build I get the following warning: npm WARN pluginkit-4-panel No repository field. What does this mean?

That’s odd. Latest Kirby version? What’s the output in index.js?

The NPM warn shouldn’t worry you, the repository field is an incentive to mention where the code is if public (git repos, etc), if you don’t want to be bothered with this you can add private: true somewhere in your package.json

Where can I find a non-minified version of /panel/dist/js/app.js so I can actually drill down on what causes the error message to show?

Here is the output if two different index.js:

Following Sylvain’s instructions:

(function () {function b(a){return a&&a.__esModule?{d:a.default}:{d:a}}var a={};if(typeof a==="function"){a=a.options}Object.assign(a,function(){var render=function(){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c("h1",[_vm._v("hello world")])};var staticRenderFns=[];return{render:render,staticRenderFns:staticRenderFns,_compiled:true,_scopeId:null,functional:undefined}}());var c=b(a);panel.plugin("mshoestrng/tableview",{fields:{tableview:c.d}});})();

Following Bastian’s instructions (from his screen cast):

// modules are defined as an array
// [ module function, map of requires ]
//
// map of requires is short require name -> numeric require
//
// anything defined in a previous bundle is accessed via the
// orig method which is the require for previous bundles
parcelRequire = (function (modules, cache, entry, globalName) {
  // Save the require from previous bundle to this closure if any
  var previousRequire = typeof parcelRequire === 'function' && parcelRequire;
  var nodeRequire = typeof require === 'function' && require;

  function newRequire(name, jumped) {
    if (!cache[name]) {
      if (!modules[name]) {
        // if we cannot find the module within our internal map or
        // cache jump to the current global require ie. the last bundle
        // that was added to the page.
        var currentRequire = typeof parcelRequire === 'function' && parcelRequire;
        if (!jumped && currentRequire) {
          return currentRequire(name, true);
        }

        // If there are other bundles on this page the require from the
        // previous one is saved to 'previousRequire'. Repeat this as
        // many times as there are bundles until the module is found or
        // we exhaust the require chain.
        if (previousRequire) {
          return previousRequire(name, true);
        }

        // Try the node require function if it exists.
        if (nodeRequire && typeof name === 'string') {
          return nodeRequire(name);
        }

        var err = new Error('Cannot find module \'' + name + '\'');
        err.code = 'MODULE_NOT_FOUND';
        throw err;
      }

      localRequire.resolve = resolve;
      localRequire.cache = {};

      var module = cache[name] = new newRequire.Module(name);

      modules[name][0].call(module.exports, localRequire, module, module.exports, this);
    }

    return cache[name].exports;

    function localRequire(x){
      return newRequire(localRequire.resolve(x));
    }

    function resolve(x){
      return modules[name][1][x] || x;
    }
  }

  function Module(moduleName) {
    this.id = moduleName;
    this.bundle = newRequire;
    this.exports = {};
  }

  newRequire.isParcelRequire = true;
  newRequire.Module = Module;
  newRequire.modules = modules;
  newRequire.cache = cache;
  newRequire.parent = previousRequire;
  newRequire.register = function (id, exports) {
    modules[id] = [function (require, module) {
      module.exports = exports;
    }, {}];
  };

  var error;
  for (var i = 0; i < entry.length; i++) {
    try {
      newRequire(entry[i]);
    } catch (e) {
      // Save first error but execute all entries
      if (!error) {
        error = e;
      }
    }
  }

  if (entry.length) {
    // Expose entry point to Node, AMD or browser globals
    // Based on https://github.com/ForbesLindesay/umd/blob/master/template.js
    var mainExports = newRequire(entry[entry.length - 1]);

    // CommonJS
    if (typeof exports === "object" && typeof module !== "undefined") {
      module.exports = mainExports;

    // RequireJS
    } else if (typeof define === "function" && define.amd) {
     define(function () {
       return mainExports;
     });

    // <script>
    } else if (globalName) {
      this[globalName] = mainExports;
    }
  }

  // Override the current require with this new one
  parcelRequire = newRequire;

  if (error) {
    // throw error from earlier, _after updating parcelRequire_
    throw error;
  }

  return newRequire;
})({"index.js":[function(require,module,exports) {
// import TableView from "./components/tableView.vue";
panel.plugin('mshoestrng/tableview', {
  fields: {
    // tableview: TableView
    tableview: {
      template: "<p>hello wor</p>"
    }
  }
});
},{}],"../../../../../../../../../../usr/local/lib/node_modules/parcel-bundler/src/builtins/hmr-runtime.js":[function(require,module,exports) {
var global = arguments[3];
var OVERLAY_ID = '__parcel__error__overlay__';
var OldModule = module.bundle.Module;

function Module(moduleName) {
  OldModule.call(this, moduleName);
  this.hot = {
    data: module.bundle.hotData,
    _acceptCallbacks: [],
    _disposeCallbacks: [],
    accept: function (fn) {
      this._acceptCallbacks.push(fn || function () {});
    },
    dispose: function (fn) {
      this._disposeCallbacks.push(fn);
    }
  };
  module.bundle.hotData = null;
}

module.bundle.Module = Module;
var checkedAssets, assetsToAccept;
var parent = module.bundle.parent;

if ((!parent || !parent.isParcelRequire) && typeof WebSocket !== 'undefined') {
  var hostname = "" || location.hostname;
  var protocol = location.protocol === 'https:' ? 'wss' : 'ws';
  var ws = new WebSocket(protocol + '://' + hostname + ':' + "63864" + '/');

  ws.onmessage = function (event) {
    checkedAssets = {};
    assetsToAccept = [];
    var data = JSON.parse(event.data);

    if (data.type === 'update') {
      var handled = false;
      data.assets.forEach(function (asset) {
        if (!asset.isNew) {
          var didAccept = hmrAcceptCheck(global.parcelRequire, asset.id);

          if (didAccept) {
            handled = true;
          }
        }
      }); // Enable HMR for CSS by default.

      handled = handled || data.assets.every(function (asset) {
        return asset.type === 'css' && asset.generated.js;
      });

      if (handled) {
        console.clear();
        data.assets.forEach(function (asset) {
          hmrApply(global.parcelRequire, asset);
        });
        assetsToAccept.forEach(function (v) {
          hmrAcceptRun(v[0], v[1]);
        });
      } else {
        window.location.reload();
      }
    }

    if (data.type === 'reload') {
      ws.close();

      ws.onclose = function () {
        location.reload();
      };
    }

    if (data.type === 'error-resolved') {
      console.log('[parcel] ✨ Error resolved');
      removeErrorOverlay();
    }

    if (data.type === 'error') {
      console.error('[parcel] 🚨  ' + data.error.message + '\n' + data.error.stack);
      removeErrorOverlay();
      var overlay = createErrorOverlay(data);
      document.body.appendChild(overlay);
    }
  };
}

function removeErrorOverlay() {
  var overlay = document.getElementById(OVERLAY_ID);

  if (overlay) {
    overlay.remove();
  }
}

function createErrorOverlay(data) {
  var overlay = document.createElement('div');
  overlay.id = OVERLAY_ID; // html encode message and stack trace

  var message = document.createElement('div');
  var stackTrace = document.createElement('pre');
  message.innerText = data.error.message;
  stackTrace.innerText = data.error.stack;
  overlay.innerHTML = '<div style="background: black; font-size: 16px; color: white; position: fixed; height: 100%; width: 100%; top: 0px; left: 0px; padding: 30px; opacity: 0.85; font-family: Menlo, Consolas, monospace; z-index: 9999;">' + '<span style="background: red; padding: 2px 4px; border-radius: 2px;">ERROR</span>' + '<span style="top: 2px; margin-left: 5px; position: relative;">🚨</span>' + '<div style="font-size: 18px; font-weight: bold; margin-top: 20px;">' + message.innerHTML + '</div>' + '<pre>' + stackTrace.innerHTML + '</pre>' + '</div>';
  return overlay;
}

function getParents(bundle, id) {
  var modules = bundle.modules;

  if (!modules) {
    return [];
  }

  var parents = [];
  var k, d, dep;

  for (k in modules) {
    for (d in modules[k][1]) {
      dep = modules[k][1][d];

      if (dep === id || Array.isArray(dep) && dep[dep.length - 1] === id) {
        parents.push(k);
      }
    }
  }

  if (bundle.parent) {
    parents = parents.concat(getParents(bundle.parent, id));
  }

  return parents;
}

function hmrApply(bundle, asset) {
  var modules = bundle.modules;

  if (!modules) {
    return;
  }

  if (modules[asset.id] || !bundle.parent) {
    var fn = new Function('require', 'module', 'exports', asset.generated.js);
    asset.isNew = !modules[asset.id];
    modules[asset.id] = [fn, asset.deps];
  } else if (bundle.parent) {
    hmrApply(bundle.parent, asset);
  }
}

function hmrAcceptCheck(bundle, id) {
  var modules = bundle.modules;

  if (!modules) {
    return;
  }

  if (!modules[id] && bundle.parent) {
    return hmrAcceptCheck(bundle.parent, id);
  }

  if (checkedAssets[id]) {
    return;
  }

  checkedAssets[id] = true;
  var cached = bundle.cache[id];
  assetsToAccept.push([bundle, id]);

  if (cached && cached.hot && cached.hot._acceptCallbacks.length) {
    return true;
  }

  return getParents(global.parcelRequire, id).some(function (id) {
    return hmrAcceptCheck(global.parcelRequire, id);
  });
}

function hmrAcceptRun(bundle, id) {
  var cached = bundle.cache[id];
  bundle.hotData = {};

  if (cached) {
    cached.hot.data = bundle.hotData;
  }

  if (cached && cached.hot && cached.hot._disposeCallbacks.length) {
    cached.hot._disposeCallbacks.forEach(function (cb) {
      cb(bundle.hotData);
    });
  }

  delete bundle.cache[id];
  bundle(id);
  cached = bundle.cache[id];

  if (cached && cached.hot && cached.hot._acceptCallbacks.length) {
    cached.hot._acceptCallbacks.forEach(function (cb) {
      cb();
    });

    return true;
  }
}
},{}]},{},["../../../../../../../../../../usr/local/lib/node_modules/parcel-bundler/src/builtins/hmr-runtime.js","index.js"], null)

You’d have to dive through the kirby repo to get the app.js source, the error is displayed by the error-boundary component, but if there’s nothing in the console I’m afraid you’ll find very little by inspecting this one. The error is thrown here, so probably means your PHP file isn’t registered correctly.

The output looks fine, I created a new folder with nothing but your index.php + outputted index.js, hello world shows.

I see you have other plugins in the folder, have you tried removing them and keeping only your plugin to see if one is causing interferences?

I now went back to the start and installed the Kirby starter kit in a new folder. Afterwards I created a simple plugin extending the text field following this guide: https://getkirby.com/docs/reference/plugins/extensions/fields#extending-existing-fields

about.yml

# ...
blaha:
        label: My plugin
        type: hello
        default: 123
# ....

index.php

<?php

Kirby::plugin('your/plugin', [
    'fields' => [
        'hello' => [
            'extends' => 'text'
        ]
    ]
]);

index.js

panel.plugin("your/plugin", {
  fields: {
    hello: {
      extends: "k-text-field",
      template: "<input>whatsup</input>"
    }
  }
});

This shows me a clickable/writable input field.

However, when I remove the “template” line it only show this:

image

…while I would expect it shows a text input (as I’m extending the text field without adding anything).

Why is this not working??

If you haven’t already, you should get the Vue.js devtools extensions, it will help you debug these kind of things.

Put one text field next to your hello field, and check what differs between the two in your devtools.

What’s happening here: inside the k-text-field there’s a k-text-input component (the actual input). Kirby automatically deduces the name of the input from the field type, so that k-select-field loads a k-select-input, etc.

Because your field is called hello, the field is trying to load a k-hello-input. That doesn’t exist. So nothing shows.

04

So, the field will work as planned after creating a k-hello-input (or rewriting the text field template so that it always load the k-text-input no matter what the field type is, but more tedious overall):

panel.plugin("your/plugin", {
  fields: {
    hello: { extends: "k-text-field" }
  },
  components: {
    'k-hello-input': { extends: "k-text-input" }
  }
});
1 Like

Ok, this worked. Thanks!

Now I tried to take the next step and move this into a component structure.

your-plugin/src/index.js:

// import myText from "./components/mytext.vue";

panel.plugin("your/plugin", {
  fields: {
    // hello: myText
    hello: {
      extends: "k-text-field",
      template: "<input>whatsup</input>"
    }
  },
  components: {
    'k-hello-input': { extends: "k-text-input" }
  }
});

However, after compiling with npm run dev, I still get the error message The field type "bla" does not exist in the panel. What am I doing wrong?

Just to clarify: all I’m looking for is a way to extend/modify the template (looks) of the structure field. So I need a setup where my field plugin provides me with a vue-component where I can paste/extend the original template code of the structure field.

@sylvainjule: would it be possible that you provide me with a skeleton for this purpose? Otherwise I think it will take many more posts for me to move from my simple text field plugin via a component-type plugin extending the text field to finally a component-type plugin extending the structure field without any proper debugging tools in the Kirby panel…

Your or anyone else’s help would be much appreciated! Thanks!

Here is another take I tried with still the same error: The field type "bla2" does not exist. Any help for this and my post just above would be much appreciated!

about.yml:

  bla:
    label: My plugin
    type: myplugtest
  bla2:
    label: My pluginevo
    type: myplugtestevo
  blax:
    type: text

index.php:

<?php

Kirby::plugin('test/pluginevo', [
    'fields' => [
        'myplugtestevo' => [
            'extends' => 'text'
        ]
    ]
]);

src/index.js:

import myplugtestevo from "./components/myplugtestevo.vue";

panel.plugin("test/pluginevo", {
  fields: {
    myplugtestevo: myplugtestevo
  },
  components: {
    'k-myplugtestevo-input': { extends: "k-text-input" }
  }
});

src/components/myplugtestevo.vue:

<template>
  <p>hello</p>

</template>

<script>
  export default {
    extends: "k-text-field"
  }

</script>

<style>

</style>

This is what the vue.js devtools are showing me. Again: how the hell is one supposed to deduct any debugging hints from the absence of useful information??

image

Your code looks fine, only tried the one from your latest post and it works fine.

Please remove all other plugins and try running npm run build in both folders instead of npm run dev. See if this fixes your issues, then only run npm run dev in one folder at a time.

You can’t have two or more plugins in dev mode simultaneously, you’ll end up with the error message you’re posting.

I moved all other plugin folders I created previously out of htdocs (document-root) and ran npm run build on the remaining plugin folder. Same error message. Same structure in Vue devtools.

Code of compiled index.js:

(function () {var a={extends:"k-text-field"};if(typeof a==="function"){a=a.options}Object.assign(a,function(){var render=function(){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c("p",[_vm._v("hello")])};var staticRenderFns=[];return{render:render,staticRenderFns:staticRenderFns,_compiled:true,_scopeId:null,functional:undefined}}());panel.plugin("test/pluginevo",{fields:{myplugtestevo:a},components:{"k-myplugtestevo-input":{extends:"k-text-input"}}});})();

What’s wrong now?

Could anyone please provide a pluginkit which extends a kirby field and uses a vue-component @bastianallgeier @sylvainjule?

Hm, the above code example works for me as well, although the first time I got the error message and then after rebuilding it worked. So I wonder if that’s rather some caching issue, similar to what happened here: The section type "pagetable" is not valid

@texnixe Well, I rebuilt the plugin several times in build and dev mode. I also opened the panel in a Chrome private window. This should get rid of any caching issues. Right?

Still it doesn’t work… And hints welcome!

Can you provide a complete package for testing, i.e. your project including the plugin that doesn’t work. I have no idea whatsoever how to reproduce that if the code snippets you provide work perfectly.

This new recipe might help if you try to do something similar: https://getkirby.com/docs/cookbook/extensions/first-panel-field

@mshoestrng Did you ever solve your problem?

@texnixe I started again from scratch and was successful, see Access content of blueprint field in index.js