Watching an object in AngularJS - javascript

I am using AngularJs 2 and i am having difficulties watching an object.
I have an object like the following in a directive:
scope.timelineFilter = {hasCapacity: true, onlyActivated: true, companyIdList: []};
I have 2 JQuery functions who change certain values of the object (Soon implementing the third)
$("[name='hasCapacity']").bootstrapSwitch().on('switchChange.bootstrapSwitch', function(event, state) {
scope.timelineFilter.hasCapacity = state;
});
$("[name='activeOnly']").bootstrapSwitch().on('switchChange.bootstrapSwitch', function(event, state) {
scope.timelineFilter.onlyActivated = state;
});
These functions work correctly. If i log the scope.timelineFilter after changing it, i can see the change from true to false (or the other way around)
Now i want to do a call to the backend each time this object changes. So i tried implementing a watcher like this:
scope.$watchCollection('timelineFilter', function() {
console.log("Changed");
}, true);
I did set the third parameter to true for reference checking.
The problem is, this event only fires when the page is loaded but when changing the properties, it does never fire anymore.
Also tried without the third parameter, using scope.$watch with and without the third parameter. Same result.
I searched SO, most people tried to watch arrays with objects, so that isn't viable for me as the backend expects an object.
How can i solve this problem? Is the problem in JQuery? By changing it in the JQuery method it does not register for the watcher?
Thanks!

You are doing things outside of angular's digest cycle. You have to invoke it yourself:
$("[name='hasCapacity']").bootstrapSwitch().on('switchChange.bootstrapSwitch', function(event, state) {
scope.timelineFilter.hasCapacity = state;
scope.$digest();
});
$("[name='activeOnly']").bootstrapSwitch().on('switchChange.bootstrapSwitch', function(event, state) {
scope.timelineFilter.onlyActivated = state;
scope.$digest();
});

Related

select2: Is it possible to pass custom input and access it in methods

I want to pass custom property while creating select2. Example (my custom property being myFilterEnabled):
$('#mySelId2').select2({
myFilterEnabled: false, //Pass my initial state
query: function(query) {
var res = {
results: CityFilter.cities
};
query.callback(res);
}
});
And use it in the query or render functions. Like:
$('#mySelId2').select2({
myFilterEnabled: false,
query: function(query) {
var fltEnabled = this.myFilterEnabled; //Read current state
var res = {
results: fltEnabled ? [] : CityFilter.cities
};
query.callback(res);
}
});
This is so that, there is an initial state for the variable. But, it can change externally, and I want to check that state during each re-render/query.
Edit: Seems I made a mistake before posting. Above code seems to work. I am planning to add a common prefix like 'my' or 'myProj' so that it doesn't conflict with any variables of select2 itself.
Edit2: As mentioned, passing initial state and reading current state are working. I still need a way to change that state from outside. If select2 doesn't have a method for that I could set a data attribute on the element.
This is the full cycle that I wanted:
Set custom state -> Read custom state during query/render -> Change custom state on user action -> Trigger re-render on state change
This is how I managed to do it as of now:
1) I can pass a custom parameter in options while setting up select2
$('#mySelId2').select2({
myFilterEnabled: false,
query: function(query){ ...
2) I am able to read the custom parameter within the callbacks as
this.myFilterEnabled
3) I can set the custom parameter from outside as
$('#s2id_<myId>').data('select2').opts.myFilterEnabled = true;
3) After setting the property as shown above, i want select2 to
re-apply the query function. I can trigger change on
input.select2-input. But, there is a check to prevent re-execution
of query while the text remains the same. So, I go a step further
and call the updateResults function with a 'true' argument. That
forces updateResult to proceed to run query again. Example:
$('#s2id_<myId>').data('select2').updateResults(true);

Does an AngularJS watch fire upon object initialization

I'm currently working on an AngularJS app
I have an object which contains some boolean flags. I created a watch for one of such bool.
Does the watch fire upon object creation? Can the watch fire at random time even the boolean flag hasn't changed?
So we have (I'm working with Gianluca):
scope.$watch("chartData.selectedIndicator", function() {
if (chartData.selectedIndicator !== -1){
highlightMessageIndicator(chartData.selectedIndicator);
}
}, true);
...despite not having updated chartData.selectedIndicator, this watch is still being hit and I am wondering if it is because within our chartData factory we are initialising selectedIndicator and this is why the watch is being hit?
angular
.module("app")
.factory("chartData", [..., chartData]);
function chartData(...) {
var chartData = {
selectedIndicator : -1,
I wonder if this would potentially be a case of just checking for newValue !== oldValue as suggested then?

Adding headers to store's proxy once in ExtJS

I have a store and I need to add some headers to its proxy. I don't want to use defaultHeaders of the Ext.Ajax singleton as these are specific only to a few stores.
The headers use key / value pairs and the value comes from a variable that is NOT loaded when the store is initially loaded, the variable is populated after a successful login.
For this reason I couldn't use a constructor on the proxy or store as the variable I use for value of a header isn't available.
The only way I could get it to work was using the beforeLoad of store. Is this really the best way of achieving this ?
Here's my listener on my store, I am checking if undefined as it's fired every single time.
listeners: {
beforeload: function( store, operation, eOpts ) {
if (this.proxy.headers === undefined) {
this.proxy.headers = {
'X-GType': CompA.Items.getGtype().get('type'),
Does anyone know a better way ?
There doesn't seem to be an event that fires only once.
As a general answer to the problem of "events only firing once" - you can configure your listener to automatically unlisten after one event.
listeners: {
'beforeload': {
fn: function(...),
single: true
}
}
In general it will be better if you create your view after the 'critical' store is loaded. Meaning that you should most likely need to set autoCreateViewport to false in your application main file. Then manually create it in the store success call back. Hook in the init() method to do that.
Example: (pseudo code)
App.js
init: function() {
myImportantStore.load({
success: function() {
// create your viewport
}
});
}

Working with current values of properties of the ViewModel with Knockout-Kendo.js

I recently found this great component -> Knockout-Kendo.js.
I use it to handle some behaviors with kendoComboBox.
The synchronization with the viewmodel works perfectly.
I want to listen changes of the control to execute some actions based on the current selected value.
I don't see any property that I can bind in the 'data-bind' attribute to listen changes but I know that internally, the knockout-kendo component listen changes and this is how the viewmodel is able to by sync with the control.
If I try to listen the valueChange event of the control, the problem is my eventhandler is catched before the viewmodel and while running in my eventhandler, I just have the previous value of the control using the viewmodel.
Look at this binding configuration in the component. What I understand is I'm able to use 'enabled', 'search', 'data', 'value' and any other exposed properties of the telerik control. What would be nice would be to define in data-bind attribute a property 'change' with an eventhandler linked in my viewmodel and be sure my eventhandler would be called after the internal eventhandler of the knockout-kendo component.
createBinding({
name: "kendoAutoComplete",
events: {
change: VALUE,
open: {
writeTo: ISOPEN,
value: true
},
close: {
writeTo: ISOPEN,
value: false
}
},
watch: {
enabled: ENABLE,
search: [SEARCH, CLOSE],
data: function(value) {
ko.kendo.setDataSource(this, value);
},
value: VALUE
}
});
I know I can try to modify the order of bind of events to be sure my eventhandler must be called after the synchronization of the viewmodel but I think it's a very bad practice.
Anybody have an idea how I can solve this problem with elegance?
You haven't mentioned why you want to do this. I can imagine two reasons:
To trigger some UI behavior/logic directly;
To trigger business logic (which may in turn trigger UI changes of course);
For people landing at this question with the latter case, here's an alternative solution. (This answer may not be a straight up answer to the OP's question, but seems useful enough to post it here.)
Suppose you have this basic view model:
var ViewModel = function() {
var self = this;
self.kendoObservable = ko.observable("Some text")
};
There are two ways you can indirectly respond to changes by Kendo. First, for simple cases, there's computed observables:
// Option 1, add this to ViewModel
self.dependentObservable = ko.computed(function() {
return self.kendoObservable() === "" ? "Empty" : "Not empty"; // example
});
This dependentObservable will be modified each time the kendoObservable changes. Basic stuff.
If you want to do something more complex when kendoObservable changes, e.g. do an AJAX call or whatnot, you may need a manual subscription:
// Option 2, add this to ViewModel
self.kendoObservable.subscribe(function(newValue) {
// Possibly do an AJAX call here or whatnot. Example:
alert("The new value is: " + newValue);
});
This will allow you to fire some complex logic each time the kendoObservable changes. AFAIK you need to check yourself whether the newValue is actually a changed value, at least in some versions of KO.

Why does my ko computed observable not update bound UI elements when its value changes?

I'm trying to wrap a cookie in a computed observable (which I'll later turn into a protectedObservable) and I'm having some problems with the computed observable. I was under the opinion that changes to the computed observable would be broadcast to any UI elements that have been bound to it.
I've created the following fiddle
JavaScript:
var viewModel = {};
// simulating a cookie store, this part isnt as important
var cookie = function () {
// simulating a value stored in cookies
var privateZipcode = "12345";
return {
'write' : function (val) { privateZipcode = val; },
'read': function () { return privateZipcode; }
}
}();
viewModel.zipcode = ko.computed({
read: function () {
return cookie.read();
},
write: function (value) {
cookie.write(value);
},
owner: viewModel
});
ko.applyBindings(viewModel);?
HTML:
zipcode:
<input type='text' data-bind="value: zipcode"> <br />
zipcode:
<span data-bind="text: zipcode"></span>?
I'm not using an observable to store privateZipcode since that's really just going to be in a cookie. I'm hoping that the ko.computed will provide the notifications and binding functionality that I need, though most of the examples I've seen with ko.computed end up using a ko.observable underneath the covers.
Shouldn't the act of writing the value to my computed observable signal the UI elements that are bound to its value? Shouldn't these just update?
Workaround
I've got a simple workaround where I just use a ko.observable along side of my cookie store and using that will trigger the required updates to my DOM elements but this seems completely unnecessary, unless ko.computed lacks the signaling / dependency type functionality that ko.observable has.
My workaround fiddle, you'll notice that the only thing that changes is that I added a seperateObservable that isn't used as a store, its only purpose is to signal to the UI that the underlying data has changed.
// simulating a cookie store, this part isnt as important
var cookie = function () {
// simulating a value stored in cookies
var privateZipcode = "12345";
// extra observable that isnt really used as a store, just to trigger updates to the UI
var seperateObservable = ko.observable(privateZipcode);
return {
'write' : function (val) {
privateZipcode = val;
seperateObservable(val);
},
'read': function () {
seperateObservable();
return privateZipcode;
}
}
}();
This makes sense and works as I'd expect because viewModel.zipcode depends on seperateObservable and updates to that should (and does) signal the UI to update. What I don't understand, is why doesn't a call to the write function on my ko.computed signal the UI to update, since that element is bound to that ko.computed?
I suspected that I might have to use something in knockout to manually signal that my ko.computed has been updated, and I'm fine with that, that makes sense. I just haven't been able to find a way to accomplish that.
sigh, I found someone with my exact same problem
If dependentObservables don't notifySubscribers on write, why do they
even bother to do it on read? They get added to the observables list
and subscribed to, but then they never trigger on updates. So what is
the point of subscribing to them at all?
Ryan Niemeyer answers:
I think that for your scenario, dependentObservables may not be the
right tool for the job. dependentObservables are set up to detect
dependencies in the read function and re-evaluate/notify whenever any
of those dependencies change. In a writeable dependentObservable, the
write function is really just a place to intercept the write and allow
you to set any observables necessary, such that your read function
would return the proper value (write is typically the reverse of read
in most cases, unless you are transforming the value).
For your case, I would personally use an observable to represent the
value and then a manual subscription to that observable to update the
original value (the one that you may not have control over).
It would be like: http://jsfiddle.net/rniemeyer/Nn5TH/
So it looks like this fiddle would be a solution
var viewModel = {};
// simulating a cookie store, this part isnt as important
var cookie = function () {
// simulating a value stored in cookies
var privateZipcode = "12345";
return {
'write' : function (val) {
console.log("updated cookie value with: " + val);
privateZipcode = val;
},
'read': function () {
return privateZipcode;
}
}
}();
viewModel.zipcode = ko.observable(cookie.read());
// manually update the cookie when the observable changes
viewModel.zipcode.subscribe(function(newValue) {
cookie.write(newValue);
});
ko.applyBindings(viewModel);​
That makes sense and its somewhat simpler to use. Overall I'm not sure how great of an idea it is to treat a cookie as an observable since the server could edit it in an ajax request, etc.
Try making your internal privatezipcode an observable. See here: http://jsfiddle.net/KodeKreachor/fAGes/9/

Categories