Component gets error this.getView is not a function - javascript

I'm going over the sapui5 walkthrough tutorials and have managed to get to step 9 where it teaches you how to use Component.js file in your app.
Now, prior to using Component.js everything in the app was working fine. However, once I try to use component I get this error:
Uncaught TypeError: this.getView is not a function
Referring to UIComponent.js line 6. Even though my component file is just called Component.js. I also get:
GET http://localhost:58736/InvoicesApp/invoicesapp/Component-preload.js 404 (Not Found)
But I'm not sure they're related
Here is my index.html
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv='Content-Type' content='text/html;charset=UTF-8'/>
<script src="resources/sap-ui-core.js"
id="sap-ui-bootstrap"
data-sap-ui-libs="sap.m"
data-sap-ui-theme="sap_bluecrystal"
data-sap-ui-bindingSyntax="complex"
data-sap-ui-compatVersion="edge"
data-sap-ui-preload="async"
>
</script>
<!-- only load the mobile lib "sap.m" and the "sap_bluecrystal" theme -->
<script>
sap.ui.localResources("invoicesapp");
sap.ui.getCore().attachInit(function () {
new sap.ui.core.ComponentContainer({
name : "invoicesapp"
}).placeAt("content");
});
</script>
</head>
<body class="sapUiBody" id="content"/>
</html>
My Component.js
sap.ui.define([
"sap/ui/core/UIComponent",
"sap/ui/model/json/JSONModel",
"sap/ui/model/resource/ResourceModel"
], function (UIComponent, JSONModel, ResourceModel) {
"use strict";
return UIComponent.extend("invoicesapp.Component", {
metadata: {
rootView:"invoicesapp.view.App"
},
init : function () {
// call the init function of the parent
UIComponent.prototype.init.apply(this, arguments);
// set data model on view
var oData = {
recipient : {
name : "World"
}
};
var oModel = new JSONModel(oData);
this.getView().setModel(oModel);
// set i18n model on view
var i18nModel = new ResourceModel({
bundleName: "invoicesapp.i18n.i18n"
});
this.getView().setModel(i18nModel, "i18n");
}
});
});
My controller
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/m/MessageToast",
"sap/ui/model/json/JSONModel",
"sap/ui/model/resource/ResourceModel"
], function (Controller, MessageToast, JSONModel, ResourceModel) {
"use strict";
return Controller.extend("invoicesapp.controller.App", {
onShowHello : function () {
// read msg from i18n model
var oBundle = this.getView().getModel("i18n").getResourceBundle();
var sRecipient = this.getView().getModel().getProperty("/recipient/name");
var sMsg = oBundle.getText("helloMsg", [sRecipient]);
// show message
MessageToast.show(sMsg);
}
});
});

In the component you can't call this.getView() because there is no getView(), the api docs at https://openui5beta.hana.ondemand.com/#docs/api/symbols/sap.ui.core.Component.html
Instead, you set the model directly on the component itself. In other words, just call
this.setModel(oModel);
and
this.setModel(i18nModel, "i18n");
By the way: in the walktrough it's done the same way.

this.getView() will not be defined in component.js as view is yet to be instantiated.
You can simply set model with just this.setModel(oModel) and you can set names model by this.setModel(oModel,"modelName");

I also had the same problem while going through the walk through.
View is not instantiated in the Component.
You can rewrite as
this.setModel("myModel);
And for i18n
this.setModel(i18nModel,"i18n");
This error was there in walk through but now its fixed it seems. There were few errors with the SAP UI5 Tutorials while I was learning but they have fixed most of them now.
This is also a good source to start with UI5. Almost similar to Demokit.
UI Development Toolkit for HTML5 (SAPUI5)
Happy Coding
Febin Dominic

Related

How to import js file from url and initialize a class in React?

I want to embed PitchPrint app on a React website. They have a vanilla html/js integration tutorial here. I added script tags with links to jQuery and their app file in my index.html file, as they require and then created a separate jsx file that suposed to return a button witch opens the app. The problem is, when I try to build, it throws an error 'PitchPrintClient' is not defined witch suposed to come from their files.
My index.html file:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://pitchprint.io/rsc/js/client.js"></script>
<title>App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
My jsx file:
import React from 'react';
const AppButton = () => {
let _launchButton = document.getElementById('launch_btn');
let _previewDiv = document.getElementById('pp_preview_div');
let _loaderDiv = document.getElementById('pp_loader_div');
_launchButton.setAttribute('disabled', 'disabled');
var ppclient = new PitchPrintClient({
apiKey: 'f80b84b4eb5cc81a140cb90f52e824f6', //Kinldy provide your own APIKey
designId: '3d8f3899904ef2392795c681091600d0', //Change this to your designId
custom: true
});
//Function to run once the app is validated (ready to be used)
var appValidated = () => {
_launchButton.removeAttribute('disabled'); //Enable the Launch button
_launchButton.onclick = () => ppclient.showApp(); //Attach event listener to the button when clicked to show the app
_loaderDiv.style.display = 'none'; //Hide the loader
};
//Function to run once the user has saved their project
var projectSaved = (_val) => {
let _data = _val.data; //You can console.log the _data varaible to see all that's passed down
if (_data && _data.previews && _data.previews.length) {
_previewDiv.innerHTML = _data.previews.reduce((_str, _prev) => `${_str}<img src="${_prev}">`, ''); //Show the preview images
}
};
ppclient.on('app-validated', appValidated);
ppclient.on('project-saved', projectSaved);
return <div>
<div id="pp_loader_div"><img src="https://pitchprint.io/rsc/images/loaders/spinner_new.svg" /></div>
<button id="launch_btn" >Launch Designer</button>
<div id="pp_preview_div"></div>
</div>;
};
export default AppButton;
PS: I know getElementById does not realy work with react, I'll deal with that later, for now I just want to initialize this app.
that's because the component is not mounted yet.
you need to call document.getElementById once the component is mounted, and in order to access dom elements inside the component you need to call it inside useEffect hook
useEffect(() => {
let _launchButton = document.getElementById("launch_btn");
let _previewDiv = document.getElementById("pp_preview_div");
let _loaderDiv = document.getElementById("pp_loader_div");
_launchButton.setAttribute("disabled", "disabled");
var ppclient = new PitchPrintClient({
apiKey: "f80b84b4eb5cc81a140cb90f52e824f6", //Kinldy provide your own APIKey
designId: "3d8f3899904ef2392795c681091600d0", //Change this to your designId
custom: true,
});
//Function to run once the app is validated (ready to be used)
var appValidated = () => {
_launchButton.removeAttribute("disabled"); //Enable the Launch button
_launchButton.onclick = () => ppclient.showApp(); //Attach event listener to the button when clicked to show the app
_loaderDiv.style.display = "none"; //Hide the loader
};
//Function to run once the user has saved their project
var projectSaved = (_val) => {
let _data = _val.data; //You can console.log the _data varaible to see all that's passed down
if (_data && _data.previews && _data.previews.length) {
_previewDiv.innerHTML = _data.previews.reduce(
(_str, _prev) => `${_str}<img src="${_prev}">`,
""
); //Show the preview images
}
};
ppclient.on("app-validated", appValidated);
ppclient.on("project-saved", projectSaved);
}, []);
Read more about React hooks and their constraints.
https://reactjs.org/docs/hooks-intro.html
also make sure to access global variables using window.PitchPrintClient
also, make sure your app is mounted once the dom is ready. by moving your script tags to the end of the body tag or using jquery on ready callback.
PS: The answer is not considering the best practices of writing react components, but I encourage you to use refs and minimize accessing to dom as much as you could.

Forcing AngularJS directive to link outside digest cycle

This refers to an Angular 1 application.
If the DOM is modified outside the context of my angular application, I know I can use angular.element(document.body).scope().$apply() to force the whole app to re-render, including the newly injected content.
However my directives never seem to link.
So in the example below, the markup <message></message> should render Hello World, but when it is injected manually, then digest applied, the link method never appears to run.
https://jsbin.com/wecevogubu/edit?html,js,console,output
javascript
var app = angular.module('app', [])
app.directive('message', function() {
return {
template: 'Hello, World!',
link: function() {
console.log('message link')
}
}
})
document.getElementById('button').addEventListener('click', function() {
document.getElementById('content').innerHTML = '<message>default content</message>'
var scope = window.angular.element(document.body).scope()
scope.$apply()
})
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
</head>
<body ng-app="app">
inside app:
<message></message>
outside app:
<button id="button">Print another message</button>
<div id="content"></div>
</body>
</html>
According to the docs, you can do this with angular.injector
angular.injector allows you to inject and compile some markup after the application has been bootstrapped
So the code for your example could be:
document.getElementById('button').addEventListener('click', function() {
var $directive = $('<message>default message</message>');
$('#content').append($directive);
angular.element(document.body).injector().invoke(function($compile) {
var scope = angular.element($directive).scope();
$compile($directive)(scope);
});
})
Hope this is what you are looking for!

Getting Monaco to work with Vuejs and electron

I'm interested in using the Monaco editor in a Vue.js backed Electron project.
Thus far:
Microsoft provides an Electron Sample (which I've run and works correctly)
There are a variety of vue.js npm repos for monaco - yet none of them seem to fully support Electron right out of the box.
The one that looks most promising is vue-monaco but I've run into issues correctly integrating it.
AMD Require?
This is the code from the Microsoft sample for using with Electron
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Monaco Editor!</title>
</head>
<body>
<h1>Monaco Editor in Electron!</h1>
<div id="container" style="width:500px;height:300px;border:1px solid #ccc"></div>
</body>
<script>
// Monaco uses a custom amd loader that overrides node's require.
// Keep a reference to node's require so we can restore it after executing the amd loader file.
var nodeRequire = global.require;
</script>
<script src="../node_modules/monaco-editor/min/vs/loader.js"></script>
<script>
// Save Monaco's amd require and restore Node's require
var amdRequire = global.require;
global.require = nodeRequire;
</script>
<script>
// require node modules before loader.js comes in
var path = require('path');
function uriFromPath(_path) {
var pathName = path.resolve(_path).replace(/\\/g, '/');
if (pathName.length > 0 && pathName.charAt(0) !== '/') {
pathName = '/' + pathName;
}
return encodeURI('file://' + pathName);
}
amdRequire.config({
baseUrl: uriFromPath(path.join(__dirname, '../node_modules/monaco-editor/min'))
});
// workaround monaco-css not understanding the environment
self.module = undefined;
// workaround monaco-typescript not understanding the environment
self.process.browser = true;
amdRequire(['vs/editor/editor.main'], function() {
var editor = monaco.editor.create(document.getElementById('container'), {
value: [
'function x() {',
'\tconsole.log("Hello world!");',
'}'
].join('\n'),
language: 'javascript'
});
});
</script>
</html>
The module I'm using allows for something like this:
<template>
<monaco-editor :require="amdRequire" />
</template>
<script>
export default {
methods: {
amdRequire: window.amdRequire
// Or put this in `data`, doesn't really matter I guess
}
}
</script>
I can't seem to figure out how to get the correct amdRequire variable defined in Electon + vue. I believe if i can conquer this everything else becomes simple.
The Electron FAQ mentions something about this (i think): I can not sue jQuery/RequireJS/Meteor/AngularJS in Electron
Sample Code
I put a sample project up on GitHub https://github.com/jeeftor/Vue-Monaco-Electron with the "offending" component being in ./src/renderer/components/Monaco.vue
Summary
How can I get this Monaco Editor to load correctly inside of a Vue.js component that will be run inside electron?
Thanks for any help you can offer.
I'm doing nearly the same, just without the extra vue-monaco component. After struggling quite a bit, I could solve the problem:
function loadMonacoEditor () {
const nodeRequire = global.require
const loaderScript = document.createElement('script')
loaderScript.onload = () => {
const amdRequire = global.require
global.require = nodeRequire
var path = require('path')
function uriFromPath (_path) {
var pathName = path.resolve(_path).replace(/\\/g, '/')
if (pathName.length > 0 && pathName.charAt(0) !== '/') {
pathName = '/' + pathName
}
return encodeURI('file://' + pathName)
}
amdRequire.config({
baseUrl: uriFromPath(path.join(__dirname, '../../../node_modules/monaco-editor/min'))
})
// workaround monaco-css not understanding the environment
self.module = undefined
// workaround monaco-typescript not understanding the environment
self.process.browser = true
amdRequire(['vs/editor/editor.main'], function () {
this.monaco.editor.create(document.getElementById('container'), {
value: [
'function x() {',
'\tconsole.log("Hello world!");',
'}'
].join('\n'),
language: 'javascript'
})
})
}
loaderScript.setAttribute('src', '../node_modules/monaco-editor/min/vs/loader.js')
document.body.appendChild(loaderScript)
}
I've just taken the electron-amd sample and adjusted it a bit. I call the loadMonacoEditor function in the components' created function.
In order to not get the Not allowed to load local resource: file:///C:/.../node_modules/monaco-editor/min/vs/editor/editor.main.css problem, you also have to set
webPreferences: {
webSecurity: false
}
in your instance of the BrowserWindow.

Defining Polymer element after importing ES6 code via System.js

I'm creating an HTML element using Polymer, and I want it to be able to work with an ES6 class I've written. Therefore, I need to import the class first and then register the element, which is what I do:
(function() {
System.import('/js/FoobarModel.js').then(function(m) {
window.FoobarModel = m.default;
window.FoobarItem = Polymer({
is: 'foobar-item',
properties: {
model: Object // instanceof FoobarModel === true
},
// ... methods using model and FoobarModel
});
});
})();
And it works well. But now I want to write a test HTML page to display my component with some dummy data:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="/bower_components/webcomponentsjs/webcomponents.js"></script>
<script src="/bower_components/system.js/dist/system.js"></script>
<script>
System.config({
map:{
traceur: '/bower_components/traceur/traceur.min.js'
}
});
</script>
<link rel="import" href="/html/foobar-item.html">
</head>
<body>
<script>
(function() {
var data = window.data = [
{
city: {
name: 'Foobar City'
},
date: new Date('2012-02-25')
}
];
var view;
for (var i = 0; i < data.length; i++) {
view = new FoobarItem();
view.model = data[i];
document.body.appendChild(view);
}
})();
</script>
</body>
</html>
Which isn't working for one simple reason: the code in the <script> tag is executed before Polymer registers the element.
Thus I'd like to know if there's a way to load the ES6 module synchronously using System.js or even better, if it's possible to listen to a JavaScript event for the element registration (something like PolymerElementsRegistered)?
I've tried the following without success:
window.addEventListener('HTMLImportsLoaded', ...)
window.addEventListener('WebComponentsReady', ...)
HTMLImports.whenReady(...)
In the app/scripts/app.js script from the polymer starter kit, they use auto-binding template and dom-change event
// Grab a reference to our auto-binding template
var app = document.querySelector('#app');
// Listen for template bound event to know when bindings
// have resolved and content has been stamped to the page
app.addEventListener('dom-change', function() {
console.log('Our app is ready to rock!');
});
Also check this thread gives alternatives to the polymer-ready event.

famo.us use of requirejs from tutorial

I have what is probably a basic question around how the famo.us slideshow tutorial is using requirejs (tutorial here). I don't know much about requirejs, and it's sort of a tertiary tool for the purposes of this tutorial, but I did do a bit of reading to try and wrap my head around what it's doing, but that seemed to only leave me more perplexed.
From the index.html file
<head>
<title>famo.us App</title>
<meta name="viewport" content="width=device-width, maximum-scale=1, user-scalable=no" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<!-- shims for backwards compatibility -->
<script type="text/javascript" src="http://code.famo.us/lib/functionPrototypeBind.js"></script>
<script type="text/javascript" src="http://code.famo.us/lib/classList.js"></script>
<script type="text/javascript" src="http://code.famo.us/lib/requestAnimationFrame.js"></script>
<!-- module loader -->
<script type="text/javascript" src="http://code.famo.us/lib/require.js"></script>
<!-- famous -->
<link rel="stylesheet" type="text/css" href="http://code.famo.us/famous/0.3.0/famous.css" />
<script type="text/javascript" src="http://code.famo.us/famous/0.3.0/famous.min.js"></script>
<!-- app code -->
<link rel="stylesheet" type="text/css" href="css/app.css" />
<script type="text/javascript">
require.config({
baseUrl: './src/'
});
require(['main']);
</script>
</head>
As I understand it we are doing two things from a requirejs perspective:
a) indicating all our modules will be located in the "src" folder
b) indicating the inital js file for execution will be 'src/main.js'
here is main.js:
define(function(require, exports, module) {
var Engine = require('famous/core/Engine');
// import the AppView class using require
var AppView = require('views/AppView');
var mainContext = Engine.createContext();
// create a new instance of app view
var appView = new AppView();
// add the instance to the context
mainContext.add(appView);
});
And here is one iteration of the appview module:
define(function(require, exports, module) {
var View = require('famous/core/View');
var Surface = require('famous/core/Surface');
var Transform = require('famous/core/Transform');
var StateModifier = require('famous/modifiers/StateModifier');
// import the SlideshowView class
var SlideshowView = require('views/SlideshowView');
function AppView() {
View.apply(this, arguments);
// create a new instance of slideshow view
var slideshowView = new SlideshowView();
// add the instance to app view
this.add(slideshowView);
}
AppView.prototype = Object.create(View.prototype);
AppView.prototype.constructor = AppView;
AppView.DEFAULT_OPTIONS = {};
module.exports = AppView;
});
My confusion is with these module imports in appview:
var View = require('famous/core/View');
Since we set the base folder to the 'src' folder, I would think require would expect this module to be in 'src/famous/core', though there is no such directory. In fact, the famous directory is a few levels above the src folder in the file hierarchy. So how did require find the famous directory?
'famous/core/View' is just a module name, not a path as you would expect. It is defined in dist/famous.js:5009. They used the / convention probably as a namespace.
For more info about defining a module with a name go to the requirejs docs.
In short terms, one could do the following in a file named foo.js :
define('foo/bar', function () {
return {
name: 'bar'
}
});
define('foo/baz', function () {
return {
name: 'baz'
}
});
And if that file foo.js is loaded at some point, one could require the following, without the need of having a folder/file structure foo/bar.js and foo/baz.js:
require(['foo/bar', 'foo/baz', function (Bar, Baz) {
console.log(Bar.name, Baz.name)
});
p.s. i strongly suggest you read the docs, requirejs is just awesome

Categories

Resources