What is the point of marionette App initializers? - marionette

I dont really understand how they are useful. In the original article that introduced initializers this was the code sample:
App = new Backbone.Marionette.Application();
App.addInitializer(function(){
// add some app initialization code, here
});
App.addInitializer(function(){
// more initialization stuff
// for a different part of the app
});
// run all the initializers and start the app
App.start();
however, as far as I can understand, there is no difference between that^, and this:
App = new Backbone.Marionette.Application();
// add some app initialization code, here
// more initialization stuff
// for a different part of the app
The benefit of the latter code being that you can actually control the order of initialization code, whereas initializers are run in random order. So, what is the advantage of addInitializer?

I think the main wins are semantics - it's a very descriptive method name - and grouping of related functionality. I write my initializers with a named function, which helps with debugging and descriptiveness:
App.addInitializer(function startSomePartOfTheApp () {
});
Another useful feature is that the function is bound to the Application instance. This gives you the option of mixing in initializers, which is useful in larger apps.
But ultimately, you can achieve the same functionality in the way you've suggested.

Marionette apps have the initializer so that you can write code that runs after App.start()
Several uses of this :
Placing code in the .js file that will execute after the inline scripts are added
<script type='text/javascript'>
$(document).ready(function () {
App.start();
} );
</script>
Performing the initial fetch of a collection once you know the collection is defined
Setting up menu code
Fun Notes :
If the app is already started they run immediately
Keeps you clear of surprises with JavaScript function availability.

Related

Cypress: How to capture text from a selector on one page to use as text on another page

New cypress user here, I am aware that cypress does not handle variables like how testcafe and others do due to the asyn nature of it. Using the example given and what I could find I have this as an example:
cy.get('selector').invoke('text').as('text_needed')
cy.get('#text_needed')
const txtneeded = this.text_needed
cy.log(txtneeded)
This looks at a given selector, takes what it finds and uses it as text and set it as a variable usable at any time in the test and outputs it to the log. The plan is to use that text in a search filter in another page to find the item it references.
The problem is that it fails with Cannot read properties of undefined (reading 'text_needed')
Is this because the content of the selector is not assigned to text properly, the outer html is <a data-v-78d50a00="" data-v-3d3629a7="" href="#">PO90944</a> The PO90944 is what I want to capture.
Your help would be appreciated!
You cannot save an alias and access it via this.* in the same execution context (callback) because it's a synchronous operation and your alias is not yet resolved at this time.
This is a correct way to go:
cy.get('selector').invoke('text').as('text_needed')
cy.get('#text_needed').then(txtneeded => {
cy.log(txtneeded)
})
First, make sure to define it as traditional function, not as an arrow function as this context doesn't work as you'd expect there, more info here.
Next, typically in a single test you should use .then() callback to perform additional actions on elements, and use aliases when you need to share context between hooks or different tests, so please consider the following:
// using aliases together with this within the single test won't work
cy.get(<selector>).invoke('text').as('text_needed')
cy.get('#text_needed').should('contain', 'PO90944') // works fine
cy.log(this.text_needed) // undefined
// this will work as expected
cy.get(<selector>).invoke('text').then(($el) => {
cy.wrap($el).should('contain', 'PO90944'); // works fine
cy.log($el) // works fine
});
Setting alias in beforeEach hook for example, would let you access this.text_needed in your tests without problems.
Everything nicely explained here.
Edit based on comments:
it('Some test', function() {
cy.visit('www.example.com');
cy.get('h1').invoke('text').as('someVar');
});
it('Some other test', function() {
cy.visit('www.example.com');
cy.log('I expect "Example Domain" here: ' + this.someVar);
});
And here's the output from cypress runner:

Cypress command vs JS function

The Cypress documentation suggests that commands are the right way to reuse fragments of code, e.g.
Cypress.Commands.add("logout", () => {
cy.get("[data-cy=profile-picture]").click();
cy.contains("Logout").click();
});
cy.logout();
For simple cases like this, why would I use a command over a plain JS function (and all the nice IDE assistance that comes with it). What are the drawbacks of rewriting the above snippet as
export function logout(){
cy.get("[data-cy=profile-picture]").click();
cy.contains("Logout").click();
}
// and now somewhere in a test
logout();
Based on my experience with Cypress (one year project and several hundred test cases), I can say that a plan JS function is great for grouping cy commands.
From my point of view, a custom cy command may be really useful only if it is incorporated into the chain processing (utilizes the subject parameter or returns a Chainable to be used further in the chain). Otherwise, a plain JS function is preferable due to it simplicity and full IDE support (unless you're using an additional plugin).
If you for any reason need to do something inside the cypress loop, you can always wrap you code by cy.then() in a plain JS function:
function myFunction() {
cy.then(() => {
console.log(("I'm inside the Cypress event loop"))
})
}
Commands are for behavior that is needed across all tests. For example, cy.setup or cy.login. Otherwise, use functions.
See official docs: https://docs.cypress.io/api/cypress-api/custom-commands#1-Don-t-make-everything-a-custom-command

Laravel Webpack - Unwanted minification of top level variable

I have a variable in my main javascript file e.g. var example = {};.
After webpack has finished its job, I find that example is now referenced as t. This presents me a problem as I am using the variable across the web project. I bind functions onto objects for example:
var example = {};
example.initialise = function () {};
Finally at the bottom of a page I may invoke this section of script e.g:
<script>example.initialise()</script>
This way of writing javascript functions is not unusual...
This is obviously a huge pain in the ass as I have no control over the minification. Moreover, it appears that webpack doesn't figure out that example.initialise = function () {}; relates to its newly minified var example (becoming)--> var t. I.e. it doesn't become t.initialise = function {}; either.
What am I supposed to do here?
I've tried using rollup as well. The same kind of variable minification happens.
The thing is, this kind of minification/obfuscation is great, particularly on the inner workings of functions where there's little cause for concern over the parameter names. But not on the top level. I do not understand why this is happening, or how to prevent it.
Any ideas?
I assume that there are ways to set the configuration of webpack. E.g. inside webpack.config.js, but my perusing of the webpack docs gives me no easy understanding of what options I can use to resolve this, like preventing property minification in some way.
In laravel-elixir-webpack-official code you can see minify() is being applied here, minify() uses UglifyJS2 and mangling is on by default.
Mangling is an optimisation that reduces names of local variables and functions usually to single-letters (this explains your example object being renamed to t). See the doc here.
I don't see any way you can customize minify() behaviour in laravel-elixir-webpack, so for now you might have to monkey patch WebpackTask.prototype.gulpTask method before using the module (not an ideal solution). See the lines I am commenting out.
const WebpackTask = require('laravel-elixir-webpack-official/dist/WebpackTask').default;
WebpackTask.prototype.gulpTask = function () {
return (
gulp
.src(this.src.path)
.pipe(this.webpack())
.on('error', this.onError())
// .pipe(jsFiles)
// .pipe(this.minify())
// .on('error', this.onError())
// .pipe(jsFiles.restore)
.pipe(this.saveAs(gulp))
.pipe(this.onSuccess())
);
};
Turns out I have been silly. I've discovered that you can prevent top level properties from being minified by binding it to window... which in hindsight is something I've always known and was stupid not to have realised sooner. D'oh!
So all that needed to be done was to change all top-level properties like var example = {}; to something like window.app.example = {}; in which app is helping to namespace and prevent and override anything set by the language itself.

Prevent re-loading of javascript if functions already exist. Otherwise ensure synchronous loading

Using JQuery.load(), I change the content of my website's mainWindow to allow the user to switch between tabs. For each tab, there is one or multiple scipts that contain functions that are executed once the tab content is loaded.
Obviously when switching to the tab for the first time, the script has to be fetched from the server and interpreted, but this shouldn't happen if the user switches back to the tab later on. So, to put it short:
Load() html
make sure javascript functions exist, otherwise load script and interpret it.
call a a function on the javascript after the DOM is rebuilt.
Step one and two have to be complete before step 3 is performed.
At the moment, I am using nested callbacks to realize this:
function openFirstTab(){
$("#mainWindow").load("firstTab.php", function(){
if(typeof(onloadfFirstTab) != "function"){
jQuery.getScript("assets/js/FirstTab.js", function(){
onloadFirstTab();
});
}
else{
onloadFirstTab();
}
} );
}
but I feel that there should be a better way.
You can't write the code entirely synchronously since you can't load script synchronously after page load ( unless you do a synchronous XHR request and eval the results - not recommended ).
You've got a couple of choices. There are pre-existing dependency management libs like RequireJS which may fit the bill here, or if you just need to load a single file you can do something like this to clean up your code a bit rather than using if/else:
function loadDependencies() {
// For the sake of example, the script adds "superplugin" to the jQuery prototype
return $.getScript( "http://mysite.com/jquery.superplugin.js" );
}
function action() {
// If superplugin hasn't been loaded yet, then load it
$.when( jQuery.fn.superplugin || loadDependencies() ).done(function() {
// Your dependencies are loaded now
});
}
This makes use of jQuery Deferreds/Promises to make the code much nicer.
If you don't want to load the JS more than once and you are going to dynamically load it, then the only way to know whether it's already loaded is to test for some condition that indicates it has already been loaded. The choices I'm aware of are:
The simplest I know of is what you are already doing (check for the existence of a function that is defined in the javascript).
You could also use a property on each tab (using jQuery's .data() that you set to true after you load the script.
You could write the dynamically loaded code so that it knows how to avoid re-initializing itself if it has already been loaded. In that case, you just load it each time, but the successive times don't do anything. Hint, you can't have any statically defined globals and you have to test if it's already been loaded before it runs its own initialization code.
(Haven't tested it yet, so I am not sure if it works, especially since I didn't yet really understand scope in javascript:)
function require(scripts, callback){
var loadCount = 0;
function done(){
loadCount -=1;
if (loadCount==0){
callback();
}
}
for ( var script in scripts){
if (!script.exitsts()){
loadCount +=1;
jQuery.getScript(script.url, done);
}
}
}
This function takes an array of scripts that are required and makes sure all of them are interpreted before it calls the callback().
The "script" class:
function script(url, testFunc){
this.url =url;
this.testFunction = testFunc;
this.exists = function(){
if(typeof(testFunction)=="function"){
return true;
}
else{
return false;
}
}
}
Where the test-function is a function that is defined (only) in the concerned script.
PS:
To enable caching in JQuery and thus prevent the browser from doing a GET request every time getScript() is called, you can use one of the methods that are presented here.
Even though unnecessary GET - requests are avoided, the script is still getting interpreted every time getScript() is called. This might sometimes be the desired behavior. But in many cases, there is no need to re-interpret library functions. In these cases it makes sense to avoid calling getScript() if the required library functions are already available. (As it is done in this example with script.exists().

Adding custom code to mootools addEvent

Even though I've been using mootools for a while now, I haven't really gotten into playing with the natives yet. Currently I'm trying to extend events by adding a custom addEvent method beside the original. I did that using the following code(copied from mootools core)
Native.implement([Element, Window, Document], {
addMyEvent:function(){/* code here */}
}
Now the problem is that I can't seem to figure out, how to properly overwrite the existing fireEvent method in a way that I can still call the orignal method after executing my own logic.
I could probably get the desired results with some ugly hacks but I'd prefer learning the elegant way :)
Update: Tried a couple of ugly hacks. None of them worked. Either I don't understand closures or I'm tweaking the wrong place. I tried saving Element.fireEvent to a temporary variable(with and without using closures), which I would then call from the overwritten fireEvent function(overwritten using Native.implement - the same as above). The result is an endless loop with fireEvent calling itself over and over again.
Update 2:
I followed the execution using firebug and it lead me to Native.genericize, which seems to act as a kind of proxy for the methods of native classes. So instead of referencing the actual fireEvent method, I referenced the proxy and that caused the infinite loop. Google didn't find any useful documentation about this and I'm a little wary about poking around under the hood when I don't completely understand how it works, so any help is much appreciated.
Update 3 - Original problem solved:
As I replied to Dimitar's comment below, I managed to solve the original problem by myself. I was trying to make a method for adding events that destroy themselves after a certain amount of executions. Although the original problem is solved, my question about extending natives remain.
Here's the finished code:
Native.implement([Element, Window, Document], {
addVolatileEvent:function(type,fn,counter,internal){
if(!counter)
counter=1;
var volatileFn=function(){
fn.run(arguments);
counter-=1;
if(counter<1)
{
this.removeEvent(type,volatileFn);
}
}
this.addEvent(type,volatileFn,internal);
}
});
is the name right? That's the best I could come up with my limited vocabulary.
document.id("clicker").addEvents({
"boobies": function() {
console.info("nipple police");
this.store("boobies", (this.retrieve("boobies")) ? this.retrieve("boobies") + 1 : 1);
if (this.retrieve("boobies") == 5)
this.removeEvents("boobies");
},
"click": function() {
// original function can callback boobies "even"
this.fireEvent("boobies");
// do usual stuff.
}
});
adding a simple event handler that counts the number of iterations it has gone through and then self-destroys.
think of events as simple callbacks under a particular key, some of which are bound to particular events that get fired up.
using element storage is always advisable if possible - it allows you to share data on the same element between different scopes w/o complex punctures or global variables.
Natives should not be modded like so, just do:
Element.implement({
newMethod: function() {
// this being the element
return this;
}
});
document.id("clicker").newMethod();
unless, of course, you need to define something that applies to window or document as well.

Resources