aurelia-contrib / aurelia-knockout

Adds support for Knockout binding syntax to make transition from Durandal and Knockout to Aurelia simpler.
MIT License
22 stars 4 forks source link

TypeError: customConfig.configure is not a function #6

Closed Sigfried closed 7 years ago

Sigfried commented 7 years ago

I'm probably just not understanding how to get aurelia-knockout set up. Others have seen a similar error (https://github.com/aurelia/bootstrapper-webpack/issues/2). Here's what I did:

After creating a new app with au new, I cp -r .../aurelia-knockout/dist/es2015 into .../src/aurelia-knockout in my new app. Then (with au run --watch) I get Error: ENOENT: no such file or directory, open .../src/knockout.js. So then I put a copy of knockout.min.js in .../src, and then au run doesn't complain anymore, but the browser console gives me the vendor-bundle.js:3394 TypeError: customConfig.configure is not a function error.

AStoker commented 7 years ago

It looks like that first error is the problem. Do you have knockout configured in your project? If you're using Aurelia CLI, make sure that you have it listed in the deps sections, or somewhere where it's defined. Most times when you see an error saying that you can't find a module in the src directory and the module it's looking for is just a name (like knockout), it's because in your code you tried to import "knockout", but since it's not defined in any config, it just assumes you're trying to load the file called "knockout" in the base directory.

ckotzbauer commented 7 years ago

I can't reproduce this error directly. I agree with @AStoker that situations where you want to copy files to other directories are often caused by missing path configuration in your app.

What I've done (this worked for me):

  1. Created a new app with "au new"
  2. Installed aurelia-knockout with npm
  3. Configured aurelia-knockout as plugin in the main.js
  4. Added the following items to the "dependencies"section in "aurelia.json" under "aurelia_projects" folder:
{
  "name": "aurelia-knockout",
  "path": "../node_modules/aurelia-knockout/dist/amd",
  "main": "aurelia-knockout"
},
"knockout"

In my test I also added "aurelia-history-browser" because the app does not find the module...

Then I was able to start the app with "au run" and use knockout binding syntax in html. I only got one error in my browser console: "Uncaught ReferenceError: exports is not defined". This happend in my "aurelia-knockout" module but I don't know why. I compared this file with the corresponding file from "aurelia-templating-resources" and found no important differences. So I don't know why this is handled in another way.... Maybe someone else knows.

AStoker commented 7 years ago

@code-chris, specifically what line was causing the error? It sounds like something else might be hosed, because exports is essentially the object that we're exporting for AMD (it's the way ES6 gets transpiled to AMD). Also, wouldn't you want "knockout" defined before your "aurelia-knockout" dependency? I'm pretty sure that the dependencies are loaded in order, but it might not cause a problem if it's all modular anyway.

ckotzbauer commented 7 years ago

@AStoker Thanks for your note with the dependency order ;) The error happend on line 4 in the amd compiled "aurelia-knockout.js" file from my package:

Object.defineProperty(exports, "__esModule", {

I've seen that this code block is not surrounded with the define block in the bundle. So no exports variable is available there...

AStoker commented 7 years ago

@code-chris, so you're saying that when it's in the bundled code, you don't have that module wrapped in a define block like this?

define(['exports', './aurelia-knockout-custom-attribute', './knockout-composition', './knockout-bindable'], function (exports, _aureliaKnockoutCustomAttribute, _knockoutComposition, _knockoutBindable) {
  'use strict';

  Object.defineProperty(exports, "__esModule", {
    value: true
  });
  exports.configure = exports.KnockoutBindable = exports.KnockoutCustomAttribute = undefined;

  function configure(frameworkConfig) {
    frameworkConfig.globalResources('./aurelia-knockout-custom-attribute');

    frameworkConfig.container.get(_knockoutComposition.KnockoutComposition).register();

    _aureliaKnockoutCustomAttribute.KnockoutCustomAttribute.register();
  }

  exports.KnockoutCustomAttribute = _aureliaKnockoutCustomAttribute.KnockoutCustomAttribute;
  exports.KnockoutBindable = _knockoutBindable.KnockoutBindable;
  exports.configure = configure;
});
ckotzbauer commented 7 years ago

Yes. It looks like this:

'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.configure = exports.KnockoutBindable = exports.KnockoutCustomAttribute = undefined;

var _aureliaKnockoutCustomAttribute = require('./aurelia-knockout-custom-attribute');

var _knockoutComposition = require('./knockout-composition');

var _knockoutBindable = require('./knockout-bindable');

function configure(frameworkConfig) {
  frameworkConfig.globalResources('./aurelia-knockout-custom-attribute');

  frameworkConfig.container.get(_knockoutComposition.KnockoutComposition).register();

  _aureliaKnockoutCustomAttribute.KnockoutCustomAttribute.register();
}

exports.KnockoutCustomAttribute = _aureliaKnockoutCustomAttribute.KnockoutCustomAttribute;
exports.KnockoutBindable = _knockoutBindable.KnockoutBindable;
exports.configure = configure;

The modules below and above of this are surrounded as expected.

ckotzbauer commented 7 years ago

Hm, this snippet is equal with the commonjs distro... But I've defined the amd version in aurelia.json

ckotzbauer commented 7 years ago

But the other modules like "aurelia-knockout/aurelia-knockout-custom-attribute" are included as amd...

Sigfried commented 7 years ago

I just followed @code-chris's instructions from 11:44am edt (except for 'added "aurelia-history-browser"', which I didn't know how to do), and I'm getting no errors so far. I was just going about it all wrong. But I have still to try to get some knockout stuff running, so I may be back... Thanks for the help!

ckotzbauer commented 7 years ago

ok. Closed, for now.

AStoker commented 7 years ago

@Sigfried @code-chris, I am not able to reproduce any of the errors mentioned on a clean CLI installation. @Sigfried, based off your comments from the other error, I think you're probably messing a few things up by just copying files. If you're using the CLI do these steps.

"knockout",
{
   "name": "aurelia-knockout",
   "path": "../node_modules/aurelia-knockout/dist/amd",
   "main": "aurelia-knockout"
},
aurelia.use
    .standardConfiguration()
    .feature('resources')
    .plugin('aurelia-knockout');
<template>
  <div knockout>
    <h1>${message}</h1>
    <h4 data-bind="text: subMessage"></h4>
  </div>
</template>

Your view model can remain the same if you want.

export class App {
  constructor() {
    this.message = 'Hello World!';
    this.subMessage = 'Hello Knockout!';
  }
}

screen shot 2016-10-06 at 1 50 06 pm

I hope this helps you get your setup @Sigfried. And perhaps @code-chris that would help documentation for installing with the CLI. The CLI is still in alpha and can be a bit complicated to figure out at first how to get things to work (especially if you're coming from JSPM/SystemJS), so it helps to have some brief docs explaining the initial setup (which thankfully isn't much).

AStoker commented 7 years ago

P.S. When Github doesn't notify that the issue has been closed and you type up a response... Yah, that stinks. 😛

Sigfried commented 7 years ago

Thanks, @AStoker! I think your instructions are pretty much the same as @code-chris's earlier (but making the next step clearer with item 4.) I got it working with a super-simple knockout data-binding (just 'text', but that should be enough to prove it works).

So now where I'm flummoxed is that I have this whole, huge knockout app with modules managed by require.js. I'm not sure how much plumbing I'm going to have to retrofit or if there's some way to get the ES6 (or whatever) and the require.js parts to work nicely together. I'd like to start by just making a big <div knockout>...</div> in app.html and stuff basically my whole index.html from the knockout app into it. In the Aurelia app's index.html I blindly shoved my aurelia data-main and my require.js data-main together. I'm sure this isn't the way to do it, but I'm not having any great ideas so far.

<body aurelia-app="main">
   <script src="scripts/vendor-bundle.js" data-main="aurelia-bootstrapper"></script>
   <script data-main="js/main" src="js/require.js"></script>
</body>

Any advice? Thanks so much again.

AStoker commented 7 years ago

We have a similar situation @Sigfried, we have an application that was knockout based with Durandal. We are about to start moving it over to our Aurelia application, and have had some success with it. What we've done is use a tool called Amd-to-ES6 (https://github.com/jonbretman/amd-to-as6) to automagically make all our view models (in AMD Require syntax) into ES6 modules/classes. We did have to go through each view that we were using knockout in (which is many) and add the knockout attribute to the div, but that's just legwork and isn't too bad.
BTW, the code snippet you had is no bueno. The vendor-bundle.js contains requirejs in it, so you don't want to pull it in again. The aurelia-bootstrapper will go and get your main.js file by convention, so you shouldn't need that second script tag.

ckotzbauer commented 7 years ago

Look at this migration article. There are some topics you have to do: https://github.com/code-chris/aurelia-knockout/wiki

Sigfried commented 7 years ago

Thanks!

Another thing, in case it matters. We weren't using Durandal. But are the issues basically the same?

ckotzbauer commented 7 years ago

So, is your current architecture based on another single-page framework? Or did you just use knockout for data-binding?

AStoker commented 7 years ago

Awesome job on the Wiki!

Sigfried commented 7 years ago

Yeah, awesome job on the wiki!

It is a single-page app written with knockout. You can see it here: https://github.com/OHDSI/Atlas

Sigfried commented 7 years ago

@AStoker, looks like I'm not going to be able to use amd-to-es6, at least not easily. The way our routing was written (https://github.com/OHDSI/Atlas/blob/master/js/app.js#L24) makes it choke on multiple module definitions.

@code-chris, I haven't quite figured out what needs to be done with amd modules based on the Wiki... I'm about to try following directions here https://github.com/code-chris/aurelia-knockout/wiki#requirejs-and-npm to see if I can keep the existing requirejs stuff for now...

Sigfried commented 7 years ago

Ok, I tried that, but I'm getting this error at aurelia-loader-default.js:54:

Uncaught TypeError: Cannot read property '__useDefault' of undefined
    ensureOriginOnExports @ aurelia-loader-default.js:54
    (anonymous function) @ aurelia-loader-default.js:162
    execCb @ require.js:29
    check @ require.js:18
    ....

Maybe I still don't have all the pieces in place... I'll keep reading that Wiki section...

Sigfried commented 7 years ago

Actually, I seem to be getting pretty close. I'm still getting that error, but the app seems to be working like the original knockout app.

Sigfried commented 7 years ago

Sorry to be annoying...

The point I'm getting stuck now is: https://github.com/code-chris/aurelia-knockout/wiki#app-startup. When I put es6 syntax in main.js, I get main.js:1 Uncaught SyntaxError: Unexpected token export.

Presumably the problem is just that I don't have transpiling in place. Following the wiki instructions (https://github.com/code-chris/aurelia-knockout/wiki#requirejs-and-npm) I installed the 'minimal required Aurelia modules' instead of doing au new, which means that au run won't work, so I'm using live-server as my web server, but that leaves out transpiling...

Sigfried commented 7 years ago

Spent last three hours trying to get my require config either ported to or cooperating with the webpack config from the Aurelia webpack skeleton. Giving up on that now.

ckotzbauer commented 7 years ago

If you use webpack with aurelia you don't need another module loader like requirejs. This is handled by the aurelia-loader-webpack implementation. Where are your problems with the configuration? I recommend not to use easy-webpack like in the skeletons, because you lost flexibility at some point...

Sigfried commented 7 years ago

Right. The reason I tried the webpack from the skeleton is that I was looking for the right way to get the es6 stuff working. It's going to be hard for me to get rid of requirejs (except gradually) because it's used so extensively in the base project.

As I mentioned before attempting to use the webpack skeleton, my app now seems fully functional though I'm calling it through <body aurelia-app="main"> and require(["aurelia-bootstrapper"]), but main.js is just my original main.js from the knockout app (a require module). I'm not able follow the wiki's app startup instructions (https://github.com/code-chris/aurelia-knockout/wiki#app-startup):

export function configure(aurelia) => {
    aurelia.use
        .standardConfiguration()...

because I don't have a build process or included libraries (as far as I know) that will do the transpiling. Also, although the base knockout app is working ok, I'm still getting this error in the console: Cannot read property '__useDefault' of undefined.

ckotzbauer commented 7 years ago

You can transpile es6 code with the babel-loader from webpack. See the README from this package: https://github.com/babel/babel-loader

Webpack creates bundles where your app and (hopefully) all dependencies are included. There is one "entry" bundle-js-file which you can use as start point. Add this script file with a <script> to your index.html and remove the require(['aurelia-bootstrapper']). Webpack includes all files based on dependencies. So a file which is never referenced in your app will be ignored by default.

Note: You have to modify your main.js a little bit if you use webpack:

bootstrap((aurelia) => {
    aurelia.use...
});
Sigfried commented 7 years ago

Thanks again. I seem to be getting closer, but still fairly confused. The way I'm doing it now, my main routine runs once when bundle.js gets loaded and again for the <body aurelia-app="main">, I think: https://github.com/Sigfried/Atlas/blob/aurelia-poc/index.html#L31

ckotzbauer commented 7 years ago

Oh, sorry. You have to remove the aurelia-app="main" too.

Sigfried commented 7 years ago

I did that and now I'm getting Error: No applicationHost was specified.

ckotzbauer commented 7 years ago

You have to set the HTML-Element where you want to place the app.

aurelia.start().then((a) => {
        a.setRoot("app", document.body);
});
Sigfried commented 7 years ago

Thank you. (I was just on the verge of figuring that out...glad to know I was headed in the right directions.)

Sigfried commented 7 years ago

Update: I finally gave up on webpack. Getting it to cooperate with requirejs was driving me nuts. Maybe I can come back to it after removing requirejs from the project. So, I've had much more success with https://github.com/icecreamliker/requirejs-babel-plugin.

Question about aurelia-knockout: Is there a way, deep inside a <div knockout> element, to make an element that uses aurelia binding again?

AStoker commented 7 years ago

If you use Aurelia Enhance on your page, I believe it works on anything on the page, including things nested from knockout

ckotzbauer commented 7 years ago

Yes, you can use aurelia bindings inside elements marked with knockout.

Only a side note to webpack: If you integrate webpack with aurelia then you can remove requirejs from your project. The aurelia-loader will with its implementation aurelia-loader-webpack load all your modules. To be backwards compatible maybe you have to polyfill the "require" function from the window context. See my unreleased solution: https://github.com/code-chris/aurelia-knockout/blob/master/src/require-polyfill.js