External component Knockout and Require - javascript

I'm just learning knockout and I need some help. My menucomponent won't show up (i.e. it should say "Boo ya ka sha"). In chrome (under the scripts inspector) I can see that htmlString is set properly (i.e. to the contents of components/menu.html). Help, please.
index.html
<html>
<head>
...
<script data-main="js/app" src="js/require.js"></script>
</head>
<body>
...
<menucomponent></menucomponent>
</body>
</html>
js/app.js
requirejs.config({
"baseUrl": "js",
"paths": {
"app": "app",
"jquery": "jquery-2.2.3",
"knockout":"knockout-3.4.0",
"main": "main",
"menu": "../components/menu",
"text": "require-text"
}
});
// make a component called menucomponent
require(['knockout'], function(ko) {
ko.components.register('menucomponent', {require: 'menu'});
});
// Load the main app module to start the app
requirejs(["main"]);
components/menu.js
define(['knockout','text!./menu.html'], function(ko, htmlString) {
function menuViewModel() {
this.myMessage = ko.observable("Boo ya ka sha");
}
return { viewModel: menuViewModel,
template: htmlString
};
});
components/menu.html
<div data-bind='text: myMessage'></div>

Knockout supports amd registration for viewmodel only, so try this:
In js/app.js, change your component registration to:
require(['knockout','text!./menu.html'], function(ko, htmlString) {
ko.components.register('menucomponent', {
viewModel: { require: 'menu'},
template: htmlString
});
and change components/menu.js to:
define(['knockout'], function(ko) {
function menuViewModel() {
this.myMessage = ko.observable("Boo ya ka sha");
}
return menuViewModel;
});

Related

Importing hash-set module not working for typescript/systemjs?

I'm working on an aurelia project, using typescript to create the javascript. Now I tried to add another custom library, 'hash-set' (using jspm install npm:hash-set --save). However I can't seem to actually use this package (using systemjs as loader).
My document structure is like:
\
dist\
src\
app.html
app.js
main.js
jsp_packages\
npm\
hash-set#1.0.1\
node_modules\
index.html
config.js
package.json
tsconfig.json
The important files (I think, please state in the comments if I miss something):
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body aurelia-app="src/main">
<script src="jspm_packages/system.js"></script>
<script src="config.js"></script>
<script>
SystemJS.import('aurelia-bootstrapper');
</script>
</body>
</html>
app.ts
This is compiled to app.js as prebuilt step. Using es2015 as target configuration.
import {hashSet} from 'hash-set';
export class App {
public myText: string;
hashFn(value) {
return value.toString();
}
constructor() {
alert("oh");
const h = hashSet;
const StringSet = hashSet(this.hashFn);
alert('oh2');
}
}
config.js
System.config({
defaultJSExtensions: true,
transpiler: false,
paths: {
"*": "dist/*",
"github:*": "jspm_packages/github/*",
"npm:*": "jspm_packages/npm/*"
},
meta: {
"bootstrap": {
"deps": [
"jquery"
]
}
},
map: { /*lots of aurelia and other library stuff*/
"hash-set": "npm:hash-set#1.0.1"
}
}
});
And it's also listed in package.json # {"jspm":{"dependencies":"hash-set": "npm:hash-set#^1.0.1"}}}
Now when I try to run above code (typescript compiles to app.js as prebuilt step), the app.js/app.ts loads, as expected. (frankly removing the hash-set specific code makes everything work as expected).
However during construction "oh" is shown, but "oh2" is never. Debugging over the code shows that "hashSet" is "undefined". Which leads me to believe that that systemjs isn't including the hash-set correctly?
Am I missing something?
EDIT: digging into the generated js (app.js) file I notice something weird:
define(["require", "exports", "hash-set"], function (require, exports, hash_set_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class App {
hashFn(value) {
return value.toString();
}
constructor() {
alert('oh');
const h = hash_set_1.hashSet;
const StringSet = hash_set_1.hashSet(this.hashFn);
alert('oh2');
}
}
exports.App = App;
});
//# sourceMappingURL=app.js.map
While debugging, hash_set_1 is actually of the type I expect hash_set_1.hashSet to be. Actually manually editing the javascript to not use hash_set_1.hashSet but rather just hash_set_1 works.
Trying
import hashSet from 'hash-set'; (notice lack of {}) changes the generated javascript offending line to const StringSet = hash_set_1.default(this.hashFn); which is still not correct (default isn't defined either).
If you look at the code, you'll see it is exported as:
module.exports = function hashSet(hashFn) {
Doing import { hashSet } from 'hash-set'; cannot work because the export would have to be module.exports.hashSet = ....
It should work if you do:
import hashSet = require("hash-set");

Configure route to display pages in a SplitApp SAPUI5

I have been trying to add pages to my SplitApp from other views (view.js) with their own drivers, but doing so causes the following error:
Sap-ui-core.js: 174 Uncaught Error: failed to load 'view / GestionDePlanta.view.js' from openui5-1.40.8 / resources / view / GestionDePlanta.view.js: 404 - Not Found (...)
I think it has to do with the paths to access that view the moment it boots
Sap.ui.view ({id: "GestionDePlanta", viewName: "view.GestionDePlanta", type: sap.ui.core.mvc.ViewType.JS});
Anyone know any way to do it?
I was following a tutorial, which I found easy to understand, but apparently this is incomplete :
http://blog.mypro.de/2014/02/14/add-page-to-ui5-boilerplate/
Thank you so much.
Index.html
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta charset="UTF-8">
<title>DatosMaestros</title>
<script id="sap-ui-bootstrap"
src="/openui5-1.40.8/resources/sap-ui-core.js"
data-sap-ui-libs="sap.m,sap.ui.commons,sap.ui.table"
data-sap-ui-theme="sap_bluecrystal"
data-sap-ui-compatVersion="edge"
</script>
<link rel="stylesheet" type="text/css" href="css/style.css">
<script type="text/javascript" src="js/utilityFunction.js"></script>
<script type="text/javascript" src="js/xml2json.js"></script>
<script>
sap.ui.localResources("P_GestionDePlanta");
var url = "http://services.odata.org/V4/Northwind/Northwind.svc/Employees",
oModel = new sap.ui.model.json.JSONModel(url, true);
sap.ui.getCore().setModel(oModel);
var app = new sap.m.SplitApp("appId",{mode:sap.m.SplitAppMode.ShowHideMode});
var master = sap.ui.view({id:"GestionDePlanta", viewName:"view.GestionDePlanta", type:sap.ui.core.mvc.ViewType.JS});
app.addMasterPage(master);
var detail = sap.ui.view({id:"PlanAbastecimiento", viewName:"view.PlanAbastecimiento", type:sap.ui.core.mvc.ViewType.JS});
app.addDetailPage(detail);
app.placeAt("content");
</script>
</head>
<body class="sapUiBody" id="content">
</body>
</html>
Component.js
sap.ui.define([
"sap/ui/core/UIComponent",
"sap/ui/Device",
"P_GestionDePlanta/model/models"
], function(UIComponent, Device, models) {
"use strict";
return UIComponent.extend("P_GestionDePlanta.Component", {
metadata: {
manifest: "json"
},
/**
* The component is initialized by UI5 automatically during the startup of the app and calls the init method once.
* #public
* #override
*/
init: function() {
// call the base component's init function
UIComponent.prototype.init.apply(this, arguments);
// set the device model
this.setModel(models.createDeviceModel(), "device");
}
});
});
Manifiest.json
{
"_version": "1.1.0",
"sap.app": {
"_version": "1.1.0",
"id": "P_GestionDePlanta",
"type": "application",
"i18n": "i18n/i18n.properties",
"applicationVersion": {
"version": "1.0.0"
},
"title": "{{appTitle}}",
"description": "{{appDescription}}",
"sourceTemplate": {
"id": "ui5template.basicSAPUI5ApplicationProject",
"version": "1.32.0"
}
},
"sap.ui": {
"_version": "1.1.0",
"technology": "UI5",
"icons": {
"icon": "",
"favIcon": "",
"phone": "",
"phone#2": "",
"tablet": "",
"tablet#2": ""
},
"deviceTypes": {
"desktop": true,
"tablet": true,
"phone": true
},
"supportedThemes": [
"sap_hcb",
"sap_bluecrystal"
]
},
"sap.ui5": {
"_version": "1.1.0",
"rootView": {
"viewName": "P_GestionDePlanta.view.GestionDePlanta",
"type": "JS"
},
"dependencies": {
"minUI5Version": "1.30.0",
"libs": {
"sap.ui.core": {},
"sap.m": {},
"sap.ui.layout": {}
}
},
"contentDensities": {
"compact": true,
"cozy": true
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "P_ProcesamientoDeArchivos.i18n.i18n"
}
}
},
"resources": {
"css": [{
"uri": "css/style.css"
}]
}
}
}
Folders
Image:
Well that tutorial you followed is quite outdated. You would want to use a Component based structure (You have a component in your sample but never use it). The official SAPUI5/OpenUI5 Developer Guide especially the walkthroughs are mostly up to date. I recommend you to do the walkthrough there.
Back to your problem: UI5 has its own way to resolve modules (≈javascript files) from names.
In your example you give UI5 the name of a view: "view.GestionDePlanta". view is the namespace and GestionDePlanta is the module name. So UI5 looks for the namespace view and – lacking any matching namespace-path-mappings – it defaults to the UI5 framework itself using the same path as the sap-ui-core.js you defined in the bootstrap <script> element: ./openui5-1.40.8/resources/view.
To add such an namespace-path-mapping you can use the sap.ui.localResources() function.
With your sap.ui.localResources("P_GestionDePlanta"); declaration UI5 would look for a module "P_GestionDePlanta.view.GestionDePlanta" in the folder ./P_GestionDePlanta/view.
Thats better already but your folder structure should look like this then:
+ P_GestionDePlanta
+ view
GestionDePlanta.view.js
+ controller
GestionDePlanta.controller.js
index.html
An other way would be to use jQuery.sap.registerModulePath() to add an namespace-path-mapping. With jQuery.sap.registerModulePath("P_GestionDePlanta", "./"); UI5 would look for a module "P_GestionDePlanta.view.GestionDePlanta" in the folder ./view.
With that you can keep your folder structure:
+ view
GestionDePlanta.view.js
+ controller
GestionDePlanta.controller.js
index.html
But either way you have to supply the full namespace starting with P_GestionDePlanta everywhere like so:
sap.ui.view({id:"GestionDePlanta", viewName:"P_GestionDePlanta.view.GestionDePlanta", type:sap.ui.core.mvc.ViewType.JS});

Automatic reference of local *.js and *.css files into index.html with grunt

I intend to develop an angularJS client where I will use angular components. This will lead to multiple .js/.css files.
In order to avoid manually referencing each newly added js/css file I intend to use a grunt-include-source task.
The problem is that, after configuring the Gruntfile.js, the „grunt includeSource” task runs, returning „Done, without errors.” status but no update is made in the index.html file.
My project structure is the one presented in the attached picture (I use WebStorm as IDE).
My index.html file is the following:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>RavenApp</title>
<!-- include: "type": "css", "files": "*.css" -->
</head>
<body>
<!-- bower:js -->
<script src="../bower_components/angular/angular.js"></script>
<script src="../bower_components/angular-route/angular-route.js"></script>
<script src="../bower_components/angular-sanitize/angular-sanitize.js"></script>
<script src="../bower_components/angular-mocks/angular-mocks.js"></script>
<script src="../bower_components/jquery/dist/jquery.js"></script>
<script src="../bower_components/underscore/underscore.js"></script>
<!-- endbower -->
<!-- include: "type": "js", "files": "*.js" -->
</body>
</html>
My Gruntfile.js is the following:
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-wiredep');
grunt.loadNpmTasks('grunt-include-source');
grunt.initConfig({
wiredep: {
target: {
src: 'app/index.html'
}
},
includeSource: {
options: {
basePath: 'app',
templates: {
html: {
js: '<script src="{filePath}"></script>',
css: '<link rel="stylesheet" type="text/css" href="{filePath}" />'
}
},
app: {
files: {
'app/index.html': 'app/index.html'
}
}
}
}
});
};
Could anyone indicate me what I have done wrong?
Thank you.
We don't need to write templates key under includeSource key:
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-wiredep');
grunt.loadNpmTasks('grunt-include-source');
grunt.initConfig({
wiredep: {
target: {
src: 'app/index.html'
}
},
includeSource: {
options: {
basePath: 'app',
app: {
files: {
'app/index.html': 'app/index.html'
}
}
}
}
});
};
HTML code is enough for including js and css:
<!-- include: "type": "css", "files": "*.css" -->
<!-- include: "type": "js", "files": "*.js" -->

Requirejs optimizer with angularjs

I am trying to make a sample application using angularjs, ui-router and requirejs to lazyload my controllers. It works good locally but I want to write requirejs optimizer for production environment, Tried using grunt:requirejs tool for this but it didnt work for me. It doesnt even give any script loading error or something..
<--index.html--!>
<!DOCTYPE html>
<html style="overflow-x:hidden">
<head>
</head>
<body class="no-padding">
<div data-ui-view="header"></div>
<div data-ui-view="module"></div>
<script data-main="app/main" src="bower_components/requirejs/require.js"></script>
</body>
</html>
//main.js
require.config({
baseUrl: "",
// Paths for just the core application and its controllers/factories/services
paths: {
"jquery": "bower_components/jquery/dist/jquery",
"angular": "bower_components/angular/angular.min",
"angular-ui-router": "bower_components/angular-ui-router/release/angular-ui-router.min",
"app": "app/app",
"underscore": "node_modules/underscore/underscore-min",
},
shim: {
//Tell requirejs to pipe in angular"s return variable as "angular"
"angular": {
exports: "angular"
},
},
// Say we have a dep on App, so it gets loaded
deps: ["app", 'lib']
});
//lib.js
define([
'jquery',
'underscore',
], function($){
});
//app.js
define([
'angular',
'angular-ui-router'
], function(){
var app = angular.module('app', ['ui.router']);
//lazy loading
var loadController = function(path){
return ['$q', function($q){
var defered = $q.defer();
require([path], function(){
defered.resolve();
});
return defered.promise;
}]
};
app.config(['$controllerProvider', function($controllerProvider){
app.registerController = $controllerProvider.register;
}]);
app.config(['$stateProvider', function($stateProvider){
//registering controller
// defining states
$stateProvider.state('app', {
url: '/',
views: {
'header':{
templateUrl:"<div>{{title}}</div>",
controller:"appCtrl"
},
'module':{
template:"<div>{{title}}</div>",
controller:"homeCtrl"
}
},
resolve: {
loadApp: loadController('../app/controllers/header'),
loadHome: loadController('../app/controllers/home')
}
});
}]);
angular.bootstrap(document, ['app']);
return app;
});
//home.js
define(function(){
angular.module('app').registerController('homeCtrl', ['$scope', '$state', function($scope,$state){
$scope.title = 'CONTENT';
}]);
});
//header.js
define(function(){
angular.module('app').registerController('appCtrl', ['$scope', '$state', function($scope,$state){
$scope.title = 'HEADER';
}]);
});
I was using grunt:requirejs task to compile an optimized dist file "main.js" and grunt copy for modified index.html inside dist directory:
grunt:requirejs and modified index.html -->
grunt.initConfig({
//...
requirejs: {
compile: {
options: {
name: 'node_modules/almond/almond',
mainConfigFile: 'app/main.js',
out: 'dist/main_opt.js',
baseUrl: './'
}
}
},
//...
})
<--dist/index.html--!>
<!DOCTYPE html>
<html style="overflow-x:hidden">
<head>
</head>
<body class="no-padding">
<div data-ui-view="header"></div>
<div data-ui-view="module"></div>
<script src='bower_components/requirejs/require.js'></script>
<script>
require.config({
paths: {
//Comment out this line to go back to loading
//the non-optimized main.js source file.
"main_opt": "dist/main_opt"
}
});
require(["main_opt"]);
</script>
</body>
</html>
When loading dist/index.html it gives me nothing, no error in browser, just doesnt work, if it was giving me script loading error for controllers it might have made any sense but its not. Completely clueless here..
There is nothing required in main.js file. You must require at least one of files declared in paths. Do it as follow:
require(['app'], function () {
console.log('app required successfully');
});
Put this lines of code in your main.js file. I hope it may help to find errors:
requirejs.onError = function (err) {
console.error('[require error] type: ', err.requireType, ' ,modules: ' + err.requireModules);
throw err;
};

Code coverage with Dojo, Mocha and Blanket - always reporting 1 covered line

I have tried setting up Blanket.js to get reports on the code coverage of our Dojo app, which we are testing using Mocha. So far, Blanket seems to load, instrument the correct files, and it also seems to figure out which lines it should look for. However, every single file/module in the report shows up as having one line tested. It looks to me like it's the top "define" line. Here's a not minimal example, but it minimally represents our app setup and reproduces the problem.
File structure
▾ dojo-mocha-blanket/
▾ modules/
GUIWidget.html
GUIWidget.js
▾ test/
▾ lib/
blanket_mocha.js // from the blanket /dist
chai.js
mocha-blanket.js // adapter as per instructions
mocha.css
mocha.js
▾ spec/
GUIWidget.js
testrunner.html
index.html
Dojo setup
<html>
<head>
<title>Dojo-Mocha-Blanket</title>
</head>
<body>
<div id="GUIWidgetContainer"></div>
<script>
var dojoConfig = {
async: true,
baseUrl: "",
modulePaths: {
"modules": "modules"
}
};
</script>
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.10.3/dojo/dojo.js"></script>
<script>
require([
"dojo/dom",
"modules/GUIWidget"
], function (dom, GUIWidget) {
var widget = new GUIWidget({}, "GUIWidgetContainer");
});
</script>
</body>
</html>
Module to test
define([
"dojo/_base/declare",
"dijit/form/Select",
"dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin",
"dijit/_WidgetBase",
"dojo/text!modules/GUIWidget.html"
], function(
declare,
Select,
_TemplatedMixin,
_WidgetsInTemplateMixin,
_WidgetBase,
template
) {
return declare("GUIWidget", [_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin], {
templateString: template,
addNumbers: function(lhs, rhs) {
return lhs + rhs;
},
postCreate: function() {
this.inherited(arguments);
this.header.innerHTML = this.addNumbers(40, 2);
}
});
});
Test setup
<html>
<head>
<title>Mocha spec runner</title>
<link rel="stylesheet" href="lib/mocha.css">
</head>
<body>
<div id="mocha"></div>
<script src="lib/mocha.js"></script>
<script
src="lib/blanket_mocha.js"
data-cover-adapter="lib/mocha-blanket.js"
data-cover-only="/modules"></script>
<script src="lib/chai.js"></script>
<script>
mocha.setup("bdd");
mocha.globals(["dojo", "dijit"]);
mocha.setup();
expect = chai.expect;
</script>
<script>
var dojoConfig = {
async: true,
packages : [{
name : "modules",
location : "/modules"
} ]
};
</script>
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.10.3/dojo/dojo.js"></script>
<script>
require(
{
paths: {
"spec": "/test/spec"
}
},
["spec/GUIWidget"], function() {
mocha.run();
});
</script>
</body>
</html>
The test
define([
"modules/GUIWidget"
], function(
GUIWidget
) {
describe("GUIWidget", function() {
var widget;
beforeEach(function() {
widget = new GUIWidget();
widget.startup();
});
it("Should set header to 42", function() {
expect(widget.header.innerHTML).to.equal("42");
});
it("Should add numbers", function() {
expect(widget.addNumbers(1, 2)).to.equal(3);
})
afterEach(function() {
widget.destroyRecursive();
});
});
});
Results
(larger image here)
Apologies for the long code and all the bootstrapping stuff, but I've found the setup to be non-trivial :) If anyone has any ideas of why this might be happening, I'd be all ears. Thanks!

Categories