I want to load a html content with RequireJS like this:
define(function (require) {
"use strict";
return function (id) {
var url = 'text!screens/' + id + '.html';
var html = require(url);
}; });
But I get this error:
Error: Module name "text!screens/home.html_unnormalized2" has not been loaded yet for context: _
If I try it in this way:
define(function (require) {
"use strict";
return function () {
var html = require('text!screens/home.html');
}; });
everything is ok. But this approach isn't very nice due to hardcore tipped url. How can I solve this?
Inline parametric require calls can only run asynchronously for modules that have not been loaded yet, as is your case. The principle is (also note url is in an array):
var url = 'text!screens/' + id + '.html';
require([url],
function(text) {
// use text
},
function(err) { // OPTIONAL BUT GOOD PRACTICE
// handle error
}
);
This has the inconvenience of not beign able to return the value immediately. Also take a look at the principle of promises (implemented by many libraries, jQuery too).
Related
In Node.JS, I'm trying to "reload" a file. I have the following code:
delete require.cache[require.resolve("./pathToFile/" + restartModule)]
with restartModule being the file name, but I'm not sure how I could add the file back using require() and define it as the variable restartModule. For example, if restartModule is myModule, how would I add myModule.js into the var called myModule? Or maybe there's an easier way to simply "reload" a file in the cache?
You could do something simple enough like this:
function reloadModule(moduleName){
delete require.cache[require.resolve(moduleName)]
console.log('reloadModule: Reloading ' + moduleName + "...");
return require(moduleName)
}
var restartModule= reloadModule('./restartModule.js');
You would have to call reloadModule every time you want to reload the source though. You could simplify by wrapping like:
var getRestartModule = function() {
return reloadModule('./restartModule.js');
}
getRestartModule().doStuff();
Or
var reloadModules = function() {
return {
restartModule = reloadModule('./restartModule.js');
};
}
var modules = reloadModules();
modules.restartModule.doStuff();
Or:
var reloadModules = function(moduleList) {
var result = {};
moduleList.forEach((module) => {
result[module.name] = reloadModule(module.path);
});
}
var modules = reloadModules([{name: 'restartModule', path: './restartModule.js'}]);
modules.restartModule.doStuff();
You could even put the module reload on a setInterval so modules would get loaded every N seconds.
Then there's always nodemon: https://nodemon.io/ this is useful in development, whenever a source file changes it will reload your server.
You just use it like node, e.g.
nodemon server.js
I have a subgenerator that uses the name from the package.json. Now I want to test that function and wrote a before() that is supposed to create a dummy package.json for the test.
Problem is that the subgenerator cannot read the dummy json file.
test file:
before(function (done) {
helpers.run(path.join( __dirname, '../addcomponent'))
.inDir(path.join( __dirname, './tmp'), function(dir) {
fs.copyTpl(
path.join(__dirname, '../app/templates/_package.json'),
dir + 'package.json',
{ ProjectName: 'foo' }
);
var test = fs.readJSON(dir + 'package.json');
console.log('test: ' + test); // returns the object
console.log('test.name: ' + test.name); // returns the correct name
})
.withArguments(['foo'])
.withPrompts(prompts)
.withOptions(options)
.on('end', done);
});
but in my sub-generator:
var memFs = require('mem-fs');
var editor = require('mem-fs-editor');
var store = memFs.create();
var fs = editor.create(store);
...
init: function() {
this.pkg = this.fs.readJSON('package.json');
console.log('this.pkg: ' + this.pkg); // returns undefined
}
// or
init: function() {
this.on('ready', function() {
this.pkg = this.fs.readJSON('package.json');
console.log('this.pkg: ' + this.pkg); // returns undefined
});
}
// or
anyOther: function() {
this.pkg = this.fs.readJSON('package.json');
console.log('this.pkg: ' + this.pkg); // returns undefined
}
The whole setup can be found here: https://travis-ci.org/markusfalk/generator-kickstart/builds/58892092
thanks for any help
Edit: I'll keep the old answer underneath and that's probably relevant to most people running into this issue, but not to you.
The idea behind mem-fs is to have an in memory store. It doesn't write anything to disk automatically. As so, it keep the state in the mem-fs instance. In this case, you're creating your own mem-fs instance, while yeoman use another instance. This mean the file you write is never seen by Yeoman (and never written to disk).
For you, the fix would be to use the generator instance provided as the first parameter of the ready event.
helpers.run(path.join( __dirname, '../addcomponent'))
.on('ready', function (generator) {
generator.fs.write('file.txt', 'foo');
});
Another option is to use the node.js sync fs methods. (fs.writeFileSync(), etc)
My guess is you're using this.fs.readJSON() inside your generator constructor.
The constructor is initialized before the ready event is triggered. This mean you read the file before it is actually written.
The usual fix is to never read inside the constructor. You can delay this step until the initializing phase where the inDir() (or the ready event) callback has run.
As a side note, you should use inTmpDir() rather than inDir()
I'm developing a SPA using Knockout.js V3 and RequireJS.
I have ko components written like this:
define(['text!settings.html'],
function( htmlString) {
'use strict';
function SettingsViewModel(params) {
...
}
// Return component definition
return {
viewModel: SettingsViewModel,
template: htmlString
};
});
Now i want to support localization and for that I have duplicated html for each supported language, so for example:
en/settings.html
de/settings.html
se/settings.html
I would like to let the user change a language and refresh the app with the new language, is it possible to instruct require text plugin to add the language prefix to all html, so when i write:
text!settings.html
it will actually load:
text!de/settings.html
Not sure if you can let the text plugin prefix the urls. What you might be able to do is create a custom template loader:
var templateFromLanguageUrlLoader = {
loadTemplate: function(name, templateConfig, callback) {
if (templateConfig.languageUrl) {
// Language from config or default language
var lang = templateConfig.lang || 'de';
var fullUrl = lang + '/' + templateConfig.languageUrl;
$.get(fullUrl, function(markupString) {
ko.components.defaultLoader.loadTemplate(name, markupString, callback);
});
} else {
// Unrecognized config format. Let another loader handle it.
callback(null);
}
}
};
// Register it
ko.components.loaders.unshift(templateFromLanguageUrlLoader );
Then your component would look something like this:
define([],
function() {
'use strict';
function SettingsViewModel(params) {
...
}
// Return component definition
return {
viewModel: SettingsViewModel,
template: {
languageUrl: 'settings.html',
language: 'nl' // overwrite default
}
};
});
Eventually i went with a different solution, i just added a variable to the path:
define(['text!' + globals.bundlePath + 'settings.html']
This variable is initialized from session storage (and get's a default if nothing found) and so when a user changes language, i keep it in session storage and refresh the app, and that way the pages are now loaded with the new language.
I'm using breeze.js to get some serverdata on the client, this works fine. To create a more modular application i want to create a 'dataservice' to bundle the breeze query's in 1 module to be included in other modules as a dependency.
this is the module:
define(function () {
var serviceName = window.location.protocol + "//" + window.location.host + '/breeze/n0uk', // route to the Web Api controller
manager = new breeze.EntityManager(serviceName);
function getPoster(callsign) {
var query = breeze.EntityQuery.from('Posters').where("Callsign","==",callsign);
return manager.executeQuery(query);
};
return {
getPoster:getPoster
};
});
I've created a testmodule to test the function:
define(["common/dataService"],function(n0uk) {
alert("home/index geladen");
n0uk.getPoster("pe1l").then(function(data) {
alert(data.Name);
}
);
});
sadly there is no data returned. I'm a breeze and require newby ( and js experience is also not topnotch). Can someone direct me in the right direction?
In your test module you're executing alert(data.Name). The breeze query will return an object with an array property named results so you probably want to use something like this:
define(["common/dataService"],function(n0uk) {
alert("home/index geladen");
n0uk.getPoster("pe1l")
.then(function(data) {
alert(data.results.length.toString() + ' items returned');
})
.fail(function(reason) { alert(reason); });
});
Other things to try:
Use your browser's F12 tools (or visual studio) to set a breakpoint and inspect the query result.
Use fiddler's "inspectors" tab to confirm your api call is returning data:
For more info on what the executeQuery method returns, look here (scroll to "executeQuery"):
http://www.breezejs.com/sites/all/apidocs/classes/EntityManager.html
I'm using Handlebars with Node, and that works fine:
require('handlebars');
var template = require('./templates/test-template.handlebars');
var markup = template({ 'some': 'data' });
console.log(markup);
That works fine. However, I need to register and use a custom helper in my template. So, now my code looks like this:
var Handlebars = require('handlebars');
Handlebars.registerHelper('ifEqual', function(attribute, value) {
if (attribute == value) {
return options.fn(this);
}
else {
return options.inverse(this);
}
});
var template = require('./templates/test-template.handlebars');
var markup = template({ 'some': 'data' });
console.log(markup);
But now when I run my script, I get
Error: Missing helper: 'ifEqual'
So: how can I define and use a custom helper in Node?
I figured it out. I needed to do this:
var Handlebars = require('handlebars/runtime')['default'];
One what's really cool is, this even works in the browser with Browserify.
However, I found that an even better way (and probably the "correct" way) is to precompile Handlebars templates via (shell command):
handlebars ./templates/ -c handlebars -f templates.js
Then I do this:
var Handlebars = require('handlebars');
require('./templates');
require('./helpers/logic');
module.exports.something = function() {
...
template = Handlebars.templates['template_name_here'];
...
};
Here is the way i did it.
I guess nowadays its a bit different.
const Handlebars = require('handlebars');
module.exports = function(){
Handlebars.registerHelper('stringify', function(stuff) {
return JSON.stringify(stuff);
});
};
then i made a little script to call require on all the helpers just so they get ran.
// Helpers Builder
let helpersPath = Path.join(__dirname, 'helpers');
fs.readdir(helpersPath, (err, files) => {
if (err) {throw err;}
files.filter((f) => {
return !!~f.indexOf('.js');
}).forEach((jsf) => {
require(Path.join(helpersPath, jsf))();
});
});
OR the simple way
require('./helpers/stringify')();
in fact you dont even have to export it as a function you can just not export anything at all and just call require from another js file with out the function params at the end.