rivets.js: prepopulate model with data from view on init - javascript

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();

Related

Ember.js - Updating a Bound Property without Triggering Local Observer

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.

Backbone and Rivets: don't immediately update a model instance with values from DOM

Two-way binding with Rivets and Backbone is great. Your Model instance gets updated as the user introduces changes in a form and then you just have to issue a save() on it.
The thing is that sometimes that Model instance is being shown somewhere else and you don't want the changes to be noticed until the save() operation has been successfully processed (or not) on server-side. In another words, sometimes is desirable to delay publishing of changes from DOM to the Model instance.
The best solution we've thought of is to bind Rivets not to the Model instance itself but to a plain object representation of it (via model.toJSON()). Rivets then publishes changes to that plain object and you only have to pass it as the 'attributes' parameter to model.save().
Have you found a better solution/pattern/practice? Thanks in advance
I haven't used Rivets per se but one solution to this sort of problem is to use a 'dirty model', ie a copy of the original model that you pass to Rivets and then when you're ready to save it you save it's attributes to the original model. So:
var dirtyModel = new Backbone.Model( originalModel.toJSON() );
then you pass that model to Rivets or whatever else you are using and then you're ready to save it you do:
originalModel.save( dirtyModel.toJSON() );
That way you escape the trap of two-way binding when you don't want it...

backbone.js and states that are not in models

In the data-driven paradigm of Backbone, the backbone views/routers should subscribe to model changes and act based on the model change events. Following this principle, the application's views/routers can be isolated from each other, which is great.
However, there are a lot of changes in the states of the applications that are not persisted in the models. For example, in a to-do app, there could be buttons that lets you look at tasks that are "completed", "not completed", or "all". This is an application state not persisted in the model. Note that the completion state of any task is persisted, but the current filter in the view is a transient state.
What is a good way to deal with such application state? Using a plain, non-backboned state means that the views/routers cannot listen to the changes in this state, and hence become difficult to code in the data-driven paradigm.
Your buttons filter example can be properly solved using Model events.
I suppose your buttons handlers have access to the tasks Collection. Then filter the collection and trigger events over the selected Models like:
model.trigger( "filter:selected" )
or
model.trigger( "filter:un-selected" )
The ModelView can be listening to these events on its Model and acts accordingly.
This is following your requirements of respecting the not use or "attributes that are not persistent" like selected but I don't have any trauma to use special attributes even if they shouldn't be persistent. So I also suggest to modify the selected attribute of your Models to represent volatile states.
So in your buttons handlers filter the collection and modify the selected attribute in your Models is my preferred solution:
model.set( "selected", true )
You can always override Model.toJSON() to clean up before sync or just leave this special attributes to travel to your server and being ignored there.
Comment got long so I'll produce a second answer to compare.
First, I feel like "completed", "not completed" are totally item model attributes that would be persisted. Or maybe if your items are owned by many users, each with their own "completed" "not completed" states, then the item would have a completedState submodel or something. Point being, while #fguillen produced two possible solutions for you, I also prefer to do it his second way, having models contain the attributes and the button / view doing most of the work.
To me it doesn't make sense for the model to have its own custom event for this. Sounds to me like a filter button would only have to deal with providing the appropriate views. (Which items to show) Thus, I would just make the button element call a function that runs a filter on the collection more or less directly.
event: {
'click filterBtnCompleted':'showCompleted'
},
showCompleted: function(event) {
var completedAry = this.itemCollection.filter(function(item) {
return item.get('completed');
});
// Code empties your current item views and re-renders them with just filtered models
}
I tend to tuck away these kind of convenience filter functions within the collection themselves so I can just call:
this.ItemCollection.getCompleted(); // etc.
Leaving these attributes in your model and ignoring them on your server is fine. Although again, it does sound to me like they would be attributes you want to persist.
One more thing, you said that using plain non-backboned states sacrifices events. (Grin :-) Not so! You can easily extend any object to have Backbone.Event capabilities.
var flamingo = {};
_.extend(flamingo, Backbone.Events);
Now you can have flamingo trigger and listen for events like anything else!
EDIT: to address Router > View data dealings -------------------//
What I do with my router might not be what you do, but I pass my appView into the router as an options. I have a function in appView called showView() that creates subviews. Thus my router has access to the views I'm dealing with pretty much directly.
// Router
initialize: function(options) {
this.appView = options.appView;
}
In our case, it may be the itemsView that will need to be filtered to present the completed items. Note: I also have a showView() function that manages subviews. You might just be working directly with appView in your scenario.
So when a route like /items/#completed is called, I might do something like this.
routes: {
'completed':'completed'
},
completed: {
var itemsView = ItemCollectionView.create({
'collection': // Your collection however you do it
});
this.appView.showView(itemsView);
itemsView.showCompleted(); // Calls the showCompleted() from View example way above
}
Does this help?

Eventhandler model change in knockout.js

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.

Backbone.js with non RESTful app? (is Backbone.js right for my current project?)

I'm trying to figure out if Backbone.js is the right framework for my current project: a visualization app.
I have a number of questions:
1) State / Routing?
As this is not your typical RESTful app, but rather a visualization application with various chart types and settings for these charts, how do i maintain state in the URL?
Let's say my areaChart model has a number of defaults like this:
AreaChartModel = Backbone.Model.extend({
defaults: {
selectedCountries: [],
year: 1970,
stacked: false
},
initialize: function(){
[...]
}
});
On an update to the model I'd like to serialize some of these attributes so that I can bookmark the specific state: chartApp.html#!year=1970&stacked=false etc.
And vice-versa, when initing the app with this state, how do I "deparam" the url state and set the model? Can I use Backbone's intrinsic routing?
2) Controller and coupling?
It seems as Backbone has a pretty tight view-model coupling?
Is this really how I should bind for example my areaChartView to the model?
AreaChartView = Backbone.View.extend({
initialize: function(){
areaChartModel.bind("change:year", this.render);
}
});
Isn't this normally the role of the controller?
3) Continuation: Model vs. Controller?
Given this scenario:
A change in the "Sidebar" should trigger a sequence of functions:
1) "New data for the current selection should be loaded"
2) "Based on this data, the scales in the Visualization view should be updated"
3) "The visualization view should be rendered"
Where should I place these functions and how can I create an event in the model that I trigger when the state is stable? (i.e. when all the functions have been invoked and it's time to set the view states?)
1) I would use Backbone.js native routing as much as possible using “:params” and “*splats” , read more. You could fit all your queries into the Backbone.js routing but I would personally sacrifice certain things in favor of intuitive UI buttons
e.g. I would have the default as a line bar and you can't preset this with the URL but to change to a stacked graph would be a simple click of a button.
I would probably stray from ever using ? and & in my URL's. I might come back to this point later as it is interesting.
2) Your example is fine and you just need to remember Backbone.js MVC terminology doesn't correlate to traditional MVC.
Backbone Views are essentially the Controller in traditional MVC.
Backbone Controllers are simply a way of routing inside a framework.
The templating engine you use with Backbone.js is the traditional MVC view.
3) Still writing
Regarding question #3, I would create a Model and a View for the slider.
Then I would associate the triggering of the change event on the model to some function in the view that updates the graph's view (like changing the scales). Something like:
var Slider = Backbone.Model.extend({})
var SliderView = Backbone.View.extend({
initialize: function() {
this.model.bind('change', this.render);
}
render: function() {
// load data, change scales, etc.
}
});
var slider = new Slider();
var slider_view = new SliderView({ model: slider });
Maybe a good idea would be to put the bindings in a parent view, that would then dispatch to sub-views, coordinating their work.
Do sit down for a while and consider if maintaining the entire state is at all a good idea ? The key motivations for having url-based state management is being able to support browser based navigation buttons and being able to bookmark a page. In a visualization app, your data would probably change every moment. This is not something you want to persist in your app-url. Do you really want that when a user bookmarks your app and comes back to it three days later - he sees the visualization for three days old data ? For your scenario, assuming I have not misunderstood your requirements, I would recommend to keep the data state in your model itself.
Also regarding synchronization of views with model data, Yes you can code all the binding logic on your own. In that case your View class will take care of setting up the bindings on the first render. And upon subsequent calls to render, which can be invoked in response to any change event in the model, will refresh the DOM/canvas where the visualization is present.
Probably you should be look forward to a plugin for data-synchronization that takes care of much of boilerplate for you. This page lists some of the data-binding extensions available. Orchestrator is another solution that I have been working on, which might be helpful in this regard.

Categories

Resources