I want to use a recursive Model/Collection structure to model a tree. I am struggling to get the circular reference to work with RequireJS. I use the CommonJs syntax rather than AMD. I get the following error:
Uncaught Error: Module name "Models/TestModel" has not been loaded yet for context: _
TestModel
define(function (require) {
var
// Dependencies.
Backbone = require('backbone'),
TestCollection = require('Collections/TestCollection'),
TestModel = Backbone.Model.extend({
initialize: function (model) {
this.Children = new TestCollection(model.Children);
}
});
return TestModel;
});
TestCollection
define(function (require) {
var Backbone = require('backbone'),
TestModel = require('Models/TestModel'),
TestCollection = Backbone.Collection.extend({
model: TestModel
});
return TestCollection;
});
Is this possible with Backbone and RequireJS?
Related
I've created an UI action using various guides (include Jeff Potts really great ones) successfully and it function exactly as expected - but I want to add that action to the multi-select tool as well. It has been really difficult finding much documentation.
Some things I've tried:
Tried to find out if there was an applicable actionGroup - which there doesn't seem to be.
Tried adding the multi-select tags to my share-config-custom.xml to define the item - it shows up, but I obviously can't seem to use the action ID to reference that action.
My next step was to try and create a js file with a registerAction function in it, which I am able to do and have it run (I can see the console.log dump) but I don't really have any idea how I would go about invoking my repo action from there).
How can I complete this task?
There is already function exist for invoking the repository custom action.This function is defined inside below file.
share-war\components\documentlibrary\actions.js
You can take reference of below code for invoking the repository action.
onActionSimpleRepoAction: function dlA_onActionSimpleRepoAction(record, owner)
{
//ACE-2470 : Clone: Clicking multiple times the simple Workflow approval menu item gives unexpected results.
if (owner.title.indexOf("_deactivated") == -1)
{
// Get action params
var params = this.getAction(record, owner).params,
displayName = record.displayName,
namedParams = ["function", "action", "success", "successMessage", "failure", "failureMessage", "async"],
repoActionParams = {};
for (var name in params)
{
if (params.hasOwnProperty(name) && !Alfresco.util.arrayContains(namedParams, name))
{
repoActionParams[name] = params[name];
}
}
//Deactivate action
var ownerTitle = owner.title;
owner.title = owner.title + "_deactivated";
var async = params.async ? "async=" + params.async : null;
// Prepare genericAction config
var config =
{
success:
{
event:
{
name: "metadataRefresh",
obj: record
}
},
failure:
{
message: this.msg(params.failureMessage, displayName),
fn: function showAction()
{
owner.title = ownerTitle;
},
scope: this
},
webscript:
{
method: Alfresco.util.Ajax.POST,
stem: Alfresco.constants.PROXY_URI + "api/",
name: "actionQueue",
queryString: async
},
config:
{
requestContentType: Alfresco.util.Ajax.JSON,
dataObj:
{
actionedUponNode: record.nodeRef,
actionDefinitionName: params.action,
parameterValues: repoActionParams
}
}
};
// Add configured success callbacks and messages if provided
if (YAHOO.lang.isFunction(this[params.success]))
{
config.success.callback =
{
fn: this[params.success],
obj: record,
scope: this
};
}
if (params.successMessage)
{
config.success.message = this.msg(params.successMessage, displayName);
}
// Acd configured failure callback and message if provided
if (YAHOO.lang.isFunction(this[params.failure]))
{
config.failure.callback =
{
fn: this[params.failure],
obj: record,
scope: this
};
}
if (params.failureMessage)
{
config.failure.message = this.msg(params.failureMessage, displayName);
}
// Execute the repo action
this.modules.actions.genericAction(config);
}
},
I keep seeing this error when executing the compiled file:
Uncaught Error: No json
Here's my current requirejs grunt task configuration:
requirejs: {
options: {
baseUrl: "build/repos/staging/dev",
mainConfigFile: "dev/main.js",
generateSourceMaps: false,
preserveLicenseComments: false,
name: "almond",
out: "./static/js/compiled.js",
//excludeShallow: ['vendor'],
findNestedDependencies: true,
removeCombined: true,
//wrap: true,
optimize: "uglify2",
uglify2: {
output: {
beautify: true,
},
lint: true,
mangle: false,
compress: false,
compress: {
sequences: false
}
}
}
}
And here's my dev/main.js file:
// This is the runtime configuration file.
// It also complements the Gruntfile.js by supplementing shared properties.require.config({
waitSeconds: 180,
urlArgs: 'bust=' + (new Date()).getTime(),
paths: {
"underscore": "../vendor/underscore/underscore",
"backbone": "../vendor/backbone/backbone",
"layoutmanager": "../vendor/layoutmanager/backbone.layoutmanager",
"lodash": "../vendor/lodash/lodash",
"ldsh": "../vendor/lodash-template-loader/loader",
"text": "../vendor/requirejs-plugins/lib/text",
"json": "../vendor/requirejs-plugins/json",
"almond": "../vendor/almond/almond",
// jquery
"jquery": "../vendor/jquery/jquery",
"jquery.transit": "../vendor/jquery.transit/jquery.transit",
"jquery.mousewheel": "../vendor/jquery.mousewheel/jquery.mousewheel",
"jquery.jscrollpane": "../vendor/jquery.jscrollpane/jquery.jscrollpane"
},
shim: {
'backbone': {
deps: ['underscore']
},
'layoutmanager': {
deps: ['backbone', 'lodash', 'ldsh']
},
'jquery.transit': {
deps: ['jquery']
},
'json': {
deps: ['text']
}
}});
// App initialization
require(["app"], function(instance) {
"use strict";
window.app = instance;
app.load();
});
And finally, my dev/app.js file:
define(function(require, exports, module) {
"use strict";
// External global dependencies.
var _ = require("underscore"),
$ = require("jquery"),
Transit = require('jquery.transit'),
Backbone = require("backbone"),
Layout = require("layoutmanager");
module.exports = {
'layout': null,
'load': function() {
var paths = [
// ***
// *** 1- define its path
// ***
'json!config/main.json',
'modules/nav',
'modules/store',
'modules/utils',
'modules/preloader',
'modules/popup',
'modules/login',
'modules/user',
'modules/footer',
];
try {
require(paths, function(
// ***
// *** 2- call it a name
// ***
Config,
Nav,
Store,
Utils,
Preloader,
Popup,
Login,
User,
Footer
) {
// ***
// *** 3- instance it in the app
// ***
app.Config = Config;
app.Nav = Nav;
app.Store = Store;
app.Utils = Utils;
app.Preloader = Preloader;
app.Popup = Popup;
app.Login = Login;
app.User = User;
app.Footer = Footer;
// require and instance the router
require(['router'], function(Router) {
// app configuration
app.configure();
// app initialization
app.Router = new Router();
});
});
} catch (e) {
console.error(e);
}
},
'configure': function() {
var that = this;
// set environment
this.Config.env = 'local';
// Ajax global settings
Backbone.$.ajaxSetup({
'url': that.Config.envs[that.Config.env].core,
'timeout': 90000,
'beforeSend': function() {
},
'complete': function(xhr, textstatus) {
}
});
// Template & layout
_.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g
};
Layout.configure({
// Allow LayoutManager to augment Backbone.View.prototype.
manage: true,
// Indicate where templates are stored.
prefix: "app/templates/",
// This custom fetch method will load pre-compiled templates or fetch them
// remotely with AJAX.
fetch: function(path) {
// Concatenate the file extension.
path = path + ".html";
// If cached, use the compiled template.
if (window.JST && window.JST[path]) {
return window.JST[path];
}
// Put fetch into `async-mode`.
var done = this.async();
// Seek out the template asynchronously.
$.get('/' + path, function(contents) {
window.JST[path] = contents;
done(_.template(contents));
}, "text");
}
});
},
};
});
Any ideas why is that json module not "required" when executing grunt requirejs ?
Thanks in advance.
Not sure if this is still an issue, but from the requirejs optimizer docs (http://requirejs.org/docs/optimization.html):
The optimizer will only combine modules that are specified in arrays of string literals that are passed to top-level require and define calls, or the require('name') string literal calls in a simplified CommonJS wrapping. So, it will not find modules that are loaded via a variable name...
It sounds like the requirejs optimizer doesn't like the require calls being made with a variable that is an array of dependencies.
It also sounds like the requirejs optimizer doesn't like the syntax of require([dependency array], callback) being used within the actual file being optimized.
You may have to refactor your dependency declarations within dev/app.js to conform to this specification. For example, you might be able to use the following refactoring of steps 1 and 2:
var Config = require('json!config/main.json');
var Nav = require('modules/nav');
var Store = require('modules/store');
var Utils = require('modules/utils');
var Preloader = require('modules/preloader');
var Popup = require('modules/popup');
var Login = require('modules/login');
var User = require('modules/user');
var Footer = require('modules/footer');
If this does work, it looks like you'll also have to do something similar for the Router dependency declaration.
Also, a minor addition that you might want to include to your requirejs configuration once you get it running is:
stubModules : ['json']
Since the built file should have the JSON object within it, you won't even need the plugin within the built file! As such, you can reduce your file size by removing the json plugin from it.
I wish to use Meteor to subscribe a few remote publication via DDP. Then show the documents in one template. Here is what I did:
Posts = {};
var lists = [
{server: "localhost:4000"},
{server: "localhost:5000"}
];
var startup = function () {
_.each(lists, function (list) {
var connection = DDP.connect(`http://${list.server}`);
Posts[`${list.server}`] = new Mongo.Collection('posts', {connection: connection});
connection.subscribe("allPosts");
});
}
startup();
This file is at client folder. Every startup, in this example, at browser I have two client collections Posts["localhost:4000"] and Posts["localhost:5000"], both are same schema. I know this format (Collection[server]) is ugly, please tell me if there is a better way.
Is there a way to show these client collections in the same template with reactive. Like this:
Template.registerHelper("posts", function () {
return Posts.find({}, {sort: {createdAt: -1}});
});
I think Connected Client is a big part of the Meteor. There should be a best practice to solve this problem, right?
Solved.
Connect to multiple servers via DDP, then observe their collections reactive via cursor.observeChanges.
Posts = {};
PostsHandle = {};
// LocalPosts is a local collection lived at browser.
LocalPosts = new Mongo.Collection(null); // null means local
// userId is generated by another Meteor app.
var lists = [
{server: "localhost:4000", userId: [
"hocm8Cd3SjztwtiBr",
"492WZqeqCxrDqfG5u"
]},
{server: "localhost:5000", userId: [
"X3oicwXho45xzmyc6",
"iZY4CdELFN9eQv5sa"
]}
];
var connect = function () {
_.each(lists, function (list) {
console.log("connect:", list.server, list.userId);
var connection = DDP.connect(`http://${list.server}`);
Posts[`${list.server}`] = new Mongo.Collection('posts', {connection: connection}); // 'posts' should be same with remote collection name.
PostsHandle[`${list.server}`] = connection.subscribe("posts", list.userId);
});
};
var observe = function () {
_.each(PostsHandle, function (handle, server) {
Tracker.autorun(function () {
if (handle.ready()) {
console.log(server, handle.ready());
// learn from http://docs.meteor.com/#/full/observe_changes
// thank you cursor.observeChanges
var cursor = Posts[server].find();
var cursorHandle = cursor.observeChanges({
added: function (id, post) {
console.log("added:", id, post);
piece._id = id; // sync post's _id
LocalPosts.insert(post);
},
removed: function (id) {
console.log("removed:", id);
LocalPosts.remove(id);
}
});
}
})
});
}
Template.posts.onCreated(function () {
connect(); // template level subscriptions
});
Template.posts.helpers({
posts: function () {
observe();
return LocalPosts.find({}, {sort: {createdAt: -1}}); // sort reactive
}
});
Can I get the name of the currently executing Meteor method (from within
same)? This would be handy for logging.
I inspected this inside a Meteor method. It is an instance of MethodInvocation, and doesn't appear to have anything useful for figuring out the method name.
Seems like it would be easy enough to add the method name to MethodInvocation and callers, but I'm not sure if the maintainers would accept a patch that added a name field to every MethodInvocation instance.
Crossposted here.
It's not ideal, but here's how you could monkey-patch Meteor.methods to get this functionality, like stubailo suggests:
var currentMethod = new Meteor.EnvironmentVariable();
function log(message) {
var method = currentMethod.get();
if (method) {
console.log(method + ": " + message);
} else {
console.log(message);
}
}
var oldMeteorMethods = Meteor.methods;
Meteor.methods = function (object) {
var methods = {};
_.each(object, function (func, name) {
methods[name] = function () {
var self = this;
var args = _.toArray(arguments);
return currentMethod.withValue(name, function() {
return func.apply(self, args);
});
};
});
oldMeteorMethods(methods);
}
Meteor.methods({
example: function (arg1, arg2) {
log("hello");
return doSomethingElse(arg1) + arg2;
}
});
function doSomethingElse(x) {
log("doSomethingElse called with " + x);
return x * 2;
}
// Meteor.call("example", 5, 6) logs:
// "example: hello"
// "example: doSomethingElse called with 5"
If you prefer not to monkey-patch:
defineMethods = function (object) {
var methods = {};
_.each(object, function (func, name) {
methods[name] = function () {
var self = this;
var args = _.toArray(arguments);
return currentMethod.withValue(name, function() {
return func.apply(self, args);
});
};
});
Meteor.methods(methods);
}
defineMethods({
example: function (arg1, arg2) {
log("hello");
return doSomethingElse(arg1) + arg2;
}
});
It's easy with Meteor 1.10.1
You can just use
this.name
e.g
new ValidatedMethod({
name: "pod.create",
validate: new SimpleSchema({
stuff: {
type: String,
}
}).validator(),
run(pData) {
console.log("methodname:" + this.name);
}
});
outputs :
I20210126-08:35:30.120(0)? methodname:pod.create
I've reworked a little bit #user337's answer. Now you can use #name inside method function.
Add this to server code (coffeescript):
currentMethod = new Meteor.EnvironmentVariable()
oldMeteorMethods = Meteor.methods
Meteor.methods = (object) ->
methods = {}
_.each object, (func, name) ->
methods[name] = ->
args = _.toArray(arguments)
this.name = name
currentMethod.withValue name, =>
func.apply this, args
oldMeteorMethods methods
Just my two cents, for a fully working monkey patched version (without underscore).
Inside method function you can use this.currentMethodName now.
Meteor.methods = (object) => {
let methods = {};
let keys = Object.keys(object)
keys.forEach(key => {
methods[key] = function () {
let self = this;
let args = [...arguments];
self.currentMethodName = key
return object[key].apply(self, args);
}
})
oldMeteorMethods(methods);
}
Is there a way to load a single entity of a Backbone collection (from the server)?
Backbone.Collection.extend({
url: '/rest/product'
});
The following code can load the entire collection with a collection.fetch() but how to load a single model? Backbone's documentation says clearly that GET can be done /collection[/id] but not how.
The model has to be declared that way:
Backbone.Model.extend({
url: function() {
return '/rest/product/'+this.id;
}
});
Using it is simple as:
var model = new ProductModel();
model.id = productId;
model.fetch({ success: function(data) { alert(JSON.stringify(data))}});
While we set
url:"api/user"
for the collection, the equivalent for the model is
urlRoot:"api/user"
this will make backbone automatically append the /id when you fetch()
collection.fetch( {data: { id:56 } } )
I did this:
Catalog.Categories.Collection = Backbone.Collection.extend({
fetchOne : function (id, success) {
var result = this.get(id);
if (typeof result !== 'undefined') {
console.log(result, 'result')
success.apply(result);
return;
}
var where = {};
where[this.model.prototype.idAttribute] = id;
var model = new this.model(where);
this.add(model);
console.log(this._idAttr, where, this.model)
model.fetch({success: function () {
success.apply(model);
}});
}
};
Now call it:
collection.fetchOne(id, function () {console.log(this)});
No more guessing if the model is already in the collection!. However, you have to use a call back as you can't depend on an intimidate result. You could use async false to get around this limitation.
just .fetch a Model.
So create a model with it's own .url function.
Something like
function url() {
return "/test/product/" + this.id;
}
I did this:
var Product = Backbone.Model.extend({});
var Products = Backbone.Collection.extend({
model: Product,
url: '/rest/product'
});
var products = new Products();
var first = new Product({id:1});
first.collection = products;
first.fetch();
This has the advantage of working when you're not using a REST storage engine (instead, using something like the HTML5 Local storage, or so forth)