ES6 call to static method throwing an error - javascript

I've got the following class in Javascript:
cart.js
class Cart{
static addItem(data, beforeSendCallback=null, successCallback=null, errorCallback=null) {
const emptyCallback = ()=>{}
if(data){
$.ajax({
url: '/orders/',
type: 'POST',
data: data,
beforeSend: beforeSendCallback == null ? emptyCallback : beforeSendCallback,
success: successCallback == null ? emptyCallback : successCallback,
error: errorCallback == null ? emptyCallback : errorCallback,
dataType: 'json'
})
}
}
}
Now, in a different file I've got:
item.js
...
function addItemToCart(e) {
e.preventDefault()
let data = {
'itemId': $('#item_id').val(),
'type': $('#item_type').val(),
'quantity': $('#quantity').val(),
'stock': $('#in_stock').val(),
'price': $('#item_price').val()
}
if (data.stock != 2) {
Cart.addItem(data, disableAddToCartBtn(true), disableAddToCartBtn(false))
}
}
...
When add-cart-btn is pressed then this function is executed but I'm getting an error:
Uncaught ReferenceError: Cart is not defined,
at HTMLAnchorElement.addItemToCart (item.js:20)
at HTMLAnchorElement.dispatch (jquery.js:5206)
at HTMLAnchorElement.elemData.handle (jquery.js:5014)
These two files are being added to app.js which is in charge of processing them via webpack:
require('./core/cart')
require('./item')
Since I'm requiring ./core/cart first I assumed its content would be available for ./item, am I wrong?

Try assigning the imported cart to a variable:
const Cart = require('./core/cart');
and make sure you export the cart in cart.js:
module.exports = class Cart {

You need to export the Cart class in cart.js, then import and actually assign it to a variable where you want to use it. In cart.js:
// ....
module.exports = Cart;
Then in whatever module you want to use Cart:
const Cart = require('./core/cart');
// use the `Cart` class.

Related

Vue.js undefined error on object value when loaded and rendered

OK. I'm not a total newbie and do have some Vue xp but this is bugging me. What really obvious thing am I missing.
I have an object loaded via an ajax call inside a mounted method:
job: {
"title": "value",
"location": {
"name":"HONG KONG"
}
}
When I call {{ job.title }} all good. When I call {{ job.location.name }} I have an undefined error but the value renders. When I call {{ job.location }} I get the json object so it is defined.
Aaargh! I'm sure it's really simple but can't possibly see why this isn't as straight forward as it should be.
// Additional
This is my entire Vue class
const router = new VueRouter({
mode: 'history',
routes: []
});
const app = new Vue( {
router,
el: '#app',
data: {
job: {}
},
mounted: function () {
var vm = this
jQuery.ajax({
url: 'https://xxx' + this.jobId,
method: 'GET',
success: function (data) {
vm.job = data;
},
error: function (error) {
console.log(error);
}
});
},
computed: {
jobId: function() {
return this.$route.query.gh_jid
}
}
})
When your component renders it tries to get value from job.location.name but location is undefined before ajax request is completed. So I guess error is kind of Cannot read property 'name' of undefined.
To fix this you can define computed property locationName and return, for example, empty string when there is no loaded job object yet:
computed:{
//...
locationName() {
return this.job.location ? this.job.location.name : '';
}
}
Or you can define computed for location and return empty object if there is no location, or you can just add empty location object to your initial data(if you are sure that your API response always has location) like job: { location: {}} all ways will fix your issue.
Also there is a way to fix it with v-if directive in your template:
<div v-if="job.location">
{{ job.location.name }}
<!-- other location related stuff -->
</div>
An ES6 solution for you:
computed: {
getJobName(){
return this.job?.location.name
}
}
Optional Chaining

React throwing error: Cannot read property 'map' of undefined

I am learning React. I am following a chapter in book where they load the data from API under componentDidMount and update the state of the component as
getInitialState: function () {
return {changeSets: []};
},
mapOpenLibraryDataToChangeSet : function (data) {
return data.map(function (change, index) {
return {
"when":jQuery.timeago(change.timestamp),
"who": change.author.key,
"description": change.comment
}
});
},
componentDidMount: function () {
$.ajax({
url: 'http://openlibrary.org/recentchanges.json?limit=10',
context: this,
dataType: 'json',
type: 'GET'
}).done(function (data) {
// console.log(data);
var changeSets = this.mapOpenLibraryDataToChangeSet(data);
console.log("changeSets:" + JSON.stringify(changeSets));
this.setState({changeSets: changeSets});
});
},
When I run this, I see error on console as
"TypeError: Cannot read property 'map' of undefined
at t.render (mikobuf.js:55:41)
at _renderValidatedComponentWithoutOwnerOrContext (https://npmcdn.com/react#15.3.1/dist/react.min.js:13:17508)
at _renderValidatedComponent (https://npmcdn.com/react#15.3.1/dist/react.min.js:13:17644)
at performInitialMount (https://npmcdn.com/react#15.3.1/dist/react.min.js:13:13421)
at mountComponent (https://npmcdn.com/react#15.3.1/dist/react.min.js:13:12467)
at Object.mountComponent (https://npmcdn.com/react#15.3.1/dist/react.min.js:15:2892)
at h.mountChildren (https://npmcdn.com/react#15.3.1/dist/react.min.js:14:26368)
at h._createInitialChildren (https://npmcdn.com/react#15.3.1/dist/react.min.js:13:26619)
at h.mountComponent (https://npmcdn.com/react#15.3.1/dist/react.min.js:13:24771)
at Object.mountComponent (https://npmcdn.com/react#15.3.1/dist/react.min.js:15:2892)"
The running link is http://jsbin.com/mikobuf/edit?js,console,output
What am I doing wrong?
UPDATE
When I added the changeSets={data} while rendering the app, I see data in console
ReactDOM.render(<App headings = {headings} changeSets={data}/>, document.getElementById("container"))
But I want the data to be pulled from API. So as soon as I remove the changeSets={data} when rendering, it fails
ReactDOM.render(<App headings = {headings}/>, document.getElementById("container"))
You are trying to use the props changeSets when it is actually part of Apps state.
This:
<RecentChangesTable.Rows changeSets={this.props.changeSets} />
Should Be:
<RecentChangesTable.Rows changeSets={this.state.changeSets} />
http://jsbin.com/tuqeciyere/1/edit?js,console,output

Constants not available for jest tests in Actions

When I run navAction-test.js below I get following error: TypeError: Cannot read property 'DATA_LOADED' of undefined
As I understand it the constant.DATA_LOADED is undefined, I'm guessing jest is mocking it and for this reason constant is undefined, I've tried everything I could find on the next but it remains undefined.
Is there anyone out there who can help. I would really appreciate it.
navAction.js:
var dispatcher = require('../dispatcher/AppDispatcher');
var constants = require('../constants/constants');
module.exports = {
load: function() {
def = $.ajax({
url: 'http://api.facebook.com/',
data: {},
success: function (data, textStatus, jqXHR) {
dispatcher.dispatch({type: constants.DATA_LOADED, data: data});
}
});
}
}
navAction-test.js:
jest.dontMock('../navAction.js');
describe('Tests NavigationCollectionActionCreators', function() {
var $;
var dispatcher;
var navAction;
beforeEach(function() {
$ = require('jquery');
dispatcher = require('../../dispatcher/AppDispatcher');
navAction = require('../navAction.js');
});
it('tests calls $.ajax & dispatcher ', function () {
navAction.load();
$.ajax.mock.calls[0][0].success({body: {header: {items: [{name: 'The name', link: 'http://www.facebook.com'}]}}});
expect(dispatcher.dispatch).toBeCalledWith({type: 'DATA_LOADED', data: [{name: 'The name', link: 'http://www.timeout.com'}]});
});
});
//constants.js
var keyMirror = require('keymirror');
module.exports = keyMirror({
DATA_LOADED: null,
});
UPDATE:
I got this to work by adding the following to navAction-test.js:
var constants = {
DATA_LOADED: 'DATA_LOADED',
DATA_NOT_LOADED: 'DATA_NOT_LOADED'
};
jest.setMock('../../constants/constants', constants);
Is this the correct way of doing it?
Add to test:
jest.dontMock('../constants/constants');
If you do not want to use automocking at all, you can remove all of the jest.dontMock and put instead:
jest.autoMockOff();
Your method to solve the problem (in the update at the bottom) is not appropriate in my opinion because it violates DRY (do not repeat yourself). What happens when you added another action constant? Now you have to remember to add it in the test too. That is problematic.

JestJs thinks ajax is not called

I'm having a lot of trouble getting a simple jest test to work. Jest insists that my Ajax call is not happening, with the error message:
FAIL authTest.js (1.828s)
● Authentication: Logging In › it Doesn't currently have a logged in user
- Expected Function to be called with { url : 'api/loggedin', type : 'GET', error : <jasmine.any(function Function() { [native code] })>, success : <jasmine.any(function Function() { [native code] })> }.
at Spec.<anonymous> (/Users/ritmatter/reps/spec/authTest.js:13:20)
at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
The code being tested is in a file called auth.jsx, and it looks like this:
loggedIn: function() {
return $.ajax({
url: 'api/loggedin',
type: 'GET',
error: function(xhr, status, err) {
return false;
}.bind(this),
success: function(data) {
return true;
}.bind(this),
});
},
The test looks like this:
/** #jsx React.DOM */
"use strict";
var React = require('react/addons');
var TestUtils = React.addons.TestUtils;
describe('Authentication: Logging In', function() {
it('Doesn\'t currently have a logged in user', function () {
var $ = require('jquery');
jest.dontMock('../js/auth.jsx');
var auth = require('../js/auth.jsx');
auth.loggedIn();
expect($.ajax).toBeCalledWith({
url: 'api/loggedin',
type: 'GET',
error: jasmine.any(Function),
success: jasmine.any(Function)
});
});
});
Any idea why jest would think that this is not getting called? I've been looking around, and it seems like there are some bugs with respect to dontMock() and mock().
As Wagner mentioned, you need to require jquery globally, outside of your test. Your component is using the global version of $, so adding var $ = require('jquery') doesn't do anything in terms of adding jquery to the global variable $.
You also did not mock the ajax call.
When testing react, I avoid issues with loading jquery by simply redefining $:
window.$ = {ajax: jest.genMockFunction()}
So, as long as you don't need jquery for anything else other than an ajax call, this one line will simulate the jquery root and mock the ajax call.
I had a similar problem with a React component that invoked ajax on initialisation.
What I found is that expect on $ only work if you require jquery outside the it method.
My React component and test case are like these (They are ES6 but you can get the idea)
import React from 'react'
import $ from 'jquery'
export default class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {things:[]};
}
componentWillMount() {
$.ajax({
url: 'http://localhost:3000/things',
success: (result) => this.setState(result),
error: (ex) => console.log(ex)
})
}
render() {
//stuff
}
}
and the test
jest.dontMock('../components/MyComponent')
import React from 'react'
import TestUtils from 'react-addons-test-utils'
import $ from 'jquery'
const Wall = require('../components/MyComponent');
describe('MyComponent', () => {
it('calls the things end point', () => {
const myComp = TestUtils.renderIntoDocument(<MyComponent />)
expect($.ajax).toBeCalledWith({
url: 'http://localhost:3000/things',
success: jasmine.any(Function),
error: jasmine.any(Function)
})
});
});

is it possible to include an event in a javascript function?

i was just wondering if getting a jqgrid event from a main javascript and separate it using another javascript in a form of function would work? what im trying to do is like this. i have a code :
...//some code here
serializeGridData: function(postData) {
var jsonParams = {
'SessionID': $('#eSessionID3').val(),
'dataType': 'data',
'recordLimit': postData.rows,
'recordOffset': postData.rows * (postData.page - 1),
'rowDataAsObjects': false,
'queryRowCount': true,
'sort_fields': postData.sidx
};
if (postData.sord == 'desc')
{
...//some code here
}
else
{
...//some code here
}
return 'json=' + jsonParams;
},
loadError: function(xhr, msg, e) {
showMessage('errmsg');
},
...//some code here
i want to get this code and write this in another javascript file and make this as a function, so that my other file could use this one..is it possible?
i created something like this in my other javascrtip file where i planned to put all my functions. here's the code (functions.js):
function serialLoad(){
serializeGridData: function(postData) {
var jsonParams = {
'SessionID': $('#eSessionID3').val(),
'dataType': 'data',
'recordLimit': postData.rows,
'recordOffset': postData.rows * (postData.page - 1),
'rowDataAsObjects': false,
'queryRowCount': true,
'sort_fields': postData.sidx
};
if (postData.sord == 'desc')
{
...//some code here
}
else
{
...//some code here
}
return 'json=' + jsonParams;
},
loadError: function(xhr, msg, e) {
showMessage('errmsg');
}
}
this isn't working and display a message syntax error. i don't know how to correct this. is there anyone who can help me.?
First of all the answer on your derect question. If you define in the functions.js file some global variable, for example, myGlobal:
myGlobal = {};
myGlobal = serializeGridData: function(postData) {
// ... here is the implementation
};
you can use it in another JavaScript file which must be included after the functions.js file:
serializeGridData: myGlobal.serializeGridData
(just use such parameter in the jqGrid definition).
If you want to use the serializeGridData parameter with the value in the most of your jqGrids you can overwrite the default value of serializeGridData in the functions.js file instead:
jQuery.extend(jQuery.jgrid.defaults, {
datatype: 'json',
serializeGridData: function(postData) {
// ... here is the implementation
},
loadError: function(xhr, msg, e) {
showMessage('errmsg');
}
});
In the example I ovewride additionally default datatype: 'xml' jqGrid parameter to datatype: 'json'. It shows that in the way you can set default values of any jqGrid parameter.
What it seems to me you really need is to use prmNames jqGrid parameter to rename some defaulf names of the standard jqGrid parameters. For example with
prmNames: {
rows:"recordLimit",
sort: "sort_fields",
search:null,
nd:null
}
you rename the standard rows parameter to recordLimit, the sidx to sort_fields and remove _search and nd parameters to be send.
Additionally you can use postData having some properties defined as the function (see here for details). For example:
postData: {
SessionID: function() {
return $('#eSessionID3').val();
},
rowDataAsObjects: false,
queryRowCount: true,
dataType: 'data',
recordOffset: function() {
var pd = jQuery("#list2")[0].p.postData;
return pd.recordLimit * (pd.page - 1);
},
json: function() {
var pd = jQuery("#list2")[0].p.postData;
return {
SessionID: $('#eSessionID3').val(),
dataType: 'data',
recordOffset: pd.recordLimit * (pd.page - 1),
rowDataAsObjects: false,
queryRowCount: true,
sort_fields: pd.sort_fields
};
}
}
I used here both json parameter which you currently use and add parameters like SessionID, queryRowCount and so on directly in the list of parameters which will be send. Of course it is enough to send only one way (either json or the rest) to send the aditional information which you need.
The second example is incorrect, as you are declaring a javascript object as the body of a function, what you could do is:
function serialLoad() {
// Return an object with the required members
return {
serializeGridData: function(postData) { ... },
loadError: function(xhr, msg, e) { ... }
};
}
You are mixing function declaration and object literal notation. This syntax: property: value is used when creating an object with object literal notation:
var obj = {
prop: val,
prop2: val
};
serializeGridData and loadError are properties of some object and you cannot define those by just putting them into a function.
One way would be to create two functions, one for serializeGridData and one for loadError, e.g.
function serialLoad(postData){
var jsonParams = {
//...
};
if (postData.sord == 'desc') {
//... some code here
}
else {
//... some code here
}
return 'json=' + jsonParams;
}
function onError(xhr, msg, e) {
showMessage('errmsg');
}
Then you can assign them in your other file to the object:
// ... some code here
serializeGridData: serialLoad,
loadError: onError,
//... some code here
Another way is to pass the object in question to the function and assign the properties there:
function attachLoadHandler(obj) {
obj.serializeGridData = function(postData) {
//...
};
obj.loadError = function(xhr, msg, e) {
//...
};
}
Then you have to pass the object you created to that function:
attachLoadHandler(obj);
But I think the first approach is easier to understand.

Categories

Resources