I recently started learning mithril.js and I'm wondering how can I make very basic Model -> View one way data binding app.
TestModel = function(data){
this.name = m.prop(data.name)
}
testModel = new TestModel({name: "John"})
code above declare a model and it works perfectly as getter/setter.
but how can I set an event listener for the model event like Backbone's listenTo('model',"change",callbackFunc)?
all sample codes I saw are setting events for actual user actions like click,keyup or onchange.but never listen to actual model value's state directly.
am I missing something or am I understanding how to use mithril.js wrongly?
thanks in advance.
One of the key ideas with Mithril is that changes usually happens after an event:
A user action like onclick or keyup defined in a m() view template
An ajax request made with m.request
Mithril automatically redraws after those, alleviating the need for most listeners.
If you are updating your models through some other method and you need to redraw manually, use m.redraw or m.startComputation / m.endComputation. Thanks to Mithril's DOM diff algorithm, redraws are very cheap so don't be afraid to use them (with some common sense, of course!) Check out the m.redraw documentation for more info.
Related
First of all I know how to set a LoadingMask for a component but have a problem with the uncoupling of the system I am making so I am just looking for a hint/idea.
I am using a MVVC architecture and in the View I have a Container with several Components in it, one of which is a Grid.Panel. The grid is bound to a store and has an event that when fired calls a method of the store. The following code happens in the ViewController:
functionForEvent() {
var store = getStoreForThisGrid();
store.update(Ext.getBody());
}
What happens now is the update() method makes a request to a server, that updates the store itself and the view component, and I need the loading mask during that time. How I handle the situation right now is I pass Ext.getBody() (or a DOM Element representation of a specific component) to the method and it deals with that reference. This function part of the store that is attached to the Grid and resides in the Store:
update : function (el) {
el.mask();
makeRequest();
el.unmask();
}
What I am looking for is another way (Pattern maybe if such exists for JavaScript) to access the View component from the Store instead of passing it around because that does not seem like a good practice and couples the system.
Since I come from a Java background I would have used the Observer pattern but cannot find how to apply this in JS.
I've created an ember component that wraps an editor (CKEditor). The editor's values are updated via setData() and getData() accessors. I want to implement two-directional binding in my ember control so that edits to the component's "content" field flow in and out of the control.
So far, I'm able to get it going one way easily - but my attempts to go bidirectional are very messy. I can set up an observer on the property and have it update the control. However, when I try to set the property when the controller's "change" event is called, it causes the observer to be triggered. That, in turn causes the editors "change" event to trigger and so on. Welcome to Loopy Land.
I know that there are ways to get around this - but everything that I've been trying has me coming up short. It seems hacky - not elegant like the rest of Ember. Can anyone suggest some examples that demonstrates the preferred pattern for this?
Thanks!
--
(Thanks David - Here is some Additional Information)
I've been trying the bound property thing. It works great for outbound updates (from the editor control to another bound textarea on the page) but when inbound the page starts to bog down.
When I initialize the CKEditor, I reference a component that I installed that adds a 'change' event:
editor.on('change', this.updateContent.bind(this));
Here is the update content event:
updateContent: function() {
this.set('_content', this.get('editor').getData());
},
And then, the bound property:
content: function(key, val, previous)
{
if (arguments.length > 1)
{
this.set('_content', val);
var editor = this.get('editor');
if (editor) editor.setData(val);
}
return this.get('_content');
}.property('_content'),
It sounds like you are attempting to update a computed property from your control. If you have a computed property of fullName which depends on firstName and lastName, then it gets confusing if your UI updates the dependencies and not the computed property.
But if you really need to update the computed result, then look at the "Setting Computed Properties" section in the Ember docs (http://emberjs.com/guides/object-model/computed-properties/) and it shows you how you can use the input to the computed property to update its dependencies.
Not sure if this addresses your requirement, but if not pls submit a snippet of what's looping and what needs to be updated.
I have a view that represents a folder. I have bunch of subviews, that this folder view creates, each representing a unique thumbnail in that folder. It turns out that each one of those subview's render method is getting called multiple times (3). Is there a way to find out how view's render method is called. There are different places which could render a trigger event for e.g., if models metadata is changed. It has become a huge mess and I'm looking for a way to debug backbone view's to know what is exactly triggering render method.
The way that I always debug events is:
view.on('all', function(eventName){
console.log('Name of View: ' + eventName);
});
You could do this on views, models or collections.
example:
http://jsfiddle.net/CoryDanielson/phw4t/6/
I added the request and sync methods manually to simulate how backbone would actually perform. The rendered event is custom -- nothing listens to it. Just to show you how/when it happens.
So as you requested, here's an example of how to override the trigger method. Note that you'll have to override it for all types of classes (Model, View, Collection, Router).
var trigger = Backbone.Model.prototype.trigger;
Backbone.Model.prototype.trigger = Backbone.View.prototype.trigger = Backbone.Collection.prototype.trigger = Backbone.Router.prototype.trigger = function(name) {
trigger.apply(this, arguments);
console.log(this, 'triggered the event', name, '.').
}
You could be more specific by overriding each method individually to add the type of object in the log. But you got the general idea.
You might what to give backbone.debug a try. Should give you some insight into what events are being fired.
Perhaps this seems a bit backwards, but I have a view bound with Rivets.js for which I'd like the view to populate the model on initialization.
The usecase is that I'm using server-side rendering to return a snippet (the view) including rivets' data-attributes. So NO JSON is returned from server to client.
Now, by pressing 'edit' a user may put the content in 'edit'-mode, and start editing at will. (Using contenteditable, but this is out of scope here I guess).
So how to make sure the model is populated with values from the view on init?
I know that this question is a little outdated but I recentry tried rivets and I came across the same problem.
The solution:
// In your rivets configuration you disable preload:
rivets.configure({
templateDelimiters: ['[[', ']]'],
preloadData: false
});
// you bind your data
var binding = rivets.bind($('#auction'), {auction: auction});
// you manually publish it once to populate your model with form's data
binding.publish();
And that's it. I still don't know how to disable prelaod per bind
From the example on Rivets website (assign to 'rivetBinding')
var view = rivets.bind($('#auction'), {auction: auction});
doing rivetBinding.publish(); will bootstrap the model with values from the view for all bindings that have 'publishes = true'.
This question is old but it still has no accepted answer, so here goes:
You need to disable the preload configuration so rivets doesn't override whatever is in the input with what you have in your model at the time you do the binding. This can be done via the preloadData=false configuration, either globally (rivets.configure(...)) or view-scoped (third param to rivets.bind(...)).
After the binding, you need to publish the view (pull the values to your model). You also need to set up the observers via sync() call, otherwise your binded methods won't be triggered.
Using the same example as the previous answers:
var view = rivets.bind($('#auction'), { auction: auction }, {
preloadData: false
});
view.publish();
view.sync();
I need to know (in JS) when my model (using knockout.js) or rather a propery has changed.
How do I do that?
Here some code:
function DrawingToolViewModel() {
var self = this;
self.drawMode = ko.observable('Line');
}
model = new DrawingToolViewModel();
ko.applyBindings(model);
Now the assigned HTML element to drawMode will be updated by the model and back, whatever changes. That's fine, but how can I react in JS if something in the model has changed?
EDIT
My question wasn't clear enough, sorry. I know observables but I want to subscribe to ALL properties without doing that for every single property. More like "notify me if something in the model has changed"
If you want to register your own subscriptions to be notified of changes to observables, you can call their subscribe function, for example:
myViewModel.personName.subscribe(function(newValue) {
alert("The person's new name is " + newValue);
});
More details # knockoutjs.com
Summarizing the comments below
To get notified on every change in the ViewModel, check Ryan Niemeyer article and John papa's changeTracker on NuGet
If you want to be notified when a specific property changes then there are several ways of doing what you want. One way is to use the subscribe function:
model.drawMode.subscribe(function(newValue) {
// your js goes in here
});
EDIT
However, if you want to be notified when ANY property changes on your view model then I would take a look at this post for creating a 'dirty flag':
http://www.knockmeout.net/2011/05/creating-smart-dirty-flag-in-knockoutjs.html
This effectively tracks any changes to your view model so you should be able to adapt it to your needs.