Communication between Stores in Reactjs - javascript

I develop an application which based on Reactjs and Flux. There is a problem of communication between Stores: ProjectsStore and TasksStore.
In the method getAllForCurrentProject of TasksStore, I call ProjectsStore.getCurrentId(). I get an Uncaught TypeError: undefined is not a function as a result. The typeof ProjectsStore is object in getAllForCurrentProject. When I call ProjectsStore.getCurrentId() from any component it works fine.
What is the reason for this behavior?
In the example MessageStore asks ThreadStore with the same pattern:
getAllForCurrentThread: function() {
return this.getAllForThread(ThreadStore.getCurrentID());
}
My stores:
ProjectsStore.js:
'use strict';
var Dispatcher = require('../dispatcher/Dispatcher');
var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');
var _ = require('underscore');
var Api = require('../services/Api');
var ProjectsConstants = require('../constants/ProjectsConstants');
var TasksStore = require('../stores/TasksStore');
var changeEvent = 'projectsChanged';
var current = 0;
var items = [];
function requestItems() {
return Api.Projects.getAll();
}
function setItems(data) {
items = data;
}
var ProjectsStore = assign({}, EventEmitter.prototype, {
emitChange: function () {
this.emit(changeEvent);
},
getAll: function () {
return items;
},
getCurrentId: function() {
return current;
},
getCurrent: function() {
var item = _.where(items, { id: this.getCurrentId() });
return (typeof item[0] == 'object' ? item[0] : null);
},
getChildrenOf: function(id, isInclude) {
var result = (typeof isInclude == 'boolean' && isInclude === true ? [id] : []),
children = _.chain(items).where({ parent: id }).pluck('id').value();
result.concat(children);
return result;
}
});
ProjectsStore.dispatchToken = Dispatcher.register(function (payload) {
var action = payload.action;
switch (action.type) {
case ProjectsConstants.projectsSetCurrent:
current = action.data;
break;
case ProjectsConstants.projectsGetAll:
requestItems();
break;
case ProjectsConstants.projectsGetAllSuccess:
setItems(action.data);
break;
default:
return true;
}
ProjectsStore.emitChange();
return true;
});
module.exports = ProjectsStore;
TasksStore.js:
'use strict';
var Dispatcher = require('../dispatcher/Dispatcher');
var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');
var _ = require('underscore');
var Api = require('../services/Api');
var TasksConstants = require('../constants/TasksConstants');
var ProjectsStore = require('../stores/ProjectsStore');
var changeEvent = 'tasksChanged';
var items = [];
function requestItems() {
return Api.Tasks.getAll();
}
function setItems(data) {
items = data;
}
var TasksStore = assign({}, EventEmitter.prototype, {
emitChange: function () {
this.emit(changeEvent);
},
getAll: function () {
return items;
},
getAllForProject: function(id) {
var projects = ProjectsStore.getChildrenOf(id, true);
return _.chain(items).where({ parent: projects });
},
getAllForCurrentProject: function() {
console.log('Type:', typeof ProjectsStore); // <-- object
console.log('Inspect:', ProjectsStore); // <-- {}
// Why ProjectsStore here is {} and
// Uncaught TypeError: undefined is not a function?
var id = ProjectsStore.getCurrentId();
// When I calling ProjectsStore.getCurrentId(); from any component it works fine.
return this.getAllForProject(id);
}
});
TasksStore.dispatchToken = Dispatcher.register(function (payload) {
var action = payload.action;
switch (action.type) {
case TasksConstants.tasksGetAll:
requestItems();
break;
case TasksConstants.tasksGetAllSuccess:
setItems(action.data);
break;
default:
return true;
}
TasksStore.emitChange();
return true;
});
module.exports = TasksStore;

It's look like you have Circular dependencies - TasksStore and ProjectsStore requiring each other.
ProjectsStore don't need to know TasksStore, remove the line:
var TasksStore = require('../stores/TasksStore');
or, if you use it, design your store to allow dependency injection, so your to classes won't dependent on each other

Related

Controller isn't counted as a function

I have many different controllers throughout this project and all of them are declared the same way. This one now isn't getting called/giving an error and I have no clue why. I've looked through it and it all looks right to me.
I think it's probably some syntax error I'm not seeing. If its something else please tell me. I'm trying to learn angular and everything helps. Also if you need anything else just tell me.
I've made sure its not that the app.js name got changed and been looking for missing syntax but can't find anything.
https://docs.angularjs.org/error/ng/areq?p0=companyDepartmentController&p1=not%20a%20function,%20got%20undefined
company-department-controller.js
app.controller('companyDepartmentController', ['$scope', '$timeout', 'companyService', function ($scope, $timeout, companyService) {
/**
* Create/Manage Company Departments & Shifts
*
*/
// INITIALIZE VARIABLES *********************************************************************************
var vm = this;
vm.Departments = [];
vm.activeDepartment = {}
vm.departmentBeforeEdit = {};
vm.activeShift = {};
vm.OffsetString = "";
vm.SaveDepartmentSuccessMessage = null;
vm.SaveDepartmentErrorMessage = null;
// STARTUP **********************************************************************************************
(vm.GetDepartments = function () {
companyService.GetDepartmentsWithShiftInformation().success(function (data) {
console.log('hi');
for (i = 0; i < data.length; i++) {
console.log(data[i])
}
vm.Departments = data;
// for now we are limiting this to 1
vm.activeDepartment = vm.Departments[0];
vm.setTimeZoneOffsets(vm.activeDepartment);
});
})();
// move to global location? handle this better?
(vm.findLocalOffsetString = function () {
console.log('hi1');
vm.OffsetString = moment(new Date()).format('ZZ');
})();
// $BROADCAST/$ON EVENTS ********************************************************************************
// EVENTS ***********************************************************************************************
vm.saveDepartment = function (department) {
// new
if (department.DepartmentID === 0 || typeof department.DepartmentID === 'undefined') {
}
// update
else {
companyService.UpdateDepartmentHeader(department).success(function (data) {
vm.SaveDepartmentSuccessMessage = "Saved!";
resetDepartmentMessage();
department.InEdit = false
});
}
};
vm.editDepartment = function (department) {
vm.activeDepartment = department;
vm.departmentBeforeEdit = angular.copy(vm.activeDepartment);
vm.activeDepartment.InEdit = true;
};
vm.cancelDepartmentEdit = function (department) {
for (var i = 0; i < vm.Departments.length; i++) {
if (department.DepartmentID === vm.Departments[i].DepartmentID) {
vm.Departments[i] = vm.departmentBeforeEdit;
vm.departmentBeforeEdit = {};
vm.activeDepartment = vm.Departments[i];
break;
};
};
};
vm.addShift = function () {
if (!vm.activeDepartment) return;
vm.activeShift = {
DepartmentID: vm.activeDepartment.DepartmentID,
StartTime: new Date(),
LocalStartTime: new Date(new Date() + vm.OffsetString)
};
vm.activeShift.StartTime.setSeconds(0);
vm.activeShift.LocalStartTime.setSeconds(0);
};
vm.deleteShift = function (shift) {
if (!shift) return;
if (confirm("Are you sure you want to delete the shift: " + shift.Name + "?")) {
companyService.DeleteShift(shift).success(function () {
angular.forEach(vm.activeDepartment.Shifts, function (c, i) {
if (c.ShiftID === shift.ShiftID) {
vm.activeDepartment.Shifts.splice(i, 1);
};
});
});
};
};
vm.setTimeZoneOffsets = function (department) {
if (!department || !department.Shifts || department.Shifts.length === 0) return;
for (var i = 0; i < department.Shifts.length; i++) {
department.Shifts[i].LocalStartTime = new Date(department.Shifts[i].StartTime + vm.OffsetString);
department.Shifts[i].EndTime = moment(department.Shifts[i].StartTime).add(department.Shifts[i].Duration, 'hours').toDate()
};
};
var fixTimezoneOnSave = function (shift) {
shift.StartTime = new Date(shift.LocalStartTime).toLocaleString();
};
vm.setActiveShift = function (shift) {
if (!shift) return;
vm.activeShift = angular.copy(shift);
};
vm.saveShift = function (shift) {
fixTimezoneOnSave(shift);
// new shift
if (shift.ShiftID === 0 || typeof shift.ShiftID === 'undefined') {
companyService.AddShift(shift).success(function (data) {
shift.ShiftID = data;
vm.SaveDepartmentSuccessMessage = "Saved!";
resetDepartmentMessage();
getUpdatedShiftsAndInfo();
}).error(function (e) {
vm.SaveDepartmentErrorMessage = e.error;
resetDepartmentMessage();
});
}
// updating existing
else {
companyService.UpdateShift(shift).success(function (data) {
vm.SaveDepartmentSuccessMessage = "Saved!";
resetDepartmentMessage();
getUpdatedShiftsAndInfo();
}).error(function (e) {
vm.SaveDepartmentErrorMessage = e.error;
resetDepartmentMessage();
});
}
}
var getUpdatedShiftsAndInfo = function () {
companyService.DepartmentAndShiftInformation(vm.activeDepartment.DepartmentID).success(function (data) {
vm.activeDepartment.DepartmentShiftInformation = data.DepartmentShiftInformation;
vm.activeDepartment.Shifts = data.Shifts;
vm.setTimeZoneOffsets(vm.activeDepartment);
});
};
var resetDepartmentMessage = function () {
// clear error/success message if they have values still
if (vm.SaveDepartmentSuccessMessage != null) {
$timeout(function () { vm.SaveDepartmentSuccessMessage = null; }, 2000);
}
if (vm.SaveDepartmentErrorMessage != null) {
$timeout(function () { vm.SaveDepartmentErrorMessage = null; }, 2000);
}
};
// create controller object in console if we have logging turned on
if (spectrum.LoggingEnabled) {
spectrum.logController(vm);
};
}]);
_CompanyDepartment.cshtml
<div class="container-fluid" data-ng-controller="companyDepartmentController as cd">
</div>
#section scripts {
#Scripts.Render("~/bundles/companyDepartments")
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/angularjs")
}
app.js
var app = angular.module('app', ['angularFileUpload', 'ngSanitize', 'ui.mask', 'ui.select', 'ui.bootstrap', 'ui.bootstrap.tpls', 'angular.filter', 'smart-table', 'colorpicker.module'])
.config(function ($httpProvider) {
//make delete type json to facilitate passing object
//to our generic method.
$httpProvider.defaults.headers["delete"] = {
'Content-Type': 'application/json;charset=utf-8'
};
});
Outside of a naming issue with the controller(which I can't see), I would imagine your issue dealing with the company-department-controller.js not being loaded.
In setting up your angular project, I would suggest that you follow this angular styleguide. It has been very helpful to me in creating a well structured project.

Variables Pointing To The Same Function

I have created a new variable, carBasket and foodBasket, and set them equal to the basketModule() function. They however are pointed to the same function when I want each of these two variables pointed to their own function. I am wondering what should I be doing to achieve this?
var basketModule = (function() {
var basket = [];
return {
addItem: function(values) {
basket.push(values);
},
getItemCount: function() {
return basket.length;
}
};
}());
carBasket = basketModule;
carBasket.addItem('Audi');
foodBasket = basketModule;
foodBasket.addItem('Ham');
foodBasket.getItemCount(); //outputs 2 instead of 1
You must call a function for each object in order to generate different variables for each one, e.g:
var basketModule = function() {
var basket = [];
return {
addItem: function(values) {
basket.push(values);
},
getItemCount: function() {
return basket.length;
}
};
};
var carBasket = basketModule(),
foodBasket = basketModule();
carBasket.addItem('Audi');
foodBasket.addItem('Ham');
foodBasket.getItemCount(); // 1
However, in order to reuse the methods for all instances, better use a constructor:
var BasketModule = function() {
this.basket = [];
};
BasketModule.prototype.addItem = function(values) {
this.basket.push(values);
};
BasketModule.prototype.getItemCount = function() {
return this.basket.length;
};
var carBasket = new BasketModule(),
foodBasket = new BasketModule();
carBasket.addItem('Audi');
foodBasket.addItem('Ham');
foodBasket.getItemCount(); // 1
You should consider trying this pattern instead:
var BasketModule = function() {
var basket = [];
return {
addItem: function(values) {
basket.push(values);
},
getItemCount: function() {
return basket.length;
}
};
};
carBasket = new BasketModule();
carBasket.addItem('Audi');
foodBasket = new BasketModule();
foodBasket.addItem('Ham');
jsfiddle: https://jsfiddle.net/nvsbjset/
This will create separate objects for each basket

JavaScript object inheritance issue

I'm a beginner with JavaScript Objects and Prototypes and trying to develop my first " multi-level inherited" JS Objects, an unexpected issue came up.
This is my code:
var Utils = function () {};
Utils.prototype = {
sayHelloGeneral: function(){
console.log('hello');
}
};
var FormTools = function () {
Utils.call(this);
this.fields = [];
};
FormTools.prototype = Object.create(Utils.prototype);
FormTools.prototype.constructor = FormTools;
FormTools.prototype.sayHelloForm= function (fields) {
console.log('hello form');
};
function GroupManager(value) {
FormTools.call(this);
this.val = typeof values === 'undefined' ? 1 : value;
};
GroupManager.prototype = Object.create(FormTools.prototype);
GroupManager.prototype.constructor = GroupManager;
GroupManager.prototype.helloGroupManager= function (givenValue) {
console.log('Hello group manager');
};
Why when I try to call the group manager, it prints only the sayHelloGeneral function?
var GM = new GroupManager;
GM.sayHelloGeneral(); //->ok
GM.helloGroupManager(); //--> ok
GM.sayHelloForm(); //->sayHelloForm is not a function
It seems to be working fine. See the snippet below
var Utils = function () {};
Utils.prototype = {
sayHelloGeneral: function(){
console.log('hello');
}
};
var FormTools = function () {
Utils.call(this);
this.fields = [];
};
FormTools.prototype = Object.create(Utils.prototype);
FormTools.prototype.constructor = FormTools;
FormTools.prototype.sayHelloForm= function (fields) {
console.log('hello form');
};
function GroupManager(value) {
FormTools.call(this);
this.val = typeof values === 'undefined' ? 1 : value;
};
GroupManager.prototype = Object.create(FormTools.prototype);
GroupManager.prototype.constructor = GroupManager;
GroupManager.prototype.helloGroupManager= function (givenValue) {
console.log('Hello group manager');
};
var GM = new GroupManager;
//GM.sayhello(); //->ok---> should be sayHelloGeneral()
GM.sayHelloGeneral();
GM.helloGroupManager(); //--> ok
GM.sayHelloForm(); //->Works fine too

Node Module Inheritance

I wrote the below listed module for an ExpressJS application. I now need to create a similar module with about 3 changed methods, and a few different instance variables. My plan is to create a superclass that has all the common (call it Common.js) and then require it for the two or more subclasses.
I generalized pointer to a tutorial might help me, but here are my specific questions:
the requires will be common, I suppose I put them in Common.js,
right?
I assume I should promote as many instance variables (the subclasses) into Common as possible?
The following could be a template fro the subclasses, with the Object.create coming at the top of the file
SubClass snippet:
var Common = require("./Common");
SubClass.prototype = Object.create(Common.prototype);
SubClass.prototype.subMethod = function() {....}
and also I assume that any submethod can refer to variables in the superclass, as well as new variables in the subclass, with as this.variableName,
BTW, how would I create new subClass instance variables?
Here is my original Code:
var _ = require('lodash');
var path = require('path');
var fs = require('fs');
var tools = require("../tools/tools");
var Job = require("./falconJob");
var Batch = function (ticket) {
this.counts = [];
this.maxes = [];
this.errors = [];
this.done = [];
this.jobs = 0;
this.started = Date.now();
this.ended = Date.now();
this.jobBatch = {};
this.ticket = ticket;
this.batchRoot = null;
}
Batch.prototype.setup = function (frameList, req, next) {
this.group(frameList);
this.makeRoot(req, next);
}
Batch.prototype.group = function (list) {
_.forEach(list, function (obj) {
if (this.jobBatch[obj.type] == undefined) {
this.jobBatch[obj.type] = [];
}
this.jobBatch[obj.type].push(obj);
}, this);
};
Batch.prototype.makeRoot = function (req, next) {
var config = global.app.settings.config;
this.batchRoot = path.join(config.JobsPath, this.ticket);
var self = this;
fs.mkdir(this.batchRoot, function (err) {
if (err) return next(err);
var mapInfoFile = path.join(self.batchRoot, "MapInfo.json");
var mapInfo = {
Date: (new Date()).toISOString(),
Version: global.manifestVID,
Zoom: req.body.Zoom,
CenterLat: req.body.CenterLat,
CenterLon: req.body.CenterLon
};
fs.writeFile(mapInfoFile, tools.pretty(mapInfo), function (err) {
if (err) return next(err);
return next(null);
});
});
};
Batch.prototype.spawn = function () {
_.forEach(this.jobBatch, function (files, key) {
var job = new Job(key, files, this.batchRoot, this.ticket, this);
this.begin(job);
job.exec();
}, this);
};
Batch.prototype.count = function () {
var sum = 0;
for (var key in this.counts) {
sum += this.counts[key];
}
return sum;
}
Batch.prototype.total = function () {
var sum = 0;
for (var key in this.maxes) {
sum += this.maxes[key];
};
return sum;
}
Batch.prototype.fails = function () {
var sum = 0;
for (var key in this.errors) {
sum += (this.errors[key]) ? 1: 0;
};
return sum;
}
Batch.prototype.finished = function () {
var keylist = Object.keys(this.done);
if (keylist.length == 0) return false;
for (var key in this.done) {
if (this.done[key] == false) return false;
};
if (this.jobs != 0) return false;
return true;
}
Batch.prototype.rate = function () {
var speed = (this.count() * 1000) / (this.ended - this.started); // tiles / second
return speed;
}
Batch.prototype.begin = function (job) {
var type = job.type;
this.jobs++;
this.counts[type] = 0;
this.maxes[type] = 0;
this.errors[type] = false;
this.done[type] = false;
}
Batch.prototype.end = function (job) {
type = job.type;
this.jobs--;
this.errors[type] = job.errors;
this.done[type] = true;
}
Batch.prototype.update = function (status) {
type = status.layer;
this.ended = Date.now();
this.counts[type] = status.tilesCount;
this.maxes[type] = status.tilesMax;
this.done[type] = status.done;
}
module.exports = Batch;
I am surprised, no one answered. Well I have a solution, and a few tips. First, read the Mozilla developer page about an introduction to javascript inheritance: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript
Here is how I structured my "sub-module" which I can just require in and it will pull in the super-module, and then subclass it.
var _ = require('lodash'); // require any additional modules that your sub module needs
var BatchRoot = require('./Batch'); // require the super-module with the superclass
var Job = require("./falconJob"); // another module that I need
var Batch = function (ticket) {
BatchRoot.call(this, ticket); // The superclass constructor takes "ticket" as a param
// define new subclass instance variables here, e.g. this.foobar = 33;
}
Batch.prototype = new BatchRoot(); // This does the subclassing
Batch.prototype.constructor = BatchRoot; // MDN says to do this to correct the constructor pointer because it points to Batch
// this is a new subclass function, notice that I use Job which is only defined here
Batch.prototype.spawn = function () {
_.forEach(this.jobBatch, function (files, key) {
var job = new Job(key, files, this.batchRoot, this.ticket, this);
this.begin(job);
job.exec();
}, this);
};
module.exports = Batch;

How to "new" a returned function in Javascript

I am trying to simulate a namespace feature in Javascript.
var com = {};
com.domain = {};
com.domain.system = {};
com.domain.net = {};
com.domain.net.ip = {};
com.domain.net.ip.tcp = {};
com.domain.net.ip.udp = {};
com.domain.net.ip.ssl = {};
com.domain.util = {};
com.domain.util.timer = {};
com.domain.plugins = {};
com.domain.session = {};
com.domain.io = {};
com.domain.algorithm = {};
com.domain.debug = {};
This is the namespaces declaration. Later I will add functions to these namespaces.
This is my selector function:
For a convenient way to use namespaces, I add a function named $. This function will walk all namespaces in com. If the selected name exists, return the object.
function $ (selector) {
function digger (namespace, selector) {
for (var prop in namespace) {
if (typeof namespace[prop] == "array" || typeof namespace[prop] == "object") {
if (prop == selector) {
return namespace[prop];
}
var dig = digger(namespace[prop], selector);
if (dig != null) {
return dig;
}
} else {
if (prop == selector) {
return namespace[prop];
}
}
}
}
return digger (com, selector);
}
After that, I add a timer to namespace com.doamin.util.
com.domain.util.timer = function () {
this._handle = new InnerObj.SystemTimer(io);
return this;
};
com.domain.util.timer.prototype.expiresFromNow = function (seconds, cbHandler) {
this._handle.ExpiresFromNow (seconds, cbHandler);
};
com.domain.util.timer.prototype.wait = function (seconds, cbHandler) {
this._handle.Wait (seconds, cbHandler);
};
com.domain.util.timer.prototype.expiresAt = function (seconds, cbHandler) {
this._handle.Wait (seconds, cbHandler);
};
com.domain.util.timer.prototype.cancel = function () {
this._handle.Cancel ();
};
Usage:
1. var timer = new com.domain.util.timer (); OK
timer.expiresAt (1, {}); OK
2. var func = $("timer"); OK
var timer = new func (); OK
timer.expiresAt (1, {}); OK
But but but but but
var timer = new $("timer") (); NG
Can anyone tell me why the last new function is not working?
Try var timer = new ($("timer"))();.
Your question is not clear but I guess since $("timer") returns a function, you want a new instance of the result of $("timer") and not a new instance of $().

Categories

Resources