We're looking into updating our Durandal app to Aurelia (finally). Our biggest concern right now is code re-use. We'll write TypeScript for the view logic, but there's a lot of complex clientside media access (webRTC stuff) that has taken a lot of time to get working as raw JavaScript AMD modules.
I've seen questions asked about using AMD ViewModels; I'm asking about using AMD modules within new aurelia viewmodels.
As a simple example, I have a mediaDeviceScanner.js module:
define(["modules/logger"], function (logger) {
'use strict';
const mediaDeviceScanner = {};
mediaDeviceScanner.scanDevices = function() {
return navigator.mediaDevices.getUserMedia({video:true}).then(function(stream) {
return stream;
}).then(function(stream) {
return navigator.mediaDevices.enumerateDevices().then(function (availableDevices) {
const mediaDevices = [];
for (let i = 0; i < availableDevices.length; ++i) {
const deviceCandidate = availableDevices[i];
if ((deviceCandidate.kind === "videoinput") && deviceCandidate.deviceId != "default" && deviceCandidate.deviceId != "communications") {
mediaDevices.push({ label: deviceCandidate.label, kind: (deviceCandidate.kind == "videoinput" ? "Camera " : "Microphone ") + (mediaDevices.length + 1), id: deviceCandidate.deviceId });
}
}
return mediaDevices;
}).catch(function (error) {
logger.log(error, logger.logLevels.warning, logger.features.webcam, logger.features.webcam);
});
})
}
return mediaDeviceScanner;
});
What's the happy-path to calling mediaDeviceScanner.scanDevices from within an aurelia viewmodel?
In my Durandal vm, I have this:
define(["amd/mediaDevices/mediaDeviceScanner"],function(mediaDeviceScanner){
mediaDeviceScanner.scanDevices().then(function(devices){alert('woot')});
});
I'd like to get a gauge on what kind of "reuse costs" there will be before I evaluate "framework shift" costs.
There is an old question regarding this topic. Using AMD module as an Aurelia ViewModel
It essentially boils down to which loader you are using. Aurelia CLI by default uses requirejs but recently got an update to support SystemJS. With that it would be possible to, as described in the referenced question, create wrappers for your existing code. The wrappers could be very thin and maybe even generalized to save lots of boilerplate
--EDIT--
Out of interest I've just quickly tried it with a new CLI Project, based on SystemJS.
After scaffolding I've placed your example module in scripts/amd/media-device-scanner.js
Removed the logger dependency, since its not provided in your example
Go to src/app.js and inside the constructor add this code:
System.import('../scripts/amd/media-device-scanner.js').then((result) => {
result.scanDevices();
}
Still works like a charm ;)
Related
I'm working with JS modules bundled with Webpack.
Suppose I have a common module, used everywhere. This module require another module, let's say it is an output for errors
var err = require("./errors.js");
var common = {
doSomething: function() {
var a = 1;
if (a == 1) {
err.output('An error occured');
}
}
}
module.exports = common;
Now this error management module could be somthing like
var $ = require("jquery");
var errors = {
output: function(msg) {
$('<div class="someClass">'+msg+'</div>).appendTo('body');
}
}
module.exports = errors;
And finally I have my entry point:
var common = require("./common.js");
common.doSomething();
My question is: since I have got two applications, let's call them backend and frontend, I would like different implementation of errors.output, because it's view-bounded. Maybe in backend I want to use a class different from "someClass", or maybe I don't want to use a DIV, but a P, or else.
I have multi entry points, actually. So I wouldn't like to have each of them passing around lots of callbacks or configuration. I would like to have only one implementation of errors module for all the entry point of the backend and only one for all the entry point of the frontend, without any code repetition.
This is something that I would implement with interface, in PHP.
Which is the best approach for Javascript?
Thank you
Supposing I have a library written in Javascript to be used in a MEAN stack application.
I wish to use my library both from NodeJS and within Angular.
To remain idiomatic I would like the library to appear as a Module inside node and as a Service within Angular.
the only way I can think to do this is to do something like:
var myLibrary = function(){
var myLib = {};
myLib.myFunc1 = function() {
//Do some cool stuff
};
return myLib;
};
if (typeof module !== 'undefined') {
module.exports = myLibrary();
} else{
var app = angular.module('myApp.services', []);
app.factory('myLibrary',myLibrary);
}
This will work, but it has at least two significant limitations:
The namespace is polluted with the variable "myLibrary"
If within the library I want to use other node modules or Angular services then I can't
I would like to know if anyone knows of a better solution, or if I should give up on using Angular services and use one of the libraries that allows client side Node module functionality.
Having done a bit of searching about this looks like the best solution:
https://gist.github.com/sevcsik/9207267
Currently I load all my javascript with a pattern similar to the code below. One file would reflect a single page or component in a site. For example, if I had a contact page the associated javascript file might be:
(function () {
$(function () {
contactForm.init();
locationMap.init();
});
var contactForm = {
init: function () {
... Do form manipulation here
}
};
var locationMap = {
init: function () {
... Do map manipulation here
}
};
})();
This is fine for smaller sites but as a site grows and functionality needs to be shared across pages or certain components develop a reliance on each other (e.g. the options in the contact form might change based upon the selection location map) this pattern shows it's limitations.
My question is, how can I improve this to allow for growing complexity and reliance between modules?
Good improvement will be organizing your code in namespaces and use The Revealing Module pattern:
var contact = contact || {};
var contact.form = (function () {
// private functions
function privateFunction() {}
// public functions
function init() {
privateFunction()
... Do form manipulation here
}
return {
init : init
};
})();
but for more complex applications i suggest to look for some MVVM architecture framework (i prefer AngularJS)
You should use a module loader, like requirejs or browserify. By requirejs you have to use AMD style module definitions, by browserify you have to use commonjs style module definitions. Commonjs is not compatible with browsers by default (it is for nodejs), so you have to build browser compatible js files from it (browserify does that). In your case requirejs might be a better solution (if you are not familiar with nodejs), but I suggest you read more about both.
Another possible solution is using ES6 modules. Currently you have to build by them as well, but later they will be supported by every browser I think.
I've got a Backbone/AMD app and what I would like to do is have a model/object be fetched in the main module when the app loads, then be either accessible to or be able to be loaded into subsequent modules without the overhead of re-fetching it (it's a permissions model and thus is required pretty much everywhere).
Is there a clean way of doing this?
Simply make it its own dependency:
define(["backbone", "text!api/v1/user/permissions"],
function(Backbone, permissionJSON) {
return new Backbone.Model(JSON.parse(permissionJSON));
});
If you require a module for which you want to keep state you can do as follows
stateMod.js
require(['jquery', 'lib1','lib2'], function($, l1,l2) {
var thisVariableHoldsStateBetweenModules = "initValue"
var thisIsTheAPIOfYourModule = function(newValue) {
thisVariableHoldsStateBetweenModules = newValue
}
var getInternalState = function () {
return thisVariableHoldsStateBetweenModules
}
return {
set: thisIsTheAPIOfYourModule,
get: getInternalState
}
}
})
What you get back when you require this module is thisIsAPIOfYourModule, so you can just use it to change thisVariableHoldsStateBetweenModules:
otherMod.js
require(['stateMod'], function(stateMod) {
stateMod.set("Hello world")
})
and later on, in module nextMod.js:
require(['stateMod'], function(stateMod) {
stateMod.get() // outputs Hello World
})
requirejs saves the object returned from the first time it is required, and this why you don't re-load (network wiise) d3js and jQuery or other common libs every time you need them in a module, otherwise it would be pretty crap.
Give this a go and let me know how it worked for you.
I'm trying to use a library -- Google's libphonenumber -- in my require application that is not AMD. What is the best way to consume this? I know I can create a module like this:
define(['module'], function (module) {
// insert and return library code here.
});
But that doesn't seem great. It seems like I would have to refactor some of their code to get that working (e.g., turn it all into an object and return that object). I see a lot of libraries using a different pattern where they use an immediately invoked function that defines the module on the window object and returns it.
(function() {
var phoneformat = {};
window.phoneformat = phoneformat;
if (typeof window.define === "function" && window.define.amd) {
window.define("phoneformat", [], function() {
return window.phoneformat;
});
}
})();
** UPDATE **
Is there any reason not to just do this?
define(['lib/phoneformatter'], function(phoneformatter) {
});
I get access to all of my methods but now it seems they are global because I did not wrap the library in a define...
Use RequireJS's shim. It'll look something like this
requirejs.config({
shim: {
'libphonenumber': {
exports: 'libphonenumber' // Might not apply for this library
}
}
});
This will load libphonenumber and put its variables in the global scope
This ended up working for me:
define(['module'], function (module) {
// insert and return library code here.
});
I am not entirely sure why 'module' was necessary. But it doesn't work without it. Also, I just returned an object and attached functions to it like so:
return {
countryForE164Number: countryForE164Number,
nextFunction: nextFunction,
// more functions as needed.
}
There is not much in the way of documentation for using 'module' but from what I can ascertain: Module is a special dependency that is processed by requireJS core. It gives you information about the module ID and location of the current module. So it is entirely possible that I messed up the paths in config.