I am writing a dragDirective. Eements are dragged in dragZones. On mouse releases I do a hitTest against all availabe dragZones. I am maintaining a static boolean flag which ends up being false if all hittests return false. In such a situation I would like to resposition the element in the dragZone it originally belonged to. How do I check against this change in variable value?
this._messageBus.listen("dragStart", (obj, event) => {
DragZoneDirective.HITTEST = false;
});
this._messageBus.listen("dragStop", (obj, event) => {
if (this.hitTest(event.x, event.y))
{
//clone object
let clone: Object = JSON.parse(JSON.stringify(obj));
this.dragZoneElems.push(clone);
DragZoneDirective.HITTEST = true;
}
let index = this.dragZoneElems.indexOf(obj);
if (index > -1)
this.dragZoneElems.splice(index, 1);
});
You can't use Angular binding that is checked by Angular change detection on static fields.
You could add a getter on a component that forwards to that static field, then a binding to that getter would be checked by Angulars change detection.
IMHO the preferred way is to use an Observable that emits an event on change. Interested code can subscribe and get notified about updates.
Related
I am using wpf busy indicator and setting its Isbusy property from viewModel. Aftersetting Isbusy property I want to filter my ICollectionview and push it on UI. This Filter operation I have put in
IsBusy = true;
await Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new System.Action(() =>
{
this.MyCollectionView.Filter = item =>
{
Iitem IdName = item as Iitem;
return Regex.IsMatch(IdName.Name, SearchText, RegexOptions.IgnoreCase);
};
}));
Workaround: If I put Task.Delay(200).Wait(); after setting IsBusy, busy indicator will be displayed for some cases.
Filter always runs on the UI thread, keeping it too busy to actually update the UI to reflect the IsBusy state.
In most cases, Filter functions should run fast enough that they don't need any special handling. However, if you're sure that you need to run a time-consuming filter, then the best way to do it is to split into two different collections: MyCollection and MyFilteredCollection. Then, any time MyCollection (or the filter) changes, do something like this:
IsBusy = true;
var items = MyCollection.ToList();
var filter = SearchText;
MyFilteredCollection = await Task.Run(() =>
items.Where(x => Regex.IsMatch(x.Name, filter, RegexOptions.IgnoreCase)));
IsBusy = false;
(this assumes that IsBusy will prevent MyCollection and SearchText from changing).
How will Backbone collections behave if I try to add false, undefined or null -kind of values in them? What events will they trigger?
When a model is added to a collection thru add/set methods, it iterates thru the passed in array and for each element it tries to assign the element passed or an empty object for that element:
From the Source Code:
//Inside the set method of Backbone.Collection
for (i = 0, l = models.length; i < l; i++) {
attrs = models[i] || {};
// Called internally by set method for each new item passed.
_prepareModel: function(attrs, options) {
if (attrs instanceof Model) return attrs;
options = options ? _.clone(options) : {};
options.collection = this;
var model = new this.model(attrs, options);
if (!model.validationError) return model;
this.trigger('invalid', this, model.validationError, options);
return false;
}
Hence for undefined,null and false an empty object is created.
The set method then internally calls the _prepareModel method for each new item passed in the array. This creates a new instance of the backbone model passing the attrs object of the passed in item which is copied to the model. Since attrs is empty object (for null,undefined,false) no new properties are added in this case.
add method (http://backbonejs.org/#Collection-add) :
add event is raised for each new element.If {merge: true} is passed, then appropriate change events will be raised.
set method (http://backbonejs.org/#Collection-set) :
In case of set method appropriate "add", "remove", and "change" events are fired based on the passed in data.
For the given data(undefined,null,false e.t.c) add event will be triggered since new objects are created every time its added to the collection.
<form>
...
<button ng-click...>Save</button>
</form>
<overlay ng-if="saving" style="background-color: #ff0000">
<pindicator size="2em" color="red"></pindicator>
</overlay>
void handleOnSaveClicked(final html.Event e) {
_logger.info("Ready to save:",_tempJob);
saving = true;
// Angular starts to register Listener
_proxy.save(tempJob).then((_) {
// Template/Component has already received "detach"
_logger.info("Job saved!");
editable = false;
saving = false;
});
}
...
void detach() {
_logger.info("Detached!");
}
proxy.save saves tempJob into a list, this list is observed by Angular.
If this list changes Angular updates all the UI-Elements attached to the list.
saving=true should show a message but if proxy.save is so fast that Angular has no chance to register a Listener for "saving" (https://github.com/angular/angular.dart/blob/master/lib/core/scope.dart - Line 352). the whole UI-Block is remove while Angular tries to register the Listener.
This leads to
JobEdit: (15:19:43.096) Ready to save:
JobEdit: (15:19:43.119) Job saved!
JobEdit: (15:19:43.123) Detached!
'package:angular/core/scope.dart': Failed assertion: line 352 pos 12: 'isAttached' is not true.
Any hints to avoid this kind of "race condition"?
I haven't tried it but I think to remember that you should use VMTurnZone to execute async operations that affect the model.
class YourComponent implements ScopeAware {
Scope scope;
VmTurnZone zone;
YourComponent(this.zone);
void handleOnSaveClicked(final html.Event e) {
_logger.info("Ready to save:",_tempJob);
saving = true;
// Angular starts to register Listener
zone.run(() {
return _proxy.save(tempJob).then((_) {
// Template/Component has already received "detach"
_logger.info("Job saved!");
editable = false;
saving = false;
});
});
}
}
The main problem is replacing the list item with a new instance of your class (new object)
Angular tests the objects hashCode if you replace a list item.
If the hashCode is different it deletes the according component, but if the hashCode is the same
it updates the vars in your html-file.
Here it also deletes the component and replaces it with the new device:
...
// hashCode of devices[42] changes
devices[42] = new Device(new Location(40.123,10.567));
But this updates! your component:
...
// hashCode of devices[42] stays the same
final Device device = devices[42];
device.location = new Location(40.123,10.567);
I have a backbone model like -
ModelA = Backbone.Model.extend({
this.set("prop1",true);
})
and View like -
ViewA = Backbone.View.extend({
initialize : function(){
this.listenTo(this.model,"change:prop1",this.changeProp1)l;
this.model.set("prop1",true);
},
changeProp1 : function(){
// callback doesn't call because I'm setting the same value
}
});
var model1 = new ModelA();
var view1 = new ViewA({model:model1});
Here the callback changeProp1 triggers whenever prop1 changes from true -> false -> true .
But I want to listen everytime whenever I'm setting the same value or different value.
I'd say it's best to leave the change event alone, and implement a new set event (or whatever you want to call it). After all, you want to be notified about things that aren't strictly 'changes'.
You could implement your own version of set() in your model which fires a custom 'set' event and then calls backbone's usual set method afterwards.
var MyModel = Backbone.Model.extend({
set: function(key, val, options) {
// Deal with single name/value or object being passed in
var changes;
if (typeof key === 'object') {
changes = key;
options = val;
} else {
(changes = {})[key] = val;
}
options || (options = {});
// Trigger 'set' event on each property passed in
for (var i = 0, l = changes.length; i < l; i++) {
this.trigger('set:' + changes[i], this, this.attributes[changes[i]], options);
}
// Call the usual backbone 'set' method
Backbone.Model.prototype.set.apply(this, arguments);
}
});
and then listen for your new event instead of (or as well as) 'change', where appropriate:
ViewA = Backbone.View.extend({
initialize : function(){
this.listenTo(this.model,"set:prop1",this.changeProp1)l;
this.model.set("prop1",true);
},
However, most of this code is just lifted from Backbone's default set method, and doesn't deal with some other issues such as some option flags and nested events. If you wanted to change the Backbone source itself, the line you want to look for is:
if (!_.isEqual(current[attr], val)) changes.push(attr);
(line 347 in version 1.0.0) and try removing that if clause.
(Code above isn't tested, sorry for any syntax errors)
For implementing above , you have to make changes in change function in backbone.js . Change checks whether the value of the property changed if yes than only it calls the binded function.
i have a simple checkbox
<CheckBox IsChecked="{Binding ForceInheritance}"/>
In Code i have a class with "INotifyPropertyChanged" and the Property
public bool ForceInheritance
{
get { return forceInheritance; }
set
{
if (forceInheritance != value)
{
value = SomeTest();
if (forceInheritance != value)
{
//something is done here.
}
OnPropertyChanged("ForceInheritance");
}
}
}
If SomeTest() returns !value so the underlying data does not have to change the CheckBox still changes its IsChecked state.
For example:
ForceInheritance is false.
CheckBox is clicked.
SomeTest() returns false.
--> The underlying data is not changed
--> I would expect the CheckBox to stay unchecked
The CheckBox gets checked.
What can i do to not change the IsChecked state of the CheckBox when the setter does not actually change the value?
I thought the "OnPropertyChanged("ForceInheritance");" would do the trick but it didn't.
Thank you for your time.
This problems occurs because when you click checkbox it s value is changed. Then binding causes property set. There you manipulate with its value and hope that calling OnPropertyChanged will cause back binding action updating checkbox value. But doesn't work because updating control after updating property may produce infinite updating loop. To prevent this PropertyChanged is ignored.
What can you do is to move some logic of your property setter to binding validation rule. You can read here about validation rules or leave a comment if you need more information or examples.
Hope it helps.
I would expect the CheckBox to stay unchecked
Now, it seems that CheckBox working as you expect. Something must have changed recently because it works differently now.
Previously
When I needed to correct a value in the setter I use Dispatcher.
public bool IsActive
{
get => _isActive;
set
{
if (_isActive != value)
{
if (value)
{
if (!CanActive())
{
Application.Current.Dispatcher.BeginInvoke((Action)(() => IsActive = false));
return;
}
}
_isActive = value;
OnPropertyChanged(nameof(IsActive));
}
}
}
Now
Now, I don't have to change a value back to false. This works even if I remove OnPropertyChanged call, because after the setter is called, the getter is also called. It looks like the CheckBox is now correcting its state.
public bool IsActive
{
get => _isActive;
set
{
if (_isActive != value)
{
if (value)
{
if (!CanActive())
{
return;
}
}
_isActive = value;
OnPropertyChanged(nameof(IsActive));
}
}
}