How to clean up left over $watches - javascript

We interop our angularJS web components with a jqxGrid. When the user edits in a cell, we create a custom typeahead editor (written in angular). When the editor is destroyed, I noticed that my $watches array doesn't return back to the previous value.
I am creating a new isolateScope for my directive, which I then compile and then append to the DOM element that JQX passes to me when the editor is needed:
var scope = $rootScope.$new(true);
var customEditor = $compile(directive)(scope);
What do I have to do in order to clean up these $watches?

Its likely that the new scope you are creating via
var scope = $rootScope.$new(true);
Is not being destroyed by the jqxGrid once the jqxGrid is done with the editor.
To clean up the watches, you simply need to ensure that a call is made to
scope.$destroy();
The tricky part is figuring out when to execute the destroy call; I believe the jqxGrid should raise events such as beforeEdit and afterEdit which you can subscribe to; the place where the $destroy() call should be made is within an event handler for the afterEdit event.

Here is the way to clean up watchers effectively.
Should angular $watch be removed when scope destroyed?
Hope this helps.

Related

Attach a regular JavaScript variable to Angular 2 model (input)?

My code scenario:
I have Angular 2 app which has, out of multiple fields, one input field (for lookup), that opens a decade old URL in a popup window (using window.open()) to get some lookup data. And it calls back a function from the window object of the parent page.
The function is defined in index.html page of the Angular 2 application like this:
<script>
function handler(res) {
var value = res;
}
</script>
The need:
The variable value now has to be tied/bound to the input's model named inputModel. Is there way by which this can be achieved?
Please Note: I am aware that this is not a good practice of having <script> tags, everything has to be component based. But its how I have received the task :-P
Thank you!
You can store the value on the window object like this: window['value'] = res;
Now, you can access the value of res inside your angular components like this window['value']
But, the problem now is that this will only work if the handler has already been invoked by the time the angular component reads the value from the window object. Your angular component will not be notified of any subsequent handler calls which update the value property in the window object.
So, you need a mechanism for the global handler function to notify your angular component that the value property on your window object has been updated.
Here is one way of communicating with your angular component from outside angular:
Inside your angular component's template, create a hidden input
Attach a (click) handler to this.
In your global handler function, use document.getElementById('hidden input's id').click() to simulate a click.
Now, the (click) handler you created in step 2, which is inside angular's context will get invoked.
Now, every time the global handler function is invoked, the (click) handler within angular's context will be invoked, essentially telling angular to get the updated value from the window object.
Here is a working Plunker
In this plunker, I've attached the global handler function to the html's onclick handler which is outside angular's context to simulate your requirement. This click event will be communicated to the angular component using the approach I mentioned above.

How to detect model parameter change event in mithril.js?

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.

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.

angular.js $destroy event - should I manually unbind?

I'm trying to figure out if angular base automatically unbinds watchers and scope events bound with $scope.$on(...) or $scope.$watch(...) when scope is destroyed?
Suppose I have following code:
$scope.$on('someEvents', handleSomeEvent);
$scope.$watch('someProperty', handleSomePropertyChange);
Do I need to manually unbind these watchers and events when $destroy event is triggered on scope?
According to Angular documentation on $scope:
'$destroy()' must be called on a scope when it is desired for the scope and its child scopes to be permanently detached from the parent and thus stop participating in model change detection and listener notification by invoking.
Also
Removal also implies that the current scope is eligible for garbage collection.
So it seems when $destroy() is called all the watchers and listeners get removed and the object which represented the scope becomes eligible for garbage collection.
If we look at the destroy() source code we'll see a line :
forEach(this.$$listenerCount, bind(null, decrementListenerCount, this));
Which is supposed to remove all the listeners.
As mentioned by #glepretre it applies to the watchers and listeners in the controller. The same doc page listed above says that:
Note that, in AngularJS, there is also a $destroy jQuery event, which can be used to clean up DOM bindings before an element is removed from the DOM.
So if you have specific listeners in the directives you should listen to the $destroy event and do the necessary cleanup yourself
If the scope is in a controller, angular unbind for you. Else you can unbind your event by calling the returned function :
var myevent = $scope.$on('someEvents', handleSomeEvent);
myevent() ; // unbind the event
http://docs.angularjs.org/api/ng/function/angular.bind
As previously answered, Angular indeed takes care of cleaning things for you, whenever possible. So if you do $scope.$on('someEvents', handleSomeEvent);, once the scope is destroyed (eg when you go to another page/view in your app), the event is automatically removed.
One important thing to note though, is that $rootScope is of course never destroyed, unless you quit your app. So if you do $rootScope.$on('someEvents', handleSomeEvent);, you may have to remove the event yourself, depending on where you listen to the event:
if in a controller or directive, then you'll have to remove it manually, else each time you'll instantiate the controller, a new event will be attached, and so handleSomeEvent will be called many times
if in a service, then you do not need to remove it manually, as services are always singleton (note that in Angular service, factory, ... all end up being the same thing)

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

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

Categories

Resources