Passing bootstrapped variables and JSON to require.js - javascript

What is the best practice for passing bootstrapped variables within the rendered page (i.e. JSON data or config variables) to require.js so they can be checked for an used by dependancies?
It looks like this could be done by checking the window object (i.e. window.bootstrapped_models but that does not seem very optimal.
app.html - example data within the HTML document
<script>
var config = {
"isAdmin": true,
"userId": 1
};
var bootstrapped_models = {
"groups": [
{
"id": 1,
"name": "Foo"
},
{
"id": 2,
"name": "Bar"
}
]
}
</script>
app.js - example app using require()
require(['jquery', 'GroupCollection'], function($, GroupCollection) {
// extend default config
if (config) {
$.extend(defaults, config);
}
// use bootstrapped JSON here
var collection = new GroupCollection;
if (bootstrapped_models.groups.length > 0) {
collection.add(bootstrapped_models.groups);
}
});

The answer from #timDunham was helpful, but it felt a little overly complicated to me. Playing around with require.js and Lithium (PHP MVC) I came up with the following. It's simple and has worked in each instance I've run into.
<script type="text/javascript">
define('bootstrapData', function () {
return <?php echo json_encode($bootstrapData) ?>;
});
</script>
Which is then available by doing the following:
define(['bootstrapData'], function(bootstrapData) {
console.log(bootstrapData); // Will output your bootstrapped object
});
Obviously the way I'm bringing the data in is language specific, but the rest should be useful regardless of your situation.

Not sure if my method is best practice but I do something a lot like what you are doing except instead of butting the bootstrapped models on the global object, I create a define for it in my HTML page:
<script type="text/javascript">
define("bootstrappedModelJson", function() {
return #Html.Action("Controller", "RenderModel");
});
require({
baseUrl: //etc.
}, ["app"],
function(){
});
</script>
then I have a js file called current.model that can be required by other modules and looks like this:
define(
[
'require',
'model'
],
function (require, Model)
{
var json= require("bootstrappedModelJson");
return new Model(json);
});

You can solve problems with optimize/build by disabling the bootstrapData in your build.js
Like this:
paths: {
bootstrapData: "empty:",

Related

Creation of a global, persistent object into Extjs

Good day all.
I'm into a big project that uses EXTjs (i guess it's 4.0), the project is huge and have several years behind.
I'm not into Extjs so I'm trying to learn what to do and how to do it, and my new task is to create a persistent, global object, available into the whole application in which I need to store some information that are used in different parts of the project (let's say for example that the user can set a particular property of this object to "true" while doing some actions and this "true" it will be used into another viewcontroller to enable some functions, things like this).
so, I've created a new file called userJsonMainModel.js :
Ext.define('Tac3.userJsonMainModel', {
extend: 'Ext.data.Model',
constructor: function() {
var userJsonMainModel = this;
userJsonMainModel.callParent(arguments);
userJsonMainModel.data.tmp = {};
},
testProperty:{foo:"bar"},
testMethod: function (){
console.log("testFunction called");
}
});
and in Application.js :
requires: [
...
'Tac.userJsonMainModel'
],
stores: ['Countries', 'Kpis', 'Dimensions'],
autoCreateViewport: false,
init: function() {
var controller = this
Ext.tip.QuickTipManager.init();
Ext.setGlyphFontFamily('FontAwesome');
var userJsonMainModel = controller.createUserJsonMainModel();
console.log("into init: ", this.userJsonMainModel.testProperty);
...
createUserJsonMainModel: function() {
var controller = this;
controller.userJsonMainModel = Ext.create('Tac3.userJsonMainModel', {
controller: controller
});
console.log("check if the jsonmainmodel exist ",controller.userJsonMainModel.testProperty);
},
this is actually working, now the second step is to access the same object from another view (or its viewcontroller), this is what I've done into a a viewController:
Ext.define('Tac3.view.udesign.UdesignController', {
extend: 'Ext.app.ViewController',
alias: 'controller.udesign',
init: function(view) {
...
console.log("into init: ", this.userJsonMainModel.testProperty);
}
and this is actually throwing a:
Uncaught TypeError: Cannot read property 'testProperty' of undefined
I was pretty sure the objects defined into application.js would be globally accessible, but I guess I'm wrong, or doing something in a wrong way.
since I've found quite no examples on this topic (which is probably because it is not a standard way to do this), I'd like to ask what I'm doing wrong?
Just define a class and require it in your application:
Ext.define('MyApp.Globals', {
singleton: true,
foo: 100,
bar: 'baz'
});

How do you use require() within a map function?

Looking at the docs for CouchDB 1.6.1 here, there is mention that you can use the JS require(path) function. How do you do this? The documentation says path is "A CommonJS module path started from design document root".
My design doc is called _design/data. I have uploaded an attachment to this design doc called test.js, which can be accessed at /_design/data/test.js, and contains the following code:
exports.stuff = function() {
this.getMsg = (function() {
return 'hi';
})()
}
But the following code in my map function:
function(doc) {
try {
var x = require('test.js');
} catch (e) {
emit ('error', e)
}
}
results in this error:
["error", "invalid_require_path", "Object has no property \"test.js\". {\"views\":{\"lib\":null},\"_module_cache\":{}}"]
It looks like require is looking for the path as an object in the docparam... but I don't understand why if it is.
Looking at this link, describing this feature in an older version of CouchDB, it says you can:
However, in the upcoming CouchDB 1.1.x views will be able to require modules provided they exist below the 'views' property (eg, 'views/lib/module')
And gives the following code example:
{
"_id": "_design/example",
"lib": {
// modules here would not be accessible from view functions
},
"views": {
"lib" {
// this module is accessible from view functions
"module": "exports.test = 'asdf';"
},
"commonjs": {
"map": function (doc) {
var val = require('views/lib/module').test;
emit(doc._id, val);
}
}
}
}
But this did not work for me on CouchDB 1.6.1. I get the error:
{message: "mod.current is null", fileName: "/usr/share/couchdb/server/main.js", lineNumber: 1137, stack: "([object Array],[object Object])#/usr/share/couchdb/server/main.js:1137\n([object Array],[object Object])#/usr/share/couchdb/server/main.js:1143\n([object Array],[object Object],[object Object])#/usr/share/couchdb/server/main.js:1143\n(\"views/lib/module\")#/usr/share/couchdb/server/main.js:1173\n([object Object])#undefined:3\n([object Object])#/usr/share/couchdb/server/main.js:1394\n()#/usr/share/couchdb/server/main.js:1562\n#/usr/share/couchdb/server/main.js:1573\n"
In your question you didn't provide the function as a string. It's not too easy to spot, but you must stringify functions before storing them in CouchDB (manually or by using .toString()). Caolan has that error in the post that you linked.
Using this example:
15 views: {
16 lib: {
17 foo: "exports.bar = 42;"
18 },
19 test: {
20 map: "function(doc) { emit(doc._id, require('views/lib/foo').bar); }"
21 }
22 }
Found in older CouchDB docs here: https://wiki.apache.org/couchdb/CommonJS_Modules
I got an example working. Not sure what the difference was really... I was running 'temp' views instead of saving but I don't know why that would have effected the require statement

How do I include a .js file in html loaded with a json?

I need to display an instance of a javascript class that's loaded with data from .json file with using require.js in backbone.
I have the following js and json files:
collections/Companies.js
define([
'models/Company'
], function(CompanyModel) {
'use strict';
var CompanyCollection = Backbone.Collection.extend({
model: CompanyModel
});
return CompanyCollection;
});
data/companies.json
[{
"id": 1000001,
"name": "Test Company 1",
"description": "this is a test company that should be displayed"
},
{
"id": 1000002,
"name": "Test Company 2",
"description": "this is another test company that should be displayed as well"
}]
Also, I have the following code snippet:
<script type="text/javascript" data-main="data/companies.json" src="collections/Companies.js"></script>
<script type="text/javascript">
require(["collections/Companies"],
function(Companies){
$("#json").append(Companies[0].id);
alert("test");
}
);
</script>
<div id="json"></div>
Of course it is not working as I intended and I can't figure out the correct syntax and/or iteration logic.
How can I load the .js class with the data from the .json file?
My <script type="text/javascript" data-main... line doesn't seem to work.
Thanks in advance and sorry if I'm not clear enough.
requirejs is usually used to load scripts and template files but you can use it to load a .json file as well. but you have to parse it yourself using JSON.parse.
You have 2 different options,
using requirejs text plugin, like:
require(["collections/Companies", "text!data/companies.json"],
function(Companies, CompaniesJSONStr) {
var CompaniesJSON = JSON.parse(CompaniesJSONStr);
}
);
or using requirejs plugins, which it again uses text! plugin:
require.config({
paths : {
json: 'data'
}
});
define([ 'collections/Companies', 'json!companies.json' ],
function(Companies, CompaniesJSON){
}
);

Backbone and best practice getting config JSON

I've got a JSON file that looks like this.
{
"config": {
"setting1": 'blabla',
"setting2": 'blablabla'
},
"content": {
"title": "Title of an exercise.",
"author": "John Doe",
"describtion": "Exercise content."
},
"answers": [
{
"id": "1",
"content": "Dog",
"correct": true
},
{
"id": "2",
"content": "Fish",
"correct": false
}
]
}
Than, I create a Backbone View, combined from content model, and answers (which are randomly selected, but It's not most important now).
I've also got a config, which has settings that will determinate which view and collection methods to use.
It seems like a simple task, but as I'm new to Backbone, I'm wondering which is the best way to fetch JSON file, creating one model with url to JSON and than using parse and initialize creating another models and collections (with answers), or using $.getJSON method that will create exactly the models that I need?
I was trying using $.getJSON
$.getJSON(source, function(data) {
var contentModel = new ContentModel(data.content);
var contentView = new ExerciseView({ model: contentModel });
var answerCollection = new AnswersCollection();
_.each(data.answers, function(answer) {
answerCollection.add(answer);
});
var answersView = new AnswersView({collection: answerCollection});
$(destination).html( contentView.render().el );
$('.answers').append( answersView.el );
)};
But It doesn't seem very elegant solution, I know that this application needs good architecture, cause It will be developed with many other Views based on 'config'.
Hope you guys give me some suggestions, have a good day!
I think what you've done works fine and is correct. But you may need to refactor a little bit since "it will be developed with many other Views based on 'config'".
IMHO, the first thing you need to do is to handle failure in your getJson callback to make the process more robust.
Second, it is useful to create a Factory to generate your views because your logic is to generate different views based on the config data from server. So the factory maybe:
contentViewFactory.generate = function(data) {
var config = data.config;
....
var ActualContentView = SomeContentView;
var contentModel = new ContentModel(data.content);
return = new ActualContentView({ model: contentModel });
}
If your logic is simple, you can have a dict map from config to view class like:
var viewMaps = {
"exercise" : ExerciseView,
"other": SomeOtherView,
//....
}
And if every workflow has a AnswersView you can keep that in your getJSON callback. So maybe now your getJSON looks like this:
$.getJSON(source, function(data) {
// keep the config->view logic in the factory
var contentView = contentViewFactory.generate(data);
var answerCollection = new AnswersCollection();
_.each(data.answers, function(answer) {
answerCollection.add(answer);
});
var answersView = new AnswersView({collection: answerCollection});
$(destination).html( contentView.render().el );
$('.answers').append( answersView.el );
})
.fail(){
//some failure handling
};
Furthermore, if you have common logics in you "ContentView"s, it's natural that you can have a "BaseContentView" or "ContentViewMixin" to extract the common logic and use extends to make your code more OO:
Backbone.View.extend(_.extend({}, ContentViewMixin, {
//.....
}
So if someone is trying to add a new ContentView, he/she just needs to add some code in the factory to make the new View be generated by config. Then extends the ContentViewMixin to implement the new View.

Is there an example of setting and retrieving settings from a Rally SDK 2 app?

What does a settings object look like? I can't seem to updateSettings with anything and get something back interesting. I'm printing out this.settings and every time I refresh, it just logs a prototype object with no values.
This is what my test app looks like. I am putting it into a panel inside Rally, not running remotely.
<script type="text/javascript" src="/apps/2.0p2/sdk.js"></script>
<script type="text/javascript">
Rally.onReady(function() {
/*global console, Ext */
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
//Write app code here
console.log( "settings", this.settings );
this.updateSettings( { Name: 'test', Value: Ext.JSON.encode( { test: "blah" } ) } );
}
});
Rally.launchApp('CustomApp', {
name: 'test'
});
});
</script>
Turns out there is a bug in the preview version I was using. And I was trying to pass the wrong kind of preference. Note that preferences are scoped to the App ID and not to the project or workspace. Since it needs the app's ID, it doesn't work when run outside Rally.
The bug is that the updateSettings function is missing a line. You can easily override this by adding the same function to your app definition (isn't it neat that the source is included in the docs?) Just make a function like this:
updateSettings: function(options){
Rally.data.PreferenceManager.updateAppPreferences({
appID: this.getContext().get('appID'),
settings: options.settings,
success: function(updatedSettings){
Ext.apply(this.settings, updatedSettings);
if(options.success){
options.success.call(options.scope);
}
},
scope: this
});
}
So, then, the preference object should be passed like this:
this.updateSettings( {settings: { test: "blah" ) } } );
Then, when it comes back, the getSetting("test") will give me "blah". (It creates a preference with Name equal to "test", Value equal to "blah" and an AppId equal to the current app.

Categories

Resources