Non-component modules, non-npm libraries and other questions

I went through the place-my-order sample app. However the app I would like to create is not the normal web app. The entire application will by zipped up, downloaded to a samsung monitor and run locally on its tweaked, internal monitor, thus allowing the monitor to run off-line as well as better performance. The only server interaction it will have is to download a json document describing the content and styling for the monitor. Also (for now) it is entirely read-only.

So, since I haven’t seen any examples or discussions about some of the issues I face, I have a list of questions:

  1. All of the examples I have seen in donejs use component-based modules. While this is great if those modules have a UI aspect (like the restaurant modules in pmo), what about modules that are there to do back-end processes or utilities? For example, I want to create a module to handle parsing the original json doc into objects holding data to render different content/styling on the page (like for css, text, images), I have a module to download the image and video files, one to interact with the local file system, etc. What is the best way to handle such modules in donejs?

  2. How do I include 3rd party libraries that are not in the npm registry? What if they are bower components? What if I just have the actual js?

  3. I see can-connect has a local store to cache subsequent find requests. Does that store persist or is it only for the duration of the session? What if I want to override the local store and force a refresh from the server? For example, the original json doc downloaded 1st time the app is used and the parsed objects need to be saved locally (localStorage or IndexedDB), so the app can load them right away next time the monitor is turned on. After the page is loaded, I want to ping the server for any updates to doc while the monitor was off.

  4. the doc can contain multiple presentations of content to be shown at different times during the day (day-parting). So what I want is, based on a scheduler (another non-UI module), to switch the viewModel from the current presentation to the new one. I was thinking of using can.route to switch between presentations (similar to the restaurant-slug in pmo). Does that sound like the way to go or is there a better way?

  5. In pmo, the slug changes were tied to can-connect, but as mentioned, I am not connecting to a server to get this data, rather to locally saved objects. Would I be able to wire can-connect to pull the data (and store the data) locally or do I need to fall back to the more traditional load into a can.Map/List approach and go from there?

  6. Since styling is not known until runtime, I can’t create static stylesheets. Rather the parser returns a css string that it compiled from the doc, and my plan was to save it as a .css file and then inject a link tag in the page (with a different .css file for each presentation). Then on subsequent page loads, it would just read from the .css file like a normal web page. Or I could just inject it into the template in a style tag. How can I get this to work with the stache template? Would a dynamic binding on can-import tag work? Just inject into the page after loading with jQuery? Is this something steal could handle? Another suggestion?

Okay, I know this is a lot and am not looking for anyone to “write” my app. I just have a lot of unknowns about donejs.

Thank you just for reading this far,
Dovid

I’ll quickly cover some of what’s here. If you want more in-depth questions, please break this post up into individual posts. People will find it more useful and it will help SEO.

what about modules that are there to do back-end processes or utilities?

You can easily create any other type of JS module. Sometimes people put them in a lib folder. But organization is up to you.

How do I include 3rd party libraries that are not in the npm registry? What if they are bower components? What if I just have the actual js?

You can use scripts installed via bower or downloaded from the internet. Typically you have to specify them as the “global” format and add any dependencies in your package.json. This example: https://github.com/canjs/steal-can-example/blob/master/package.json#L51 shows setting up some jQueryUI libraries.

I see can-connect has a local store to cache subsequent find requests. Does that store persist or is it only for the duration of the session? What if I want to override the local store and force a refresh from the server?

You can wire up can-connect to different stores. The localstorage will last past the session. But you can use a memory store that will last the duration of the page lifecycle. You could create a sessionStore version of localStorage.

I was thinking of using can.route to switch between presentations (similar to the restaurant-slug in pmo)

If this state doesn’t belong in the URL, don’t put it in the url. Instead use an AppViewModel define property based around a time compute:

// time-compute.js
import compute from "can/compute/";
var t = can.compute(new Date());
setInterval(function(){
  t(newDate());
},1000*60);
export default t;
import time from '../lib/time-compute'

...

define: {
  contentDayType: {
    get: function(){
      if( isMorning( t() ) {
        return "morning"
      } else if( isAfterNoon( t() ) {
        return "afternoon"
      } else {
        return "night"
      }
    }
  }
}

Then your template could have:

{{#eq contentDayType 'morning'}}
   <something-morning/>
...

Would I be able to wire can-connect to pull the data (and store the data) locally or do I need to fall back to the more traditional load into a can.Map/List approach and go from there?

I’m not sure what you mean exactly by locally saved objects. Presumably you mean static resources on the server. can-connect is super crazy flexible once you understand it’s wide range of interfaces so it’s likely it’s still useful. No need to use can-connect though unless you are going to benefit from it.

Rather the parser returns a css string that it compiled from the doc

I’m not sure what this means so it’s hard to answer the rest of your question.

Would a dynamic binding on can-import tag work?

I believe this works, but not with the build system. You’d have to manually tell steal-tools about the bundles that might be dynamically loaded.

am not looking for anyone to “write” my app

Why not?!? It’s smart if you are. You have a lot of good questions. Maybe you’d like to be our guest for this wednesday’s weekly training? You could ask me and the team these questions and we could even hack on solutions for an hour. (you’d have to be willing to share a bit of the details of your app, maybe a screenshot).

If you need some more in-depth responses, please break up these questions so other people will be able to make sense of the answers you’ll get. Thanks!

@justinbmeyer, thank you for your response. I didn’t want to “flood” the forum with a bunch of questions all at once, at least not yet. :wink:

My initial question about the non-UI modules, was in terms of using donejs init to create the modlet pattern. For these modules I should just create the modlet files manually or do you think it’s better to use the init and just ignore/delete the stache file?

I finally found the more in-depth can-connect docs, so I am now going through them and should get more clarity.

I’ll send follow up posts for on additional questions.

When you say just for the weekly training, do you mean during the actual hour or afterwards? I’d hate to take the focus away from the main topic to the detriment of the others watching. Thanks for the offer. Maybe if I don’t have enough put together for this week, I can join next week?

In any case, very excited to use donejs on this (and hopefully future) projects. Thanks for all your great work.

1 Like

I am also wondering how to get 3rd party library scripts working with donejs.

When attempting to use <can-import> to load scripts in index.stache I am getting errors like:

document.querySelector is not a function

@justinbmeyer, you mention that you have to specify them in the “global” space, but I am not able to find any information for how to do this.

@jfleming, how have you imported them into your project?

Are you using npm or bower? Or are you dropping in the raw js code into a lib folder in your project?

@justinbmeyer went through some of how to do that in a training video with me.

@dbleier thanks for linking that video. Lots of useful information in there

I am just dropping raw js code into a libs folder. Basically I have a theme from themeforest and I’m trying to load some dependent scripts. Unfortunately it looks like there is something about some of the libraries that can-import just doesn’t like. For example, I am trying to load the library pace (https://github.com/HubSpot/PACE)

I set up the path and meta tags in packages.json

    "paths": {
        "pace": "./src/libs/pace/pace.js"
    },
    "meta": {
        "pace": {
            "format": "global"
        }
    }

I’m imorting it in app.js with

import 'pace';

I get the following error

can-serve starting on http://localhost:8080
WARN: Could not find pace in node_modules. Ignoring.
Potentially unhandled rejection [3] TypeError: Error loading "pace" at file:C:/DoneJS/admin/src/import 'src/libs/pace/pace.js
Error loading "pace" from "admin@0.0.0#index.stache!done-autorender@0.6.3#autore
nder" at file:C:/DoneJS/src/index.stache
Error evaluating file:C:/DoneJS/src/import 'src/libs/pace/pace.js
document.querySelector is not a function
    at getFromDOM (file:C:/DoneJS/src/import 'src/libs/pace/pace.js:119:19)
    at file:C:/DoneJS/src/import 'src/libs/pace/pace.js:210:75
    at file:C:/DoneJS/src/import 'src/libs/pace/pace.js:885:4
    at Object.exports.runInThisContext (vm.js:54:17)
    at doEval (C:\DoneJS\admin\node_modules\can-ssr\node_modules\steal\node_modules\steal-systemjs\dist\system.src.js:2012:10)
    at __eval (C:\DoneJS\admin\node_modules\can-ssr\node_modules\steal\node_modules\steal-systemjs\dist\system.src.js:1929:7)
    at Loader.exec [as __exec] (C:\DoneJS\admin\node_modules\can-ssr\node_modules\steal\node_modules\steal-systemjs\dist\system.src.js:339:5)
    at load.metadata.execute (C:\DoneJS\admin\node_modules\can-ssr\node_modules\steal\node_modules\steal-systemjs\dist\system.src.js:1105:16)
    at linkDynamicModule (C:\DoneJS\admin\node_modules\can-ssr\node_modules\steal\node_modules\steal-systemjs\dist\system.src.js:668:32)
    at link (C:\DoneJS\admin\node_modules\can-ssr\node_modules\steal\node_modules\steal-systemjs\dist\system.src.js:528:11)

I’m having a similiar problem when attempting to import the jquery-ui library. I know that @justinbmeyer shows how to import modules individually from bower above and in the video, but importing the jquery-ui-1.10.4.custom.min.js script directly gives the error

view.getComputedStyle is not a function

You need

meta: {moduleName: {format: “global”}}

Sorry, I’m having trouble seeing how that’s different from what I’ve said I’ve done above in package.json. Here is the full file for clarity.

{
  "name": "admin",
  "version": "0.0.0",
  "description": "admin",
  "homepage": "",
  "author": {
    "name": "",
    "email": "",
    "url": ""
  },
  "scripts": {
    "test": "testee src/test.html --browsers firefox --reporter Spec",
    "start": "can-serve --port 8080",
    "develop": "can-serve --develop --port 8080",
    "document": "documentjs",
    "build": "node build"
  },
  "main": "admin/index.stache!done-autorender",
  "files": [
    "src"
  ],
  "keywords": [],
  "system": {
    "main": "admin/index.stache!done-autorender",
    "directories": {
      "lib": "src"
    },
    "configDependencies": [
      "live-reload"
    ],
    "paths": {
        "pace": "./src/libs/pace/pace.js"
    },
    "meta": {
        "pace": {
            "format": "global"
        }
    }
  },
  "dependencies": {
    "bootstrap": "^3.3.6",
    "can": "^2.3.16",
    "can-connect": "^0.3.4",
    "can-ssr": "^0.11.6",
    "done-autorender": "^0.6.3",
    "done-component": "^0.4.0",
    "done-css": "~1.1.16",
    "generator-donejs": "^0.7.0",
    "jquery": "~2.2.1",
    "pace": "0.0.4",
    "steal": "^0.14.0"
  },
  "devDependencies": {
    "documentjs": "^0.4.2",
    "donejs-deploy": "^0.4.0",
    "funcunit": "~3.0.0",
    "steal-qunit": "^0.1.1",
    "steal-tools": "^0.14.0",
    "testee": "^0.2.4",
    "donejs-cli": "^0.7.0",
    "can-fixture": "^0.1.2"
  }
}

Ah, this isn’t a steal issue, this is a can-ssr issue. Can-ssr’s virtual Dom doesn’t support queryselector.

For now, the best thing is to conditionally prevent this module from loading on the server and not use it in a node environment. I can show you this tomorrow.

Soon, can-wait and can-ssr will be documented well enough that you can use a different virtual DOM that does support this method.

@justinbmeyer are you able to show an example of conditionally loading to prevent a script from running on the server?

Yep! In your package.json

{
  "system": {
    "envs": {
      "server-development": {
        "map": {
          "whatever": "@empty"
        }
      },
      "server-production": {
        "map": {
          "whatever": "@empty"
        }
      }
    }
  }
}

Then in your code:

var whatever = require("whatever");

if(typeof whatever === "undefined") {
  // Don't use whatever
}

I need to use tinymce. I understand that it doesn’t play nicely with SSR. I am trying to suppress it there but it’s still crashing the server with the same error. I think I am following your advice from above.

I refer to it thus:

My main package.json includes this:

Am I understanding your instructions for suppressing SSR correctly?

Any thoughts on how to debug?

Thanks.

Hellllloooooooo…

Can anyone lend a brother a hand?

Hi @tqwhite, did you also check to see if tinymce wasn’t undefined in the code that uses it?

Something like:

import tinymce from "node_modules/tinymce/";
if (typeof tinymce !== "undefined") {
  // Use tinymce
}