Submodule becoming "undefined" in Backbone.Marionette app - marionette

I'm making a Marionette app for uploading files and I'm at the very beginning of it. To start things off, I will show you the files I'm working with:
upload_view.js
AppManager.module("PrimaryApp.Upload", function(Upload, AppManager, Backbone, Marionette, $, _){
Upload.UploadPage = Marionette.ItemView.extend({
template: "#upload-template"
});
});
upload_controller.js
AppManager.module("PrimaryApp.Upload", function(Upload, AppManager, Backbone, Marionette, $, _){
Upload.Controller = {
show: function(){
var uploadView = new Upload.UploadPage();
AppManager.regions.primary.show(uploadView);
},
};
});
app.js
var AppManager = new Marionette.Application();
AppManager.on("before:start", function() {
var RegionContainer = Marionette.LayoutView.extend({
el: "#app-container",
regions: {
primary: "#primary-region",
secondary: "#secondary-region",
header: "#header-region"
}
});
AppManager.regions = new RegionContainer();
});
AppManager.on("start", function(){
console.log(AppManager);
AppManager.PrimaryApp.Upload.Controller.show();
})
AppManager.start();
When running this application in a browser I get this error:
Uncaught TypeError: Cannot read property 'Upload' of undefined
For this line of code in app.js:
AppManager.PrimaryApp.Upload.Controller.show();
However, when I output AppManager to the console I get this:
Which shows that AppManager.PrimaryApp is indeed defined and contains all the submodules needed. Any ideas why my AppManager.PrimaryApp is undefined when called as a function, but then is defined when outputted to the console?

Figured out what was wrong!
AppManager.start();
Was in the wrong place and tried to run before AppManager.PrimaryApp was defined. However,
console.log(AppManager);
was somehow called after AppManager.PrimaryApp was defined resulting in a correct output. A simple move of my start() function fixed the order of things.

Related

Vuelidate, can I inherit a custom method via mixin?

Using Veulidate, using VueJS 2 on a project built using Vue CLI, I'm simply trying to using a custom method for the purpose of validating a phone number. The method is coming from a global mixin located in main.js.
main.js
Vue.mixin({
methods: {
vd_phone(val) {
var phonePattern = /^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/;
return phonePattern.test(val) ? true : false
}
});
form.vue
validations: {
phone: {
required,
phone: this.vd_phone
}
}
Seems simple enough, right? Over and over, I get Cannot read property of 'vd_phone' of undefined. Tried vd_phone, this.vd_phone, this.vd_phone(), etc..
Also tried putting the method into a global methods option (instead of a mixin) and trying to access it via $root like this:
main.js
var vm = new Vue({
router, el: '#app', store,
methods: {
vd_phone() {
var phonePattern = /^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/;
return phonePattern.test(val) ? true : false;
}
},
render: h => h(App)
});
Same problem! In my form.vue file I attempted accessing this method using this.$root.vd_phone, $root.vd_phone, etc.. no dice.
This is all I found on the topic: https://github.com/vuelidate/vuelidate/issues/308, but this seems to talk about inheriting entire validator properties - not just a method.
Any help is appreciated.
You're using a factory pattern to instantiate a function from it's source for use in other files. To do this you must export from the source file so other files can import it, like this:
main.js
export default {
vd_phone(val) {
var phonePattern = /^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/;
return phonePattern.test(val) ? true : false;
}
}
Then import the file where ever you need that function, and you will have access to it.

How do I fix a "browser.elements is not a function" error in Nightwatch.js?

I'm trying to use page objects in Nightwatch js and am creating my own commands in that. For some reason now Nightwatch doesn't seem to recognise standard commands on browser and give me a type error on different commands. What am I doing wrong with my code?
I'm tried different things here already, for example adding 'this' or 'browser' in front of the command, which didn't help. My code has gone through many versions already I am not even sure anymore what all I've tried after Googling the error.
My pageObject:
const homePageCommands = {
deleteAllListItems: function (browser) {
browser
.elements('css selector', '#mytodos img', function (res) {
res.value.forEach(elementObject => {
browser.elementIdClick(elementObject.ELEMENT);
});
})
.api.pause(1000)
return this;
}
};
module.exports = {
url: "http://www.todolistme.net"
},
elements: {
myTodoList: {
selector: '#mytodos'
},
deleteItemButton: {
selector: 'img'
}
},
commands: [homePageCommands]
};
My test:
require('../nightwatch.conf.js');
module.exports = {
'Validate all todo list items can be removed' : function(browser) {
const homePage = browser.page.homePage();
homePage.navigate()
.deleteAllListItems(homePage)
// I have not continued the test yet because of the error
// Should assert that there are no list items left
}
};
Expected behaviour of the custom command is to iterate over the element and click on it.
Actual result:
TypeError: browser.elements is not a function
at Page.deleteAllListItems (/pageObjects/homePage.js:18:14)
at Object.Validate all todo list items can be removed (/specs/addToList.js:8:14)
at processTicksAndRejections (internal/process/next_tick.js:81:5)
And also:
Error while running .navigateTo() protocol action: invalid session id
Looks like you need to pass browser to the deleteAllListItems function instead of homePage on this line:
homePage.navigate()
.deleteAllListItems(homePage)

When i set axios response in vue js i get an empty array in laravel blade

I am trying to display results in my page, When i log response. I can see the response, I have tried passing the response to vue js data, but each time it displays an empty array
var app = new Vue({
el: '#app',
data: {
results: []
},
mounted(){
var url = "{{ url('fetch/messages') }}";
axios.get(url)
.then(function (response) {
console.log(response.data);
this.results = response.data;
})
.catch(function (error) {
console.log(error);
});
}
I am trying to display the response in the page #{{ results }}
The reason your data is not getting added to the Vue instance is because this will not refer to the Vue instance inside a closure (traditional function () {}). Please have a look at this article for more information on scope and this keyword in javascript.
Below I refer to ES quite a bit which stands for ECMAScript. Here is an article to show the difference between it and vanilla javascript.
I can see from your post that you're using ES6/ES2015 syntax for your method definitions so I'm assuming you're happy with just using modern browsers for now.
If this is the case then you can use Arrow Functions to get past the scope issue:
.then(response => {
console.log(response.data);
this.results = response.data;
})
If not, then as mentioned in the scope article above, you would need to assign this to a variable:
var self = this;
axios.get(url)
.then(function (response) {
console.log(response.data);
self.results = response.data;
});
If this is the case, then I would also suggest not using the ES6 method definitions either so change mounted(){ to mounted: function () {.
Finally, if you want to be able to use modern/new javascript e.g. ES6/ES2015 and above, but have your code work consistently with older browsers then I would suggest using something like Laravel mix to compile your javascript for you.
Laravel Mix Documentation
Laravel Mix tutorial series
You cannot have the following line of code, it will break.
var url = "{{ url('fetch/messages') }}";

Show Modal on AngularJS $httpProvider: responseError

We need to hookup a Modal with a generic message on our failed Ajax calls in AngularJS.
i was able to intercept the error on the app.config part, but from there i am unable to import the $modal or the $scope to show the modal. What i got working so far is creating a function on my global scope and add the opening of the modal in my app.run:
var showGeneralException = function () { };
var app= angular.module('myApp', ['ui.bootstrap'])
.run(function ($rootScope, $modal) {
showGeneralException = function () {
var exceptionModel = $modal.open({
templateUrl: 'generalException',
controller: 'exceptionModals',
});
};
});
app.config(function ($httpProvider) {
$httpProvider.interceptors.push(function ($q) {
return {
'responseError': function (rejection) {
showGeneralException();
}
};
});
});
When i perform it this way a modal opens but i get a not found exception in my modal and following errors in my debug console:
TypeError: Unable to get value of the property 'data': object is null or undefined
LOG: WARNING: Tried to load angular more than once.
LOG: WARNING: Tried to load angular more than once.
LOG: WARNING: Tried to load angular more than once.

Running into Error while waiting for Protractor to sync with the page with basic protractor test

describe('my homepage', function() {
var ptor = protractor.getInstance();
beforeEach(function(){
// ptor.ignoreSynchronization = true;
ptor.get('http://localhost/myApp/home.html');
// ptor.sleep(5000);
})
describe('login', function(){
var email = element.all(protractor.By.id('email'))
, pass = ptor.findElement(protractor.By.id('password'))
, loginBtn = ptor.findElement(protractor.By.css('#login button'))
;
it('should input and login', function(){
// email.then(function(obj){
// console.log('email', obj)
// })
email.sendKeys('josephine#hotmail.com');
pass.sendKeys('shakalakabam');
loginBtn.click();
})
})
});
the above code returns
Error: Error while waiting for Protractor to sync with the page: {}
and I have no idea why this is, ptor load the page correctly, it seem to be the selection of the elements that fails.
TO SSHMSH:
Thanks, your almost right, and gave me the right philosophy, so the key is to ptor.sleep(3000) to have each page wait til ptor is in sync with the project.
I got the same error message (Angular 1.2.13). My tests were kicked off too early and Protractor didn't seem to wait for Angular to load.
It appeared that I had misconfigured the protractor config file. When the ng-app directive is not defined on the BODY-element, but on a descendant, you have to adjust the rootElement property in your protractor config file to the selector that defines your angular root element, for example:
// protractor-conf.js
rootElement: '.my-app',
when your HTML is:
<div ng-app="myApp" class="my-app">
I'm using ChromeDriver and the above error usually occurs for the first test. I've managed to get around it like this:
ptor.ignoreSynchronization = true;
ptor.get(targetUrl);
ptor.wait(
function() {
return ptor.driver.getCurrentUrl().then(
function(url) {
return targetUrl == url;
});
}, 2000, 'It\'s taking too long to load ' + targetUrl + '!'
);
Essentially you are waiting for the current URL of the browser to become what you've asked for and allow 2s for this to happen.
You probably want to switch the ignoreSynchronization = false afterwards, possibly wrapping it in a ptor.wait(...). Just wondering, would uncommenting the ptor.sleep(5000); not help?
EDIT:
After some experience with Promise/Deferred I've realised the correct way of doing this would be:
loginBtn.click().then(function () {
ptor.getCurrentUrl(targetUrl).then(function (newURL){
expect(newURL).toBe(whatItShouldBe);
});
});
Please note that if you are changing the URL (that is, moving away from the current AngularJS activated page to another, implying the AngularJS library needs to reload and init) than, at least in my experience, there's no way of avoiding the ptor.sleep(...) call. The above will only work if you are staying on the same Angular page, but changing the part of URL after the hashtag.
In my case, I encountered the error with the following code:
describe("application", function() {
it("should set the title", function() {
browser.getTitle().then(function(title) {
expect(title).toEqual("Welcome");
});
});
});
Fixed it by doing this:
describe("application", function() {
it("should set the title", function() {
browser.get("#/home").then(function() {
return browser.getTitle();
}).then(function(title) {
expect(title).toEqual("Welcome");
});
});
});
In other words, I was forgetting to navigate to the page I wanted to test, so Protractor was having trouble finding Angular. D'oh!
The rootElement param of the exports.config object defined in your protractor configuration file must match the element containing your ng-app directive. This doesn't have to be uniquely identifying the element -- 'div' suffices if the directive is in a div, as in my case.
From referenceConf.js:
// Selector for the element housing the angular app - this defaults to
// body, but is necessary if ng-app is on a descendant of <body>
rootElement: 'div',
I got started with Protractor by watching the otherwise excellent egghead.io lecture, where he uses a condensed exports.config. Since rootElement defaults to body, there is no hint as to what is wrong with your configuration if you don't start with a copy of the provided reference configuration, and even then the
Error while waiting for Protractor to sync with the page: {}
message doesn't give much of a clue.
I had to switch from doing this:
describe('navigation', function(){
browser.get('');
var navbar = element(by.css('#nav'));
it('should have a link to home in the navbar', function(){
//validate
});
it('should have a link to search in the navbar', function(){
//validate
});
});
to doing this:
describe('navigation', function(){
beforeEach(function(){
browser.get('');
});
var navbar = element(by.css('#nav'));
it('should have a link to home in the navbar', function(){
//validate
});
it('should have a link to search in the navbar', function(){
//validate
});
});
the key diff being:
beforeEach(function(){
browser.get('');
});
hope this may help someone.
I was getting this error:
Failed: Error while waiting for Protractor to sync with the page: "window.angular is undefined. This could be either because this is a non-angular page or because your test involves client-side navigation, which can interfere with Protractor's bootstrapping. See http://git.io/v4gXM for details"
The solution was to call page.navigateTo() before page.getTitle().
Before:
import { AppPage } from './app.po';
describe('App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should have the correct title', () => {
expect(page.getTitle()).toEqual('...');
})
});
After:
import { AppPage } from './app.po';
describe('App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
page.navigateTo();
});
it('should have the correct title', () => {
expect(page.getTitle()).toEqual('...');
})
});
If you are using
browser.restart()
in your spec some times, it throws the same error.
Try to use
await browser.restart()

Resources