I am trying to do a simple ko binding but get an error if I do the following:
<div data-bind="foreach: collections">
.....
</div>
Here is the js code:
define(
['jquery', 'knockout', 'RestClient', 'Constants'],
function($, ko, ccRestClient, Constants) {
var collections = ko.observableArray([]);
return {
onLoad: function() {
RestClient.request(Constants.ENDPOINT, input,
function(data) {
for (var i = 0; i < data.childData.length; i++) {
var level = {
"firstName": ko.observable(data.childData[i].firstName),
"Id": ko.observable(data.childData[i].Id)
};
categories.push(level);
}
});
}
}
}
);
I get the following error:
Error - collections is not defined
With Knockout, you should use something like ViewModel which is simply an object with properties and functions that you will use in the view. I cannot see that in your code. It should look like:
function ViewModel() {
var self = this;
self.collections = ko.observableArray();
// do what you want with collections
}
In view onload function you should use ko.applyBindings(new ViewModel()) to apply all of your bindings to the view. Only then you will be able to access them with data-bind attributes.
UPDATE
If ko.applyBindings is applied internally, the problem is with the way you declare collections. You made it a private variable with var, but it should be made a property of the model which is applied with ko.applyBindings. If object returned by the function in your code is the model, just make it like this:
return {
collections: ko.observableArray(),
onLoad: //...
}
If not, then I cannot tell you the solution without more details about your application.
First of all, you have a mistype in your js code. See the define
function($, ko, ccRestClient, Constants) {
and then you use RestClient, not ccRestClient from above
RestClient.request(Constants.ENDPOINT, input,
Replace RestClient with ccRestClient.
As strange as it sounds, removing the use: strict did the trick for me.
Related
I declare a instance related dictionary.
Template.newMessage.onCreated(function () {
var self = this;
self.dict = new ReactiveDict('namedDic');
});
And access it via Template.instance().dict.get() in helpers and events.
However, autoform nests another template somewhere so my code to access this dict, no longer works:
AutoForm.hooks({
serverCall: {
before: {
method: function (insert, update, current) {
insert.foo = Template.instance().dict.get('foo'); <-- fails
Meteor.call('serverCall', insert);
return true;
}
}
}
});
The doco doesn't seem to mention any way to access the parent template and I don't want to start guessing how many levels of parents it is.
If it makes any difference, my markup looks like this:
+quickForm id='newMessage' schema='Schema.CustomMessage' type='method' meteormethod='serverCall'
This is a bit of a A->B problem, what I want to do is just have autoform generate and validate a few fields, then I can insert some hidden values as part of a helper/event/hook before sending it off to the server.
I'm sure it is not a best approach, but I got it works by setting external variable in the same file scope.
Example:
var myNewMessageTemplate = null;
Template.newMessage.onCreated(function () {
var self = this;
self.dict = new ReactiveDict('namedDic');
myNewMessageTemplate = self;
});
AutoForm.hooks({
serverCall: {
before: {
method: function (insert, update, current) {
insert.foo = myNewMessageTemplate.dict.get('foo'); <-- fails
Meteor.call('serverCall', insert);
return true;
}
}
}
});
What you need to be insure, that hooks and onCreate are in the same file, to be in the same scope.
I've done this in the past like so:
Define a helper to retrieve the ReactiveDict
Template.newMessage.helpers({
foo : function () {
return Template.instance().dict.get('foo');
}
});
Then, I include that helper somewhere in my template. Use class="hidden" if you don't want the user to see it.
<input type="text" class="hidden" value="{{foo}}" data-schema-key="foo" name="foo">
When quickForm submits, I believe it looks for all inputs with a data-schema-key attribute defined (or possibly it looks at the name, you can define both just to be certain.
Using this method, you should not need to define any hooks.
I guess that's the simple question. I'm new in js, especially in Backbone.js.
All I want to know is how I can refer to my function inside jquery function.
getLanguages: function() {
...
return languages;
},
render: function() {
...
$("input[type='checkbox']").bind("change", function() {
// todo: getLanguages
});
}
I tried to get languages via this but, of course, I got checkbox in this case.
Edit:
It's so simple. Many thanks to all!!!
This is a classic problem in Javascript. You need to reference this inside a callback, but this changes to the element being bound to. A cheap way to do it:
render: function() {
var that = this;
$("input[type='checkbox']").bind("change", function() {
that.getLanguages();
});
}
that will stay defined as the this that render is defined on.
However, you’re using Backbone, and it has more suitable ways to handle this situation. I don’t know the name of your Backbone.View class, but here’s an example adapted from the documentation:
var DocumentView = Backbone.View.extend({
events: {
"change input[type='checkbox']": "doSomething"
},
doSomething: function() {
this.getLanguages(); # uses the correct this
}
});
Calling bind inside render is not The Backbone Way. Backbone views are made to handle event delegation without the unfortunate need to pass this around.
Save this object before bind change event in the scope of render function.
render: function() {
var CurrentObj = this;
$("input[type='checkbox']").bind("change", function() {
CurrentObj.getLanguages();
});
}
You can save the appropriate object into a local variable so from the event handler, you can still get to it:
getLanguages: function() {
...
return languages;
},
render: function() {
...
var self = this;
$("input[type='checkbox']").bind("change", function() {
var lang = self.getLanguages();
...
});
}
I'm using Require.js and Backbone, and have a Backbone router module like:
define([
"views/global",
"views/project/edit",
"views/project/list",
], function(GlobalView, edit, list){
return Backbone.Router.extend({
routes: {
"projects/:action/" : "projectsAction",
},
projectsAction : function(action) {
/* .... lots of code cut out here .... */
/* Create and render the action specified */
this.subView = new eval(action+"()").render();
}
});
});
This is an example, I've cut a lot of setup code out of projectAction.
I would like the URL: /projects/list to run projectAction, with the action param = list, and then the list module from the Require.js function to be called. I'm currently doing it with eval(), but I'm wondering if there is a better way?
Basically, in Javascript, can you refer to a variable, with another variable name, without using eval()?
I guess a shorter version would be, how do you do:
var name = "Math.random";
name(); // = 0.34343....
Without eval()?
You cannot access a variable having the name in a string. But you can create a mapping:
var actions = {
edit: edit,
list: list
};
And then you can access the function by the key:
projectsAction : function(action) {
this.subView = new actions[action]().render();
}
The best way imo, is to use the require function of requirejs:
projectsAction : function(action) {
/* .... lots of code cut out here .... */
/* Create and render the action specified */
var self = this;
require('views/project/' + action, function(view) {
(self.subView = new view).render();
}
}
As it would also cut the boilerplate from having lots of actions.
I've seen different examples in different articles about how to return a Backbone collection (or View, for that matter) from a RequireJS define. For example:
define(['models/person'], function( person ) {
var personCollection = Backbone.Collection.extend({
model: person,
url: "api/person"
});
// do this?
return new personCollection();
// or this?
//return personCollection;
});
Is there a memory advantage to either approach? Is there standard design pattern that dictates which should be used?
The same question would apply to views, as I've seen them done both ways too.
I would do the second way, because then you would receive reference to "blueprint" not the object itself. In most cases one should want to initialize object creation itself.
Also, even if you want only single instance of collection created I would advise using factory method like this:
define(['models/person'], function( person ) {
var personCollection = Backbone.Collection.extend({...}),
singleCollection,
export = {
getInstance = function (options) {
if (!singleCollection) {
singleCollection = new personCollection(options);
}
return singleCollection;
}
}
return export;
});
And then you could call it like this:
require('models/person', function (person) {
var personCollection = person.getInstance(options);
}
I am attempting to use namespaces to better organize my sites very complicated code. I have:
var myApp = {};
myApp.fp = {
brandCarousel: null,
init: function() {
// initialize brand carousel
this.brandCarousel = new Pluit.Carousel('brand-scroll-outer', { circular: false });
}
};
document.observe("dom:loaded", function() { myApp.fp.init(); });
I am using prototype 1.7 and the Pluit Carousel library.
If I try to call one of Pluit Carousels methods (moveNext) by typing:
myApp.fp.brandCarousel.moveNext()
I get a JavaScript error:
TypeError: Object # has no method 'moveNext'
Is there something I am getting wrong when it comes to scope or namespacing? How can I access this internal function?
Nothing about your particular example looks especially wrong (except that it switches between myApp and masApp (typo?)). Here's a stripped-down, dummy example to show that your approach works (and that your problem must be in another location):
var myApp = {};
myApp.fp = {
brandCarousel: null,
init: function() {
// initialize brand carousel
this.brandCarousel = new (function() {
this.moveNext = function() {
alert("moving!");
};
})
}
};
myApp.fp.init()
myApp.fp.brandCarousel.moveNext()
The above code alerts moving! as expected.