Basic JavaScript object structure help accessing properties - javascript

(function (window, document, $, undefined) {
"use strict";
//we cache a few useful values, like jQuery wrapped window and document
var $window = $(window),
$document = $(document),
ProjectManagement = {
// Cache Properties
cache: {
jsonPromiseCache: {},
},
init: function(){
console.log('ProjectManagement.init() Function ran');
// Call the User module
ProjectManagement.modules.User.populateUserlistJson('test userlistJsonData data');
},
modules: {
User: {},
Milestones: {},
Tasks: {},
},
};
// Run Init on DOM Ready
$(function() {
ProjectManagement.init();
});
}(this, document, jQuery));
Based on this exact code structure above. How could I load an Object with its own similar style functions and properties into this property:
ProjectManagement.modules.User
Something like this would be loaded into the above code where I have ProjectManagement.modules.User ...
(function(){
ProjectManagement.modules.User = {
cache: {
userlistJson: '',
},
init: function(){
console.log('ProjectManagement.modules.User.init() Function ran');
},
populateUserlistJson: function(userlistJsonData){
ProjectManagement.modules.User.cache.userlistJson = userlistJsonData;
console.log('ProjectManagement.modules.User.populateUserlistJson(userlistJsonData) Function ran from ProjectManagement.init()', userlistJsonData);
},
getUsers: function(){
},
};
})();
UPDATE with a JSFiddle Demo
http://jsfiddle.net/jasondavis/cpvsado1/
In the demo I get this error Uncaught TypeError: ProjectManagement.modules.User.populateUserlistJson is not a function - line 44
Line 44 is this: ProjectManagement.modules.User.populateUserlistJson('test userlistJsonData data'); which is inside the ProjectManagement.init() function.
If I try to put the code for ProjectManagement.modules.User above the code for ProjectManagement then I get this error below since my ProjectManagement object hasn't been defined yet:
Uncaught ReferenceError: ProjectManagement is not defined - line 25
Line 25 is the start of the user object code: ProjectManagement.modules.User = {
I am looking for help in modifying the ProjectManagement.modules.User code so that I can access functions and properties from this inside my core ProjectManagement object.
MY real project has about 6,000 lines of code and I do not want to completely change its core structure however the structure for my "modules" can be changed as needed to be able to work in the way I described.
IN my core ProjectManagement object I want to be able to simply load the code into the page for each module and can functions and access property data from these module objects inside my ProjectManagement obnject

Main problem here: ProjectManagement is a local variable, so it can't be access from another function.
You can just add this object to window object, and use as global.
JSFiddle
Also you can just pass this object to function:
(function (window, document, $, undefined) {
"use strict";
//we cache a few useful values, like jQuery wrapped window and document
var $window = $(window),
$document = $(document),
ProjectManagement = {
// Cache Properties
cache: {
jsonPromiseCache: {},
},
init: function(){
console.log('ProjectManagement.init() Function ran');
// Call the User module
ProjectManagement.modules.User.populateUserlistJson('test userlistJsonData data');
},
modules: {
User: {},
Milestones: {},
Tasks: {},
},
};
// Run Init on DOM Ready
$(function() {
ProjectManagement.init();
});
window.ProjectManagement = ProjectManagement;
}(this, document, jQuery));
(function(ProjectManagement){
ProjectManagement.modules.User = {
cache: {
userlistJson: '',
},
init: function(){
console.log('ProjectManagement.modules.User.init() Function ran');
},
populateUserlistJson: function(userlistJsonData){
ProjectManagement.modules.User.cache.userlistJson = userlistJsonData;
console.log('ProjectManagement.modules.User.populateUserlistJson(userlistJsonData) Function ran from ProjectManagement.init()', userlistJsonData);
},
getUsers: function(){
},
};
})(window.ProjectManagement);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Related

ember-cli data returned empty using initializer

I have an app where we need to create an initializer that inject our global into all the route where our global is a function that load data from a JSON file and return the data.
global-variable.js
export function initialize(container, application) {
var systemSetting = {
systemJSON: function(){
return Ember.$.getJSON("system/system.json").then(function(data){
return data
});
}.property()
};
application.register('systemSetting:main', systemSetting, {instantiate: false});
application.inject('route', 'systemSetting', 'systemSetting:main');
}
export default {
name: 'global-variable',
initialize: initialize
};
index.js - route
export default Ember.Route.extend({
activate: function(){
var _settings = self.systemSetting.systemJSON;
console.log(_settings.test);
},
}
system.JSON
{
"test" : 100
}
the result of the console.log give me this
ComputedProperty {isDescriptor: true, _dependentKeys: Array[0], _suspended: undefined, _meta: undefined, _cacheable: true…}
I think it's because of the JSON is not loaded yet but after that I try to do something like this at route
index.js - route
activate: function(){
var self = this;
var run = Ember.run
run.later(function() {
var _settings = self.systemSetting.systemJSON;
console.log(_settings);
}, 1000);
},
but still give me the same log. Am I use wrong approach to this problem?
I finally found the answer. Because of what I want to call is from an initializer then one that I must do is to use .get and if I just using get then the one that I received is a promise and to get the actual data I must use .then
The code will look like this:
index.js - route
activate: function(){
this.get('systemSetting.systemJSON').then(function(data) {
console.log(data.test);
});
}

Backbone.js Collection.add method being overwritten by 3rd party script

I am having an issue with a method being overwritten on a dependency that is being injected with Require.
Currently I am have a utility that adds and controls some notifacations across our site that you can see below.
define([
'jQuery',
'Underscore',
'Backbone',
'Data',
'Window',
'text!utilities/notify/templates/utility.html'
], function($, _, Backbone, Data, Window, Template) {
var Notify = Backbone.View.extend({
events: {
'click': 'close'
},
initialize: function() {
var self = this;
_.bindAll(this);
// add notify if not in the dom
var element = $('#notify')[0];
if(_.isEmpty(element)) {
var template = _.template(Template, {});
$('body').prepend(template);
}
Data.add([
// notify object
{
id: 'notify',
addToUrl: false,
addToHistory: false
}
]);
}
return new Notify;
});
(This is only a small portion of this file with the relevant data)
The Data dependency is a wrapper to add a few helper methods to deal with Collections. But we do not overload or modify the add method on the Collection in anyway. The problem I am facing is that in every modern evergreen browser (chrome, firefox, etc) Data is injected correctly and Data.add() works as expected. But in IE8 (sadly I have to support this) the Data.add method sudenly executes a function in Googles Adsense async-ads.js file that we use on our page. When this happens its causes a crazy recursion and IE8 gives a stack overflow message.
I am totally perplexed as to how the Data.add() method is being overwritten by a 3rd party JS function! Any ideas would be greatly appriciated!
Included JS Version Info
Backbone 1.0.0
Require 2.1.2
Underscore 1.3.3
EDIT: I've included the code from the Data utility as requested
/**
#appular {utility} data - designed to store variables for apps that multiple modules may need access too. works closely with the router to keep the url updated as well.
#extends backbone.collection
#define Data
*/
define([
'jQuery',
'Underscore',
'Backbone',
'utilities/data/models/data',
'Cookies'
], function($, _, Backbone, DataModel, Cookies){
var Collection = Backbone.Collection.extend({
model: DataModel,
lastChanged: '',
initialize: function() {
_.bindAll(this);
this.on('add', function(model) {
model.on('change', function() {
this.lastChanged = model.get('id');
this.trigger('dataChanged', model.get('id'));
}, this);
}, this);
},
// Sets data based on url data on initial load (ignores any parameters that are not defined in initialize above)
load: function(data) {
var dataInitialized = _.after(data.length, this.finalizeLoad);
_.each(data, function(dataArray) {
var model = this.get(dataArray[0]);
if(!model) {
model = _.find(this.models, function(model) { return model.get('alias').toLowerCase() === dataArray[0].toLowerCase(); });
}
if(model) {
model.set({value: decodeURIComponent(dataArray[1])}, {silent: true});
}
dataInitialized();
}, this);
},
finalizeLoad: function() {
var triggerInitialized = _.after(this.length, this.triggerInitialized);
_.each(this.models, function(model) {
if(model.get('getFromCookie')) {
var cookieName = null;
if(model.get('alias') !== '') {
cookieName = model.get('alias');
} else {
cookieName = model.get('id');
}
model.set({value: Cookies.get(cookieName)});
}
if(model.get('isArray') && _.isString(model.get('value'))) {
var value = model.get('value');
model.set('value', value.split(','));
}
triggerInitialized();
}, this);
},
/**
#doc {event} initialized - fires when all data has been loaded
*/
triggerInitialized: function() {
this.trigger('initialized');
},
/**
#doc {function} getValueOf - shortcut to get model's value
*/
getValueOf: function(name) {
return this.get(name).get('value');
},
/**
#doc {function} setValueOf - shortcut to set model's value
*/
setValueOf: function(name, value) {
return this.get(name).set('value', value);
}
});
return new Collection;
});

Why can I not use this.posts out side the success function?

Maybe I am not understanding scoping but in the following:
AisisWriter.Routers.Posts = Backbone.Router.extend({
writer_posts: null,
posts: null,
routes : {
'': 'index'
},
initialize: function() {
this.writer_posts = new AisisWriter.Collections.Posts();
},
index: function() {
var self = this;
this.writer_posts.fetch({
reset: true,
success: function(collection, response, options){
this.posts = collection;
console.log(this.posts);
}
});
console.log(self.posts)
}
});
inside the success: function(){} the this.posts console log has two posts in it. it looks like:
child {length: 1, models: Array[1], _byId: Object, constructor: function, model: function…}
But when I try and use this.posts out side the fetch call, it returns null. Why is that? Is this not scoped properly? or am I doing something wrong?
You are not being able to get access to your this.posts only because it is executed sooner than you get the response. You even don't have to save 'this' in the self variable. To check it just add in the initialize function this line:
this.listenTo(this.writer_posts, 'reset', this.test);
And then create test function:
test: function() { console.log(this.posts); }
As you will see collection is saved properly.
Since your fetch might take time to get into success promise the next line is getting executed sooner before that.
index: function() {
var self = this;
this.writer_posts.fetch({
reset: true,
success: function(collection, response, options){
//write your callback function inside the success
//self.afterSuccess(collection);
}
});
},
You can pass the parameters for the function and fetch it.
afterSuccess: function(collection) {
console.log("the collection has"+JSON.stringify(collection));
}

Async loading a module property

I've defined a module (module1) which is supposed to load the value of a property asynchronously. How can I use this property in my app as soon as it is defined and only after it is defined?
My setup (simplified)
v1
app.js
require(['module1'], function(mod) {
document.getElementById("greeting").value = mod.getPersonName();
});
module1.js
define(['jquery'], function($) {
_person;
$.get('values/person.json')
.done(function(data) {
_person = data
});
return {
getPersonName: function() { return _person.name; }
}
values/person.json
{ name: 'John Doe', age: 34 }
This only works if the GET happens nearly instantaneously, otherwise it fails because _person is undefined when getPersonName is called.
v2
To counter this, I figured I would register a callback to notify the app when person was loaded.
app.js
require(['module1'], function(mod) {
mod.onPersonLoaded(function() {
document.getElementById("greeting").value = mod.getPersonName();
});
});
module1.js
define(['jquery'], function($) {
_person;
_onLoaded;
$.get('values/person.json')
.done(function(data) {
_person = data;
_onLoaded();
});
return {
getPersonName: function() { return _person.name; },
onPersonLoaded: function(cb) { _onLoaded = cb; }
}
}
This works if the GET is slow, however, if it's quick _onLoaded is undefined when .done() is called.
Is there a good way to use _person values in app.js as soon as they are defined and only once they are defined?
I'm using RequireJS, but my question is generally applicable to AMD.
Edit
In simplifying my example, I removed a layer which may be important. I'm using RactiveJS for the UI.
Setup (slightly less simplified)
app.js
require(['Ractive', 'module1'], function(Ractive, mod) {
var ractive = new Ractive({
...
data : {
name: mod.getPersonName()
}
});
ractive.observe(...);
ractive.on(...);
});
Edit 2
My current solution, subject to change. Register a callback that notifies app.js when person is loaded. Callback is called immediately if person is already loaded when callback is registered.
app.js
require(['Ractive', 'module1'], function(Ractive, mod) {
var ractive = new Ractive({
...
data : {}
});
mod.watchPerson(function() {
ractive.set('person.name', mod.getPersonName());
});
ractive.observe(...);
ractive.on(...);
});
module1.js
define(['jquery'], function($) {
_person;
_onLoaded;
$.get('values/person.json')
.done(function(data) {
_person = data;
try {
_onLoaded();
} catch (e) {
// that was fast!
// callback will be called when it is registered
});
return {
getPersonName: function() { return _person.name; },
watchPerson: function(cb) {
_onLoaded = cb;
if(_person != null) {
_onLoaded();
}
}
}
}
Promises are a good choice here because callbacks are always called asynchronously - you never encounter that confusing situation where _onLoaded() gets called before it's designed.
Unfortunately, jQuery's promise implementation doesn't adhere to the Promises/A+ specification, so you don't get that guarantee. If possible, you could use an alternative AJAX library like Reqwest, and do something like
app.js
define(['module1'], function (mod) {
mod.then(function(person) {
// do something with person.name
}
});
module1.js
define(['reqwest'], function (reqwest) {
return reqwest('values/person.json');
});
Using the text loader plugin
Another option, since you're already using AMD, would be to use a loader plugin, such as text:
app.js
define(['module1'], function (person) {
// do something with person.name
});
module1.js
define(['text!values/person.json'], function (personJSON) {
return JSON.parse(personJSON);
});
Using the text loader plugin
In fact there's even a JSON plugin, so you could do away with module1 entirely in this example situation:
app.js
define(['json!values/person'], function (person) {
// do something with person.name
});
This is how I would do this. Basically, it is not much different from your V2, but it adds more incapsulation.
app.js
require(['module1'], function(mod) {
mod.loadPerson(function(person) {
document.getElementById("greeting").value = person.getPersonName();
});
});
module1.js
define(['jquery'], function($) {
return {
loadPerson : function(callback) {
$.get('values/person.json').done(function(data) {
_person = data;
callback({
getPersonName: function() { return _person.name; }
});
});
}
}
}
You may also use promises promises instead of simple callback.

Why does jQuery.data behave different from inside jQuery UI options-events?

I am using jQuery v1.8.3 and jQuery UI v1.9.2. I have implemented the Autocomplete widget this way:
$('#input_id').autocomplete({
create: function (event, ui) {
// Initialize data
$(this).data( 'custom', { property1: 'Hello', property2: { num: 1, funct: function() { ... return value } } );
alert($(this).data('custom').property1) // Display 'Hello'
},
select: function(event, ui) {
alert($(this).data('custom').property1) // Display 'Hello'
},
source: function(request, response) {
alert($(this).data('custom').property1) // Display 'undefined'
alert(this.data('custom').property1) // I get 'TypeError: this.data is not a function'
}
});
Why in the source option I get undefined while in create and select events I get Hello? How should I properly access the number property in the search option context so to get Hello?
You're getting undefined there, because apparently this inside source function refers to anonymous function, not to the INPUT you're assigning data to in create function.
Use other means to access input inside source function.
$('#input_id').autocomplete({
create: function (event, ui) {
// when using "this" here, you're refering to #input_id input
},
source: function(request, response) {
// when using "this" here, you're refering to anonymous function
}
});
To access your data within source function use following:
// ...
source: function(request, response) {
// you don't need to wrap this.element in jQuery again since it is already wrapped
this.element.data('custom').property1
}
Demo for future quick reference: http://jsbin.com/ojesig/1/edit

Categories