Backbone - Get model's idAttribute value from the collection? - javascript

I am creating a generic table view that can be used with different collections. Here is an example of what I'm trying to do:
...
getModelIdAttribute: function () {
return this.collection.__proto__.model.prototype.idAttribute;
},
getModelFromRow: function (HTMLrow) {
return this.collection.get(this.dataTable.row(HTMLrow).data()[this.getModelIdAttribute()]);
},
...
The data() function returns an object with the attributes that were used to create the model, but I want to account for different idAttributes.
Currently, my way works - but __proto__ is deprecated and it seems like there should be an easier way that I am missing?
Thanks

Related

Load includes on existing model

I'm trying to load includes on an existing model in sequelize. In express we pre check the models to see if they exist in the middleware.
So once we're in the actual "controller" we want to run some includes on that existing model that is passed in.
req.models.item.incude([
{model: Post, as: 'posts'}
])
Is there any way to accomplish this?
EDIT:
I know we can do something like this.
return req.models.item.getThing()
.then(function (thing) {
req.models.item.thing = thing;
return req.models.item;
});
But:
My expansions for includes are a dynamic property that come via url parameters, so they are not know ahead of time.
It I return the above you will not see the "thing" in the response. I need it nicely built as part of the original instance.
Something like a .with('thing', 'other.thing'); notation would be nice. Or in the case of sequelize .with({include: ...}); or .include([{model: ...}]);
If the variable req.models.item is already an Instance but without its other related instances ("includes"), then you could include them using something like the following code:
Item.findAll({
where: req.models.item.where(),
include: [{
model: SomeAssociateModel,
}]
})
.then(function(itemWithAssoc) {
// itemWithAssoc is an Instance for the same DB record as item, but with its associations
});
See here for some documentation. See here for a script demo'ing this.
Update: Given the instance, how do I just get the associated models?
To do this just use the automatically generated "getAssociation" getter functions, e.g.:
function find_associations_of_instance(instance) {
return instance.getDetails();
}
I've updated the script to include this as an example. For more information on these functions, see the SequelizeJS docs.

What's the secret to data-binding?

For most JS frameworks and libraries, the value they bring are often in the form of a new structure as to how to build an application (Backbone, React), or new ideas that effectively power-up the language (Angular), or simply the methods they offer are well tested, fast, and really convenient (jQuery).
Usually the ideas and methods they provide are pretty straightforward usage of JavaScript, but with a very clever team behind it that find interesting ways to do things which you can think through and get a solid guess as to how the guts work.
However, I've been unable to think through the ability to two-way bind JS models to view components. What is the secret sauce at the heart of this feature that makes this work? Changing an internal variable from a user input is simple, but what about the reverse? How would you be able to "know" when a JS variable has changed in order to update the display instantly? Surely it can't be polling, so what then?
Whenever a block of your JS runs that angular triggered it will run a digest cycle when the block finishes executing. This basically checks all the values that might of changed and would require updates to the view.
If angular didn't trigger the code then it won't know that something might of changed so your bindings can get out of sync. For example if you run something like this
setTimeout(function() {$scope.myValue = '123'});
Angular won't know that myValue changed and it actually won't update the view. That's why Angular has it's own services for doing everything. e.g. $timeout or $http.
If you have some callback function that Angular doesn't know about then you can manually tell it to check for changes by calling $scope.$apply()
there are several ways to do it. Object.observe is great, but lacks good support. You can poll for values as well, keeping a 2nd copy of the object around to compare. You can also write your own explicit set/get methods to update the model like backbone does.
One neat method i use a lot is using getters/setters to keep the model synced to the dom:
//a demo "model" of data:
model = {
name: "Fred"
};
function change(k,v){alert([k,v]);} // a stand-in change monitor for demo
// iterate model and replace values with getter/setter combos:
Object.keys(model).forEach(function(key) {
var val = model[key];
delete model[key];
Object.defineProperty(model, key, {
get: function() {
return val;
},
set: function(v) {
val = v;
change(key, val);
} //call change upon setting
});
change(key, val); //update view "onload"
}); // alerts "Fred";
//update model (fires change() with "name" and "sally" arguments:
model.name="sally"; // alerts "sally";
the change function is quite simple and for your case should just find elements bound to keys.
the advantage here is that you don't need special custom CRUD methods, you can just modify the object properties via assignment like it's 1999. It also doesn't poll, and works correctly all the way back to IE9 and any other ES5 environments. It's the simplest way to bind JS>DOM (afaik) without custom methods.
It does have some limits: nested objects are tricky to get/set upon, you can't do the whole object at once, you can only "watch" primitives. Arrays are a problem too: you can't really replace expando properties with getters/setters without side-effects. But, upon a relatively flat collection of JSON-safe data, get/set works a charm and needs no complex libs to get operational.
checkout a complete example using this method: http://pagedemos.com/xg3szbguqnwu/4
I can speak to how it's done in Backbone, which has a relatively low-level perspective on data-binding.
It's a combination of 1. the library having control over attribute setter methods 2. invoking callback functions when attributes change (e.g. by dispatching events) in order to update the UI.
The essential pseudocode is this:
class Model:
method set(name, value):
if value != this.attributes[name]
this.triggerEvent('change', name, value)
this.attributes[name] = value
m = new Model()
someInputWidget.onEvent('userChangedInput', function(value) {
m.set(someInputWidget.name, value)
})
m.onEvent('change', function(name, value) {
getInputWidgetByName(name).setValue(value)
})
Backbone does not do any data binding to the UI, but you can refer to Backbone's annotated source for the actual event-dispatching implementation.

Confusion about Models in Backbone + React application

Here's an example that uses Backbone with React.
He defines a Model: var _todos = new Backbone.Model();
And then adds two functions to it:
var TodoStore = _.extend(_todos, {
areAllComplete: function() {
return _.every(_todos.keys(), function(id){
return _todos.get(id).complete;
});
},
getAll: function() {
return _todos.toJSON();
}
});
What I don't understand is why areAllComplete is being applied to a Model instead of to a Collection.
Shouldn't this be a function in a Collection that will get all of its models and check that complete attribute.
Similarly, I would expect getAll to belong to a Collection - get all of its models.
This example seems to replace Collection with Model.
Maybe I don't totally understand how models are used.
That example is using Backbone.Model in a fairly wierd way in my opinion.
This is where it's adding new todos to the store:
var id = Date.now();
_todos.set(id, {
id: id,
complete: false,
text: text
});
}
What it's basically doing is setting every todo-item as an attribute of the Model, using the id as the attribute name. It ends up with _todos.attributes looking something like below
{
"1436600629317": {
"id": 1436600629317,
"complete": false,
"text": "foo"
},
"1436600629706": {
"id": 1436600629706,
"complete": false,
"text": "bar"
}
}
That's the same output you get from _todos.toJSON(). I've no idea why they decided to implement it like that, if they were to try using Backbone.Sync they'd end up with a server API that's not exactly RESTful. It seems strange to use Backbone without leveraging any of the things Backbone provides. There's a reference to the change event here but I don't see it being used anywhere. You could easily reimplement that store using any regular JS object.
The only thing that example seem to be actually using from Backbone is Backbone.Events in the dispatcher. You're totally right that using a Collection would make way more sense because then you could actually make it talk to a REST based server API. That example seems to only use Backbone for the sake of using Backbone.

I want an action that links me to the Object clicked in a list of Objects

I have a simple action that can be attached to list items in an {{#each}} loop, and when that action is triggered, it will link to that instance of the model.
This is what it looks like now
VpcYeoman.SuperTableController = Ember.ArrayController.extend({
actions: {
goTo: function(input) {
this.transitionToRoute('someModel', input);
}
}
});
The action is called on an HTML element like this
{{action 'goTo' this bubbles=false}}
You can see the problem with this in that 'goTo' cannot be reused on other models because it is specifically looking at the 'someModel' model.
Please help me make this action work for whatever the current model is
I tried replacing 'someModel' with a generic 'model' & even 'this.model' but they didn't work.
Do not reply with 'use {{#link-to}}' please. I am aware that this exists and
Before you read this, you should know that I do recommend you use the link-to helper. I normally pass a computed property to the helper when I need it to change based on the model...
I am not sure where you have that action in your code, but you could just compute that path as needed. For example, take this item controller:
App.ItemController = Ember.ObjectController.extend({
getTransitionPath: function () {
return this.get('foo') + '_bar';
},
transitionPath: function () {
return this.get('foo') + '_bar';
}.property('foo'),
actions: {
goTo: function(input) {
//this.transitionToRoute(this.getTransitionPath(), input); // Regular method
this.transitionToRoute(this.get('transitionPath'), input); // Computed property
}
}
});
I also don't know what kind of logic you are looking for inside of those methods, but this pattern should work on a per model basis.
Good luck!

Extending knockout viewmodels with prototype

If a viewmodel is already defined, either manually or automatically with mapping plugin, is there any problem to "extend" (add properties/functions) the viewmodel later in the code by using prototype?
I'm aware of the create callback of mapping plugin, I just want to know if there are any implications of using prototype instead? The reason I'm asking is because I'm generating large parts of the viewmodels from server side code, but sometimes need to extend the viewmodel later than at initial generation.
I don't think there is any problem with this, I work with a large deep view model graph instantiated via the mapping plugin from correspondingly structured JSON and I use prototypes to define an "AbstractViewModel" with useful properties and toJSON "overrides" among other things.
There is no problem with this. Just make sure that the view responds appropriately when there's no data in that particular field in the viewModel.
There seems to be a couple ways of going about this.
For one, you can take a single object view model and utils.extend them via prototype:
ko.utils.extend(ViewModelClass.prototype, {
newPrototype1: function () { ... },
newPrototype2: function () { ... }
}
Or, you can add an extender to knockout and invoke it via the observable object itself:
(http://knockoutjs.com/documentation/extenders.html)
ko.extenders.numeric = function(target, precision) {
...details in link above...
}
...
self.NumericProperty = ko.observable(data).extend({ numeric: 0 });
...
Or create a function that is available to all instances of an observable, observableArray, computed...
(http://knockoutjs.com/documentations/fn.html)
ko.observable.fn.filterByProperty = function(propName, matchValue) {
return ko.computed(function() {
...details in link above...
}, this);
}
I tend to use combinations of these. I like extending prototypes of View Models in a separate file from the VMs and Mappings, so I have a structure like
ViewModel(s).js
ViewModel(s).Mappings.js
ViewModel(s).Prototypes.js
As you'll see, the timespan between these 'answers' is rather large, so some things have changed yet some things remain the same.

Categories

Resources