I want to run some view code when the model that the view is bound to changes.
My view has an observer on the controller model, like so:
App.SomeView = Em.View.extend
modelDidChange: (()->
# do stuff
).observes('controller.model')
When the model changes, modelDidChange is called twice.
Why is that?
Is there a better/different way to achieve what I'm trying to do here?
Using Ember 1.3.0.
Are you sure that your view needs to know when the model changes? The view's template gets its properties from the controller, and should update automatically when they change. You certainly want to avoid any observers if you can.
But I don't know much about your use case, so assuming that it's necessary, I would try a debouncer. It's likely that the model isn't changed twice, the event is just fired twice. Ember has a LOT of action that happens in between user hooks, so the model is probably just set to the same value twice. Use a debouncer to have your method called just once.
Ember.beginPropertyChanges();
# model updates
Ember.endPropertyChanges();
The first time it fires because you made a change in your browser and that change gets sent to the Ember data store which triggers the observer callback. The store then makes an update request to the backend which responds with an updated model, when the store receives this updated model it triggers the observer callback once again.
Related
It is my understanding that you are not supposed to change states in the render function cause that would cause and infinite re render or the component.
This makes perfect sense, but I find myself in a particular situation. I am building an offline application and I am using an offline storage system to retrieve data. Now, whenever a method is called to get certain data, cache is checked, if it is not expired the component will be able to access the data and therefore nothing happens, but if it is expired there is a call to the API, the data is updated and the interested components re-rendered.
These methods will change the state of the component the first time they are called because they are going to the API grabbing the new data and re-rendering, and then they will not change the state anymore because the data will already be in cache.
Now, I could call these methods in component will mount, and that is what I am doing now, but if I am forced to re call them, I need to unmount and remount the components. Is this the only possible way of doing this?
Thanks!
Well the first step is understanding that state management and rendering needs to be decoupled which you got already.
Now what you can do is consider your external state/cache element as an observable object (ie. I want to do something like observableObject.listen('change', onChangeHandler); you can use EventsEmitter from the events library). You do that listening on componentDidMount and clean up in componentWillUnmout. That onChangeHandler is very simple: this.setState({ value: observableObject.value }) which will trigger a component re-render which should be a pure function that outputs DOM nodes depending on props being passed and it's own state.
This means that your logic of checking if the cache is invalid is not on a per request of the value (inside rendering) but rather think of the object as self contained. It regularly checks if itself needs to notify its listeners that it changed. Since JS does not do parallel execution you don't have to deal with threads and synchronization. You know that at the point in time your render function executes it will have the latest value. If after rendering the logic that checks for cache executes and it sees that it needs to be updated it simply notifies as said earlier its listeners and that makes your component re-render because your onChangeHandler changed the state.
Hope I helped.
I have an object:
NAMESPACE.SOMEOBJECT.VALUE = 0
Some other foreign script owns and changes that value based on an event that I don't have access to. I know that the value is changed by different events on different pages. I can't edit the .js that controls that object. Does anyone have any idea how i can listen to that specific object and then run a function if that value changes?
I cannot add a new library or any external script. I'm using Backbone, require.js and jQuery.
If I were to use a backbone.model and add a reference to this object in my model, and then in my view I were to listenTo my model, would that tell me if that object has been updated? If so, sow would I go about doing this?
I DO have access to an ID on the page that allows me to access a DIV that has the value that I'm looking for. Keep in mind, that every time my object value updates, I need to know if it's been updated.
Unfortunately there's no way to 'add a reference to this object in the model' as this is just another way of saying that something should 'listen' for changes on the object. In order for a model to trigger a change event, one of its attributes has to change and this cannot happen by any means other than calling the model's set method or (re-)fetching (changed) data from the server-side.
How about polling the object instead of listening for an event? You could use setInterval to periodically query the relevant value and track its changes. In the event that the value has changed you can invoke the desired behaviour.
You could also wrap this functionality in a Backbone model if this is a good fit for your app's design: In the model's initialize / constructor use setInterval to start polling the value periodically and use set to copy it onto one of the model's attributes. Backbone will take care of tracking state for you and the model will trigger a change event if (and only if) the value changes.
Hope this helps.
It appears the model hook is not working as documented for RC1. The model hook is not being called when a linkTo is used instead of visiting the item directly by editing the url in the browser.
Given this example app: http://jsfiddle.net/wmarbut/QqDjY/
When visited directly at '/#/edit-item/3', the model hook is called, however when a linkTo call is used to direct the user to the same page, the model hook is not called.
Given the documentation here http://emberjs.com/guides/routing/specifying-a-routes-model/, I can't find anything to explain this. Is this a bug or am I doing it wrong?
Edit
I'm not using Ember Data nor do I plan to.
It appears the model hook is not working as documented for RC1. The model hook is not being called when a linkTo is used instead of visiting the item directly by editing the url in the browser.
This is the exact way it's supposed to work. This is because the model is given via linkTo. When you write {{linkTo posts post}} the model is the 3rd argument. There is no need to call the model hook. The model hook is only executed when entering a state via the URL because it must look up a model.
In your fiddle you have {{#linkTo editItem item.id}}{{item.name}}{{/linkTo}}. You do no need to do that. You should have {{#linkTo editItem item}}{{item.name}}{{/linkTo}}. This will not solve the "problem" however. It will make the lookup automatic.
I am trying to create a web page with form that once user change any field, the validation and update commit immediately rather than letting user to click on submit button. I am using Knockout.js and mapping plugin. I know I can achieve this by creating a computed field for each original fields, but this kind of work is tedius and dumb, is there good practice to create a general listener to listen on any changes in any fields and update backend respectively?
In order to subscribe to any change you could use ko.toJS() method. Actually it allows to walk through object graph and unwrap observables. As your probably know when you use ko.computed it subscribes to all reads of observables fields and re-evaluate on every change. So if you use code like this:
ko.computed(function() {
ko.toJS(viewModel);
// update data on server
});
Also you should pay attention that this piece of code will update data on server right after initialization. It could be easily avoided. Please checkout this example: http://jsfiddle.net/UAxXa/embedded/result/
Also I think you might want to send only changed data to server. You could incorporate ko.editbales plugin ( https://github.com/romanych/ko.editables ) and some KO under-hood knowledge. Please checkout this sample: http://jsfiddle.net/romanych/RKn5k/
I hope it could help you.
You've got several options but if you want a single listener, one good way is to use the same code I used to create a change tracker. It simply listens for the observable changes. Its about 19 lines of code. You can grab this and instead of using it for change tracking, just wire in a method that saves the changes when they occur.
NuGet http://nuget.org/packages/Knockout.ChangeTracker
Codeplex http://kochangetracker.codeplex.com/
To Setup change tracking, add this tracker property to your view model:
viewModel.tracker = new ChangeTracker(viewModel);
Hook these into your view to determine when changes occur:
viewModel.tracker().somethingHasChanged();
Hook this into your view model when you want to reset state in functions (ex: load, save):
viewModel.tracker().markCurrentStateAsClean;
Optionally, you can pass your own hashFunction for state tracking, too.
If I have done my homework correctly, I have come to learn that Backbone does not have built-in save event that is triggered when a model is saved using the model's save method (even though there is a destroy event).
I have also learned that Backbone has a nifty mechanism for creating custom events using the Backbone.Events object. Using the latter works, but I have the impression that it is not fine-grained enough for my needs.
My setup is as follows. I have a table (view) built up of rows (views) with each row having a reference to a model. When the model is saved, I'd like to update/render the row to reflect the changes.
How does one go about creating a save event that is triggered when a model is saved so that the table row (view) that has a reference to that model is updated?
In other words, I'd like to be able to do the following:
this.model.bind('save', this.render);
Just 3 days ago, a commit was made to Backbone that triggers a sync event when the model is successfully saved. This commit hasn't been release yet, though, so you will need to download the source code from the github account if you want to use it.
View = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'onModelSaved');
this.model.bind('sync', onSuccessCallback);
},
onModelSaved: function(model, response, options) {
//perform your after save logic
}
});
As of Backbone.js 1.0.0 you have sync event that is triggered if the model is saved successfully.
this.listenTo(this.model,'sync', this.render);
Note that, the change:attribute is fired first for relevant attributes if there is change in value of the attribute, followed by the change event then followed by the sync event.
sync event is fired irrespective of the change in model. It is to denote that the model is now in sync with server values.
Also these event fire only if the values are valid. i.e models.validate should not return any errors for these values got from the server.