Interfacing old system with new Angular 4 frontend - javascript

We are in the process of converting our systems over to Angular 4. Since the conversion will take some time, we were hoping to convert our components one by one into angular and have angular wrap around our old pages and run as they do now in conjunction with the new angular components. In our current system is run in multiple frames, header, navigation, working area. We are removing frames in our new angular system.
Our old system primarily replaces the contents of the the working area with a new html page which is entirely composed on the server.
I've seen code to launch an iframe from a component.html and then attach a listener to catch onClicks to anything on that frame. I want to evaluate those click actions from the old system then determine whether to allow the href (etc..) to continue as it use to or re-rout it to the new angular router (ie: this.router.navigate(['/customer']) ) and stop the requested event. I'm able to get most of this to work, but when my new function runs it doesn't have access to needed pieces of the component, methods and properties are not found. (things like str.replace(), str.match() which are normal javascript methods used in conjunction with regexp , but also properties included in the component, like router.navigate)
Can this be done or is there a better way to accomplish this?
I've also looked at the alt-angularjs-migration-using-iframes-demo which I run into the same situation.
Here is how I am currently trying to do this:
contact.component.html
<div class="iBody1" (click)="onRightClick($event)">
<iframe #iframe [src]="'https://www.volgistics.com/ex/Cp.dll?ACT=7' +
urlUserStr | safeUrl" (load)="myFunction()"
id="work1" name="WorkArea" scrolling="no" marginwidth="0" marginheight="0"
frameborder="no" >
<p>Your browser does not support iframes.</p>
</iframe>
</div>
contact.component.ts
myFunction() {
let doc = this.iframe.nativeElement.contentDocument ||this.iframe.nativeElement.contentWindow;
if (typeof doc.addEventListener !== undefined) {
doc.addEventListener("click", this.iframeClickHandler, false)
} else if (typeof doc.attachEvent !== undefined) {
doc.attachEvent("onclick", function (this) {this.iframeClickHandler(this)} )
}
}
iframeClickHandler(e): void {
e.preventDefault();
let vars : string = e.target;
let rx = new RegExp("([^?=&]+)(=([^&]*))?", "g");
let xx = rx.exec( vars );
// let var1 : String = vars.match(/(\?|\&)([^=]+)\=([^&]+)/g).toString(); // match is undefined if used
// let var2 : String = JSON.parse('{"' + decodeURI(var1.replace(/\?/g, "").replace(/&/g, "\",\"").replace(/=/g,"\":\"")) + '"}') // replace is undefined if used
this.router.navigate(['/customer/100100109']); // Uncaught TypeError: Cannot read property 'navigate' of undefined
}
Thanks for any help in advance.
Rob Thompson

I'm not sure if this helps you in any way, but we have achieved quite satisfactory results with splicing angular on top of an existing application by ignoring angular's routing and just slowly replacing old server-site generated html with angular components.
If I understand your case correctly, you want hrefs from within the iframe to navigate to pages in angularJS. The approach we used was we'd actually let it navigate to the page, and then the html of that page would just be
<our-new-component></our-new-component>
which would display our component
Once the transformation is done, you can switch to full angular incl. routing.

Related

How to assert element property value with Espresso-web?

I'm working on a subclass of default Android WebView with some additional functionality (content-filtering) on the top. So i override shouldInterceptRequest(...) and prevent loading of some resource (let's say images with filename "image.png").
Now i want to test this functionality with the help of Espresso-Web. So i do start embedded http server (with WireMock) and load some webpage that uses that "image.png" and it's expected to be blocked.
How can i assert the state of DOM element with id "someId"?
I expected to have smth like:
onWebView().
.withElement(findElement(Locator.ID, blockImageId)) // finds the DOM node
.check(webMatches(getProperty("naturalWidth"), equalTo("0"))) // "getProperty"?
What i need is smth like "getProperty()" that just generates JS to access the property of node found by findElement(Locator.ID, blockImageId), is there anything out-of-box?
I was able to find getText() but it seems it does it in completely different way (so i can't just request another "node property"):
/** Returns the visible text beneath a given DOM element. */
public static Atom<String> getText() {
return new GetTextTransformingAtom(new GetVisibleTextSimpleAtom(), castOrDie(String.class));
}
I was able to do it in JS way:
// JS-way helpers
const val naturalWidth = "naturalWidth"
const val notBlockedImageWidth = 50
const val blockedImageWidth = 0
fun getPropertyForElementId(elementId: String, property: String): Atom<String> =
Atoms.script(
"""return String(document.getElementById("$elementId").$property);""",
Atoms.castOrDie(String::class.java))
fun imageIsBlocked(elementId: String): WebAssertion<String>? = WebViewAssertions.webMatches(
getPropertyForElementId(elementId, naturalWidth),
equalTo(blockedImageWidth.toString())) {
"""An image with id="$elementId" IS expected to be blocked, but it's NOT."""
}
// part of the test
val redImage = "image.png"
load(
listOf(blockingPathPrefix),
"""
|<html>
|<body>
| <img id="$blockImageId" src="${blockingPathPrefix}$redImage"/>
| <img id="$notBlockImageId" src="$greenImage"/>
|</body>
|</html>
|""".trimMargin()
)
onWebView()
.withTimeout(1, TimeUnit.SECONDS) // it's already loaded
.check(imageIsBlocked(blockImageId)) // red image IS blocked
.check(imageIsNotBlocked(notBlockImageId)) // green image is NOT blocked
I do understand that the way i did it is suboptimal as it joins everything: searching of the node and accessing at once and i wonder what's the right way to do it. Any suggestions, links?
PS. "naturalWidth" is just a property that helps me in this particular case. In common case i just need to access a property of found DOM node and it can be some other property next time (eg "style.display" to check element visibility).
PS. Can anybody explain how to write WebDriverAtomScripts, eg. smth similar to WebDriverAtomScripts.GET_VISIBLE_TEXT_ANDROID?
is there anything out-of-box?
The answer to your question is "no", I don't think there is anything better out-of-the-box than creating an Atom like you are doing (or similar approaches like Atoms.scriptWithArgs or subclassing SimpleAtom).
Your best bet is to file a feature request here (and then maybe propose/contribute an implementation): https://github.com/android/android-test
You can assert on the html document with xpath, but that won't work for computed DOM node attributes like you are looking for.
onWebView()
.check(webContent(hasElementWithXpath("//*[#id=\"myButton\" and #type=\"button\"]")))

Accessing elements added to DOM with ng-bind-html

This probably has been answered before but I have tried for 48 hours now to find an answer with now luck. Posting this is a last resort.
So I have this Tetris clone and it works as seen here: http://www.crawfordcomputing.com/portfolio/Jetris.php
But it does not work in this single page angular app:
http://www.crawfordcomputing.com/AkadineWebOS/
I am re-coding the whole site to be a AngularJS single page app. To this end, I load the html via ng-bind-html and the compile directive found here: AngularJS directive in ng-bind-html.
The html string I am binding is exactly: EDIT: tried making canvas a childnode, did not work.<div><canvas id='gamewindow' width='780px' height='560px'></canvas></div>
The necessary scripts are loaded via the function found here: Single page application - load js file dynamically based on partial view
Note: html5jetris.js is actually an object: var jetris = { startgame: function(), ...};
Using firebug, I can see all added elements and the scripts in the DOM. I also am assuming that being a single page already loaded that I cannot use window.onload = jetris.startGame; after the closing ";" of var jetris = {};
1) I tried putting an alert("hello"); as second to last line (just before the call to startGame) and tried stepping through with firebug. It appears that html5jetris.js, while loaded, is not running.
2) I tried to access the canvas by id directly from address bar with javascript:getElementByID("gamewindow").innerHTML = "hello";
no success; this was several seconds after the ng-bind-html executed well past the 1-1.5 seconds it takes angular to load it.
3) if I pre-emptivly load all the js in my index.php, then firefox will step through it. But I only want to load the javascript when needed. This does not fix not being able to access the canvas by id.
4) by it self, Jetris works, just not loaded dynamically by AngularJS.
Note: Jetris does not require jQuery, even though it's there for Angular. It depends on my own utitlty.js (made as part of a javascript class, converted to object: var U = {};) and my own html5canvas.js (again, var canvas = {};) Since these are objects, the variables in them should not bump heads with anything jQuery or Angular.
Any ideas?
here is the directive:
.directive('compile', ['$compile', function ($compile) {
return function(scope, element, attrs) { //taken from Joël (Thank you!) source: https://stackoverflow.com/questions/26594153/angularjs-directive-in-ng-bind-html
scope.$watch(
function(scope) {
// watch the 'compile' expression for changes
return scope.$eval(attrs.compile);
},
function(value) {
// when the 'compile' expression changes
// assign it into the current DOM
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
$compile(element.contents())(scope);
}
);
};
}])
Jetris.php:
<?php //Jon Crawford AkadineWebOS pages/jetris.php
//describe window
$window = array();
$window['content'] = "<canvas id='gamewindow' width='780px' height='560px'></canvas>";
$window['title'] = "Jetris";
$window['x'] = 20;
$window['y'] = 250;
$window['id'] = 900;
$window['requireJS'] = array();
$window['requireJS'][0] = "scripts/html5Jetris.js";
$window['requireJS'][1] = "scripts/html5Canvas.js";
$window['requireJS'][2] = "scripts/utilities.js";
echo json_encode($window);
?>
This gets loaded to a div with title bar and exit button that is draggable. My menu system is icon like. My AkadineWebOS looks like a regular desktop. I have an about.php and a welcome.php that work just fine in my div-windows.
Ok so this is a special case I probably did not clarify my question. Here are the steps I used to accomplish my goal.
To load an HTML formated string to the DOM I used a $compile directive by Joël (Thank you!) source: AngularJS directive in ng-bind-html
To load external dependency script files, I used a load script function by Ben Thielker (Thank you!) source: Single page application - load js file dynamically based on partial view
Then I change the onload in my java game Jetris to: var externalJS = { run: function() { window.jetris.startGame(); } };
So that I will not have to re-code my angular app whenever I want to add a new game or other javascript app, this allows my app to check if var externalJS is undefined and if is is defined, it executes externalJS.run();. If the app's div-window is closed, this will effectively restart it when reopened.
Now since everything is loaded using Angular's HTTP module, you have to wait for Angulars promise to finish for everything to be loaded. If I try to run it to soon, I get a blank window and no game. To that end, I use
setTimeout(function() {
if (typeof externalJS != 'undefined'){
externalJS.run();
}},750);
in the HTTP module's successCallback to give it some time.
Here is a link to a working Alpha of the project, with all issues questioned here ironed out. You can drag the windows and icons, open more than one of the same window like a normal desktop unless a oneInstance flag is set like with Jetris, and you can play Jetris! Check it out! AkadineWebOS
Triva: I created the acronym Akadine as a online handle with a program that made random readable words (not real, just readable) years ago, and came up only recently with Advanced Kiosk And Dynamic Internet Navigation Environment.

Firefox: How do I get an nsIMessageManager instance from a JS Module under Electrolysis (e10s)?

I'm trying to port my Firefox extension to work under Electrolysis / e10s / multi-process mode. I've got a feature that requires registration through nsIComponentRegistrar so it's in a JSM which gets loaded only once (per process). I'm running in the child scope, so I don't have access to things like files, but my feature requires that. So I want to sendSyncMessage() to the parent process to fetch that detail (just the path to a file in this case).
The docs even mention doing something like this explicitly. But in the JSM I don't have a message manager in scope to call sendSyncMessage() on. How do I get a handle to (the right?) one? When I get called I don't have anything relating to the content document/window in scope.
Update, for clarity:
var c = Cc['#mozilla.org/childprocessmessagemanager;1'];
var s = c.getService(Ci.nsISyncMessageSender);
var response = s.sendSyncMessage('id', {'data': 'x'});
dump('response len?? ' + response.length + '\n');
This code produces 0 responses, even running directly in the frame script (not in the JSM which the frame script loads). If I just use the globally available sendSyncMessage() in the frame script then it gets the 1 response I expect.
"#mozilla.org/childprocessmessagemanager;1" is the way to go. Use that in child process JSMs.
However, as MDN puts it:
In addition to Message Managers centered around window and tab objects
there also is a separate hierachy focusing on process boundaries.
Therefore, you cannot use the regular frame script messengers, but have to use "#mozilla.org/parentprocessmessagemanager;1" in the parent (main) process.
child.jsm
let cpmm = Cc["#mozilla.org/childprocessmessagemanager;1"].
getService(Ci.nsISyncMessageSender);
cpmm.sendSyncMessage("addon:present?!")[0] === "yup"
parent.jsm
let ppmm = Cc["#mozilla.org/parentprocessmessagemanager;1"].
getService(Ci.nsIMessageListenerManager);
ppmm.addMessageListener("addon:present?", m => "yup");
Core code uses this scheme in various places, e.g. Network:SampleRate
This may work, no promises.
Try loading:
Cc["#mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
If that doesn't work then try using:
Cc['#mozilla.org/childprocessmessagemanager;1'].getService(Ci.nsISyncMessageSender);
Or vice-versa

Windows Phone App in JavaScript with AngularJS

I am developing a Windows Phone Application in JavaScript. I am using the AngularJS library. The problem is that I cannot add a dynamic content because of security reasons.
The error I get: HTML1701: Unable to add dynamic content '<div id="view_login" class="view"> <div id="view_login_container"> <img class="logo" src="http://oi60.tinypic.com/okwifa.jpg"> <input type="text" placeholder="Username" ng-model="loginUsername"> <input type="password" placeholder="******" ng-model="loginPassword"> <button ng-click="doLogin()">Login</button> <button ng-click="changeView('/signup')" class="link">... or sign up now</button> </div> </div>'. A script attempted to inject dynamic content, or elements previously modified dynamically, that might be unsafe. For example, using the innerHTML property to add script or malformed HTML will generate this exception. Use the toStaticHTML method to filter dynamic content, or explicitly create elements and attributes with a method such as createElement. For more information, see http://go.microsoft.com/fwlink/?LinkID=247104.
I changed one line in AngularJS library which should fix the problem:
append:function(a,c){
**MSApp.execUnsafeLocalFunction(function () {**
r(new N(c),function(c){
1!==a.nodeType&&11!==a.nodeType||a.appendChild(c)
})
});
}
Unfortunately it did not work.
I spent several hours trying to find a solution, but I did not manage it. I would appreciate any suggestions how to make working the Windows Phone App written in JavaScript with AngularJS.
Microsoft Open Technologies recently released a shim which will prevent this exact problem for Windows Store apps using AngularJS, as well as many other popular JavaScript libraries.
Simply download the JavaScript Dynamic Content shim off of GitHub, then reference the file towards the beginning of your app before any other scripts are run. You should no longer see a dynamic content error.
Let me know if this solves your problem!
I encountered this issue when using Angular in a Windows Store App. The solution I came up with was to monkey patch the DOM manipulation functions that were unsafe, rather than having to hack up Angular or jQuery because I still wanted to be able to update using bower.
var patch = {
methods: [
'appendNode',
'cloneNode',
'insertBefore',
'removeChild',
'replaceChild'
],
properties: [
'innerHTML',
'outerHTML'
]
};
patch.methods.forEach(function (name) {
proxyUnsafeMethod(HTMLElement.prototype, name);
});
patch.properties.forEach(function (name) {
proxyUnsafeProperty(HTMLElement.prototype, name);
});
function proxyUnsafeMethod(object, name) {
var _unsafe = object[name];
object[name] = function () {
var context = this;
var args = arguments;
return MSApp.execUnsafeLocalFunction(function () {
return _unsafe.apply(context, args);
});
};
}
function proxyUnsafeProperty(object, prop) {
var descriptor = Object.getOwnPropertyDescriptor(object, prop);
proxyUnsafeMethod(descriptor, 'set');
Object.defineProperty(object, prop, descriptor);
}
Angular dynamically puts HTML comment tags <!-- --> for ng-repeat and other directives. Unfortunately, Microsoft considers these to be unsafe when put in from javascript using element.innerHTML, and thus is not allowed.
The workaround is to modify the actual angular.js file and wrap all element.innerHTML calls in MSApp.execUnsafeLocalFunction();
In the version of Angular that I'm using, this is line 2539 and line 2162
Line 2539:
MSApp.execUnsafeLocalFunction(function() { element.innerHTML = value; });
Line 2162:
MSApp.execUnsafeLocalFunction(function() { div.innerHTML = '<div> </div>' + element });
The best method would be to search the angular.js file for all instances of innerHTML and wrap it.
In a lot of cases where you run into issues with dynamic content Winstore-jsContrib might help. Simply add the .js file at the beginning of your app and you're good to go.

Auto-load/include for JavaScript

I have file called common.js and it's included in each page of my site using <script />.
It will grow fast as my sites functionality will grow (I hope; I imagine). :)
Lets example I have a jQuery event:
$('#that').click(function() {
one_of_many_functions($(this));
}
For the moment, I have that one_of_many_functions() in common.js.
Is it somehow possible that JavaScript automatically loads file one_of_many_functions.js when such function is called, but it doesn't exist? Like auto-loader. :)
The second option I see is to do something like:
$('#that').click(function() {
include('one_of_many_functions');
one_of_many_functions($(this));
}
That not so automatically, but still - includes wanted file.
Is any of this possible? Thanks in an advice! :)
It is not possible to directly auto-load external javascripts on demand. It is, however, possible to implement a dynamic inclusion mechanism similar to the second route you mentioned.
There are some challenges though. When you "include" a new external script, you aren't going to be able to immediately use the included functionality, you'll have to wait until the script loads. This means that you'll have to fragment your code somewhat, which means that you'll have to make some decisions about what should just be included in the core vs. what can be included on demand.
You'll need to set up a central object that keeps track of which assets are already loaded. Here's a quick mockup of that:
var assets = {
assets: {},
include: function (asset_name, callback) {
if (typeof callback != 'function')
callback = function () { return false; };
if (typeof this.assets[asset_name] != 'undefined' )
return callback();
var html_doc = document.getElementsByTagName('head')[0];
var st = document.createElement('script');
st.setAttribute('language', 'javascript');
st.setAttribute('type', 'text/javascript');
st.setAttribute('src', asset_name);
st.onload = function () { assets._script_loaded(asset_name, callback); };
html_doc.appendChild(st);
},
_script_loaded: function (asset_name, callback) {
this.assets[asset_name] = true;
callback();
}
};
assets.inlude('myfile.js', function () {
/* do stuff that depends on myfile.js */
});
Sure it's possible -- but this can become painful to manage. In order to implement something like this, you're going to have to maintain an index of functions and their corresponding source file. As your project grows, this can be troublesome for a few reasons -- the 2 that stick out in my mind are:
A) You have the added responsibility of maintaining your index object/lookup mechanism so that your scripts know where to look when the function you're calling cannot be found.
B) This is one more thing that can go wrong when debugging your growing project.
I'm sure that someone else will mention this by the time I'm finished writing this, but your time would probably be better spent figuring out how to combine all of your code into a single .js file. The benefits to doing so are well-documented.
I have created something close to that a year ago. In fact, I have found this thread by search if that is something new on the field. You can see what I have created here: https://github.com/thiagomata/CanvasBox/blob/master/src/main/New.js
My project are, almost 100% OOP. So, I used this fact to focus my solution. I create this "Class" with the name "New" what is used to, first load and after instance the objects.
Here a example of someone using it:
var objSquare = New.Square(); // Square is loaded and after that instance is created
objSquare.x = objBox.width / 2;
objSquare.y = objBox.height / 2;
var objSomeExample = New.Stuff("some parameters can be sent too");
In this version I am not using some json with all js file position. The mapping is hardcore as you can see here:
New.prototype.arrMap = {
CanvasBox: "" + window.MAIN_PATH + "CanvasBox",
CanvasBoxBehavior: "" + window.MAIN_PATH + "CanvasBoxBehavior",
CanvasBoxButton: "" + window.MAIN_PATH + "CanvasBoxButton",
// (...)
};
But make this more automatic, using gulp or grunt is something what I am thinking to do, and it is not that hard.
This solution was created to be used into the project. So, the code may need some changes to be able to be used into any project. But may be a start.
Hope this helps.
As I said before, this still is a working progress. But I have created a more independent module what use gulp to keep it updated.
All the magic que be found in this links:
https://github.com/thiagomata/CanvasBox/blob/master/src/coffee/main/Instance.coffee
https://github.com/thiagomata/CanvasBox/blob/master/src/node/scripts.js
https://github.com/thiagomata/CanvasBox/blob/master/gulpfile.js
A special look should be in this lines of the Instance.coffee
###
# Create an instance of the object passing the argument
###
instaceObject = (->
ClassElement = (args) ->
window[args["0"]].apply this, args["1"]
->
ClassElement:: = (window[arguments["0"]])::
objElement = new ClassElement(arguments)
return objElement
)()
This lines allows me to initialize a instance of some object after load its file. As is used in the create method:
create:()->
#load()
return instaceObject(#packageName, arguments)

Categories

Resources