Simple Dojo i18n implementation - javascript

I just recently started learning dojo for personnal use and for experience. So far, I have been doing the tutorials on various dojo stuff (on their website and over the web) and I have been "struggling" with implementing a concrete infrastructure for more complex application (or good practice). I have find one interesting project (https://github.com/csnover/dojo-boilerplate) and article (http://www.sitepen.com/blog/2011/05/04/what-is-the-best-way-to-start-a-dojo-project/). With that, I think my first problem is resolved. Correct me, if I'm wrong.
I feel like the tutorial on i18n is missing concrete implementation. For example, I would like to add i18n on the dialog box from the boilerplate project.
define([ 'dojo/_base/declare', 'dijit/Dialog' ], function (declare, Dialog) {
return declare(Dialog, {
title: 'Hello World',
content: 'Loaded successfully!'
});
});
Here, My project hierarchy is:
AS you can see, I create my own nls folder for my application and store for different (lang-locale) my "strings". Now, how do I specify the locale content on title or content for my dialog code above. I have done recently i18n on ruby on rails (with the concept of MVC) and depending on my view I had to create for this specific view a file for localization (.yml). I know that RoR and Dojo are really not the same thing, but does a widget (could be compared to my view) and so each widget needs to have their own localization... I have come accross 2 tutorials, first and second. Maybe, I'm reading it all wrong.
I have something like this right now, but it doesn't work.. What am I missing?
dojo.requireLocalization("app", "dialog");
define([ 'dojo/_base/declare', 'dijit/i18n' 'dijit/Dialog' ], function (declare, Dialog) {
i18n: dojo.i18n.getLocalization("app", "dialog"),
return declare(Dialog, {
title: i18n.title,
content: i18n.content
});
});
Thank you.
EDIT:
define([ 'dojo/_base/declare', 'dojo/i18n!app/nls/labels', 'dijit/Dialog' ], function (declare, labels, Dialog) {
return declare(Dialog, {
title: labels.title,
content: labels.content
});
});
I have no error now, but my labels.title is empty...?
EDIT(1): I forgot to add the root on the default nls folder.

Here's an example of how I have built some dialogs with localization.
directory structure
myApp\
dialog\
myDialog.js
nls\
dialog.js
fr-ca\
dialog.js
myDialog.js
define("myApp/dialog/myDialog", [
"dojo", "dijit/Dialog", "dojo/i18n",
"dojo/i18n!./nls/dialog" // this is a relative path to the
// dialog.js from myDialog.js
], function(dojo, Dialog) {
var i18n = dojo.i18n.getLocalization(
"myApp.dialog", // this is the directory path to the nls folder
"dialog" // this is the file
);
return declare(Dialog, {
title: i18n.title,
content: i18n.content
});
});

Related

Using D3.JS and the sankey plugin in a require.js module

I'm trying to build a requirejs module giving client code the options to render stuff with d3.js. The first plugin I want to use is a sankey diagram.
My module so far:
define(['d3'], function(ignore) {
console.log("bef", d3);
require(['sankey.js']);
console.log("aft", d3);
d3.sankey();
return {
...
d3: d3,
renderSankey: function(options) {
...
}
}
The sankey.js script uses the global d3 variable and adds the function sankey(). (I tried both btw, define(['d3'], function(ignore) and define(['d3'], function(d3), exactly the same result).
The error: TypeError: d3.sankey is not a function, no matter if I try to call it directly as the code shows or like this.d3.sankey() in the renderSankey function.
The console output says (both times, before and after the require(...) call:
sankey: d3.sankey()
No matter what I try, it won't work. I feel like I missed something JS specific about shadowing, but why is there a sankey function, when I console.log the object and a row later, when I try to call I get an error? What am I doing wrong?
info:
I'm using this inside a splunk html dashboard, this is maybe important...
I don't want the client code to import the dependicies (with about 100 plugins to come, this would be a pain)
When I just copy the content of the sankey.js into my module, everything works fine
/edit: Here is the Require configuration (given by the Splunk Dashboard)
require.config({
baseUrl: "{{SPLUNKWEB_URL_PREFIX}}/static/js",
waitSeconds: 0 // Disable require.js load timeout
});
The require call you are using to load sankey is asynchronous. It will launch the loading of sankey but by the time require returns, sankey is not loaded. You should change your code to:
define(['d3', 'sankey'], function (d3) {
d3.sankey();
I take it that d3 also leaks the symbol d3 in the global space but AMD modules should not rely on global symbols unless these are part of the runtime environment (e.g. window, document).
You also need to set your RequireJS configuration to make sankey dependent on d3 because the define above does not by itself ensure that d3 will load before sankey. So you need this in your configuration:
shim: {
sankey: ['d3']
}
This makes sankey dependent on d3. (Note that shim can only be used to affect the loading of files that are not proper AMD module. sankey does not call define to register itself, and thus is not a proper AMD module, and we can use shim for it.)
Also, module names should generally not have .js in them so when you want to load the plugin, load it as sankey, not sankey.js.
Okay, I think #Louis and I just misunderstood each other. This may be caused by my own stupidity, since I wasn't aware that a configuration of require.js can be done anywhere (and not only once in the root file). How ever, to still get the Splunk specific part I post this answer (instead of accepting Louis'):
I added a new app to my splunk environment to (a viz app). I actually configure the dependencies first (in the by other splunk apps loadable d3-viz module):
require.config({
paths: {
'd3': '../app/D3_Viz/d3', // d3.js
'sankey': '../app/D3_Viz/sankey', // sankey.js
'XYZ': 'all the paths go here'
},
shim: {
'sankey': ['d3'],
'XYZ': ['d3'],
// all the dependecies go here
}
});
define(['splunkjs/ready!', 'jquery', 'd3'],
function(mvc, $, ignore) {
var d3Vis = {
...
renderSankey: function(options) {
// load dependencies dynamically
require(['sankey'], function() {
// actually render things
});
},
renderXYZ: function(options) {
require(['XYZ'], function() {
...
});
},
...
}
}
return d3Vis;
All my dependencies can be configured in the viz-app (and not in the client code using the app, this has been my fundamental missunderstanding of require.js); the only thing to do is loading the app/viz as a whole (in this example in a HTML dashboard:
require([
"splunkjs/mvc",
"splunkjs/mvc/utils",
"splunkjs/mvc/tokenutils",
"underscore",
"jquery",
"splunkjs/mvc/simplexml",
"splunkjs/mvc/headerview",
"splunkjs/mvc/footerview",
...
"../app/D3_Viz/viz"
],
function(
mvc,
utils,
TokenUtils,
_,
$,
DashboardController,
HeaderView,
FooterView,
...
d3Viz
){
... splunk specific stuff
// No dependencies have to be configured
// in the client code
d3Viz.renderSankey({...});
}
);

Knockout with require.js - not looking for knockout in my configured 'paths' section

I’m developing a multi-page app, using requirejs to manage my javascript libs / dependencies.
My idea is that i'll have a main.js that holds the config, and then an .js file for each page that needs it, for example "register.js"
My require config is in javascripts/main.js
requirejs.config({
baseUrl: '/javascripts',
waitSeconds: 200,
paths: {
'async': 'lib/require.async',
'jquery': 'lib/jquery-1.7.2.min',
'knockout': 'lib/knockout-3.0.0'
});
I’ve got a knockout view model that looks like this:
javascripts/viewModels/userDetailsViewModel.js
define(['knockout'], function(ko) {
return function() {
var self = this;
self.name = ko.observable();
self.email = ko.observable();
});
My ‘entry point’ is javascripts/register.js
require(['./main', 'knockout', 'viewModels/userDetailsViewModel'], function(main, ko, userDetailsViewModel) {
ko.applyBindings(new userDetailsViewModel());
});
On my register.html page, i’ve got the script reference like this:
<script data-main="/javascripts/register" src="/javascripts/lib/require.js"></script>
When my page loads, I get these errors in the console:
GET http://localhost:3000/javascripts/knockout.js 404 (Not Found)
and
Uncaught Error: Script error for: knockout
I’m not sure why it’s looking for knockout.js - I’ve specified knockout in the paths section of my config, to look in lib/knockout-3.0.0
My dir structure is:
javascripts/
Most of my pages js files go here
javascripts/viewModels
Has knockout viewmodels
javascripts/lib
Contains knockout, jquery, requirejs etc...
The problem is that RequireJS will execute the call require(['./main', 'knockout', 'viewModels/userDetailsViewModel'] without a configuration. Yes, ./main is listed before knockout but there is no order guarantee between the dependencies passed in a single require call. RequireJS may load ./main first, or knockout first. And even if ./main were loaded first by this specific call, I believe it would not have any impact on how the other modules loaded by this call would load. That is, I think this require would operate on the basis of the configuration that existed at the time it was called, and that any configuration changes caused by the modules it loads would take effect only for subsequent require calls.
There are many ways to fix this. This should work:
require(['./main', function(main) {
require(['knockout', 'viewModels/userDetailsViewModel'], function(ko, userDetailsViewModel) {
ko.applyBindings(new userDetailsViewModel());
});
});
Or you might want to restructure your files and what you pass to data-main so that your requirejs.config is loaded and executed before your first require call. Here's an example of restructuring. Change your entry point to be /javascripts/main.js:
<script data-main="/javascripts/main.js" src="/javascripts/lib/require.js"></script>
Change /javascripts/main.js so that it contains:
requirejs.config({
baseUrl: '/javascripts',
waitSeconds: 200,
paths: {
'async': 'lib/require.async',
'jquery': 'lib/jquery-1.7.2.min',
'knockout': 'lib/knockout-3.0.0'
});
require(['knockout', 'viewModels/userDetailsViewModel'], function(ko, userDetailsViewModel) {
ko.applyBindings(new userDetailsViewModel());
});
And remove /javascripts/register.js. This would be one way to do it. However, it is hard for me to tell whether this would be what you want in your specific project, because I do not know the whole project. The way to restructure for your specific project really depends on what other pages might use RequireJS, what information is common to all pages, what is specific to each page, whether you use a template system to produce HTML, etc.

Deploying a Durandaljs/Phonegap app to android

I need help, I'm at my wits end here.
I have developed an app with durandaljs and I have been doing my testing on the browser, so far it works like a dream! Then I try to deploy the app to an android device and all hell breaks loose!
I followed the directions here and here to optimize my build with Weyland using Node. I get a main-built.js file inside my app folder and a build folder pretty much containing everything in app and lib folders (eek!!!). So first question, what do I copy to my phonegap android www folder; the contents of build or the contents of my initial application?
The guide says the build includes a custom version of Almond so I don't need to load requirejs in my app (yay!) and I only have to change the requirejs script tag to point to main-built (which one the one in ./build/app/ or the one in ./app??).
So I use the one in my initial app, now I have just two script references in my index.html
<script type="text/javascript" src="phonegap.js"></script>
<script src="./app/main-built.js"></script>
Back tracking a bit, in my main.js; I kept thinking how to make sure the app didn't start until after deviceready was triggered and the dom had loaded. I saw this youtube video and I took a cue from the guys code so now my main.js before optimization looks in part like this
requirejs.config({
paths: {
'text': '../lib/require/text',
'async': '../lib/require/async',
'domReady': '../lib/require/domReady',
'durandal': '../lib/durandal/js',
'plugins': '../lib/durandal/js/plugins',
'transitions': '../lib/durandal/js/transitions',
'knockout': '../lib/knockout/knockout-2.3.0',
'bootstrap': '../lib/bootstrap/js/bootstrap',
'jquery': '../lib/jquery/jquery-1.9.1',
'jpanelmenu': '../lib/jpanelMenu/jquery.jpanelmenu.min'
},
shim: {
'bootstrap': {
deps: ['jquery'],
exports: 'jQuery'
}
}
});
define(['domReady', 'durandal/system', 'durandal/app', 'durandal/viewLocator', 'scripts/dealtag'], function (domReady, system, app, viewLocator, dealtag) {
domReady(function () {
var useragent = navigator.userAgent.toLowerCase();
if (useragent.match(/android/) || useragent.match(/iphone/) || useragent.match(/ipad/) || useragent.match('ios')) {
document.addEventListener('deviceready', onDeviceReady, false);
}
else {
onDeviceReady('desktop');
}
});
function onDeviceReady(desktop) {
if (desktop !== 'desktop')
cordova.exec(null, null, 'SplashScreen', 'hide', []);
app.title = 'My App';
app.configurePlugins({
router: true,
dialog: true,
widget: true
});
app.start().then(function () {
//Replace 'viewmodels' in the moduleId with 'views' to locate the view.
//Look for partial views in a 'views' folder in the root.
viewLocator.useConvention();
....my own initialization code
});
}
});
So back to after optimisation, I copy the contents of build + my css folder which wasn't included in the optimisation to the www folder, run phonegap local build android; then I deploy this to my android device. The first page of the app (registration page) loads ok and in logcat I can see a lot of the same stuff that I could see in the browser console including some of my own console.log check points. However, when I click on a link to go to any other page, all I see is a white screen on my device and the following in never ending errors in logcat
Does anyone have any idea what I am doing wrong coz loads of people seem to be developing for android using durandaljs. Thanks in advance for any help you can give.
I figured out what the cause of the "confusion" is. If you have been doing your routing with Sammyjs before migrating to durandaljs, you may also come into this confusion. With sammyjs, having a route in the href attribute of your view acts like a link and automatically navigates you to the corresponding route. Something like this
My Page
or with a ko binding
<a data-bind="attr: {href: '#mypage/' + id}"></a>
This makes durandaljs go berserk! In durandal create a function in you view model like this
define(['plugins/router', 'knockout'], function (router, ko) {
var myviewmodel= function () {
this.list= ko.observableArray();
}
....
myviewmodel.prototype.showItem = function (item) {
router.navigate('mypage/' + item.id);
}
return myviewmodel;
});
and then in your view you can have
<a data-bind="click: $parent.showDeals"> <!-- if within a list context -->
or
<a data-bind="click: showDeals">
I hope this saves someone the needless grief I had to go through for hours unend trying to figure out what the problem was.

RequireJS plugin: load timeouts experienced when using plugin

Using RequireJS I'm building an app which make extensive use of widgets. For each widget I have at least 3 separate files:
request.js containing code for setting up request/response handlers to request a widget in another part of my application
controller.js containing handling between model and view
view.js containing handling between user and controller
Module definition in request.js:
define(['common/view/widget/entity/term/list/table/controller'],
function(WidgetController) { ... });
Module definition in controller.js:
define(['common/view/widget/entity/term/list/table/view'],
function(WidgetView) { ... });
Module definition of view.js is:
define(['module','require'],function(module,require) {
'use strict';
var WidgetView = <constructor definition>;
return WidgetView;
});
I have lots of these little situations as above in the case of widgets I have developed. What I dislike is using the full path every time when a module is requiring another module and both are located in the same folder. I'd like to simply specify as follows (assuming we have a RequireJS plugin which solves this for us):
define(['currentfolder!controller'],
function(WidgetController) { ... });
For this, I have written a small plugin, as I couldn't find it on the web:
define({
load: function (name, parentRequire, onload, config) {
var path = parentRequire.toUrl('.').substring(config.baseUrl.length) + '/' + name;
parentRequire([path], function (value) {
onload(value);
});
}
});
As you might notice, in its basic form it looks like the example of the RequireJS plugins documentation.
Now in some cases, the above works fine (e.g. from the request.js to the controller.js), but in other cases a load timeout occurs (from controller.js to view.js). When I look at the paths which are generated, all are proper RequireJS paths. Looking at the load timeouts, the following is logged:
Timestamp: 13-09-13 17:27:10
Error: Error: Load timeout for modules: currentfolder!view_unnormalized2,currentfolder!view
http://requirejs.org/docs/errors.html#timeout
Source File: http://localhost/app/vendor/requirejs/require.js?msv15z
Line: 159
The above log was from a test I did with only loading the view.js from controller.js using currentfolder!view in the list of modules in the define statement. Since I only requested currentfolder!view once, I'm confused as to why I both see currentfolder!view_unnormalized2 and currentfolder!view in the message.
Any idea as to why this might be happening?
My answer may not answer your primary questions, but it will help you achieve what you're trying to do with your plugin.
In fact, Require.js support relative paths for requiring modules when using CommonJS style. Like so:
define(function( require, exports, module ) {
var relativeModule = require("./subfolder/module");
module.exports = function() {
console.log( relativeModule );
};
});

Using Dust in require.js

Sorry for posting another "how do you use * with require.js" question, but I can't seem to get dust.js working in my require project. I've googled around and other people are definitely getting dust to work with require. My configuration is fairly standard, but I can't find anyone having the same problems as I'm seeing.
I'm using the version of dust 0.3.0 from here:
https://github.com/akdubya/dustjs/blob/master/dist/dust-core-0.3.0.js
here is my config:
requirejs.config({
//exceptions:
paths: {
'jquery' : 'lib/require-jquery.1.9.1',
'jquery.mockjax' : 'lib/jquery.mockjax.1.5.1',
'dust' : 'lib/dust.0.3.0'
},
//Shims are required for jQuery plugins.
shim: {
'jquery.mockjax': {
deps: ['jquery'],
exports: 'jQuery.fn.mockjax'
},
'dust': {
exports: 'dust'
}
}
});
this is how I include dust in my module:
define( function( require ) {
var _d = require('dust')
, _template1 = require('text!template/basicmodal.html');
function render(key,callback){
var compiled = _d.compile("Hello {name}!", key);
_d.loadSource(compiled);
_d.render(key, {name: "Fred"}, callback);
}
return {
render : render,
If I set a breakpoint within the render function I can see that _d does contain the dust object, but for some reason it doesn't have all its methods. In particular its 'compile' method is missing which causes my code to fail.
Does anyone with a better understanding of dust know what I might be missing here?
Please see if using https://github.com/akdubya/dustjs/blob/master/dist/dust-full-0.3.0.js instead helps you.
Dust is now supported by Linkedin. If you want to compile some dust template, maybe this might help you https://github.com/linkedin/dustjs/wiki/Dust-Tutorial#compiling-a-dust-template.
I am not an expert in JS but it could be useful to use Backbone in addition to dust + require.
Chek out this (I don't have enough reputation to put more links) : http://weatherlabs.com/2012/10/12/backbone-underscore-and-dust/
You can precompile the template and have it wrapped in a define call using this npm module:
https://npmjs.org/package/grunt-dust-require

Categories