I know how to make the cache cleared for view :
.state('app.list', {
cache : false,
url: "/lists/:listId",
views: {
'menuContent': {
templateUrl: "templates/listDashboard.html",
controller: 'listDashboardCtrl'
}
}
})
, but I need something else - delete all the cache for the app in controller method. how to do it?
I found a solution, Wrap the clearCache and ClearHistory in a $timeout. Something Like this.
$scope.logout = function(){
$location.path('/signin')
$timeout(function () {
$ionicHistory.clearCache();
$ionicHistory.clearHistory();
$log.debug('clearing cache')
},300)
}
Edit:Changed Timeout seconds
You can use $ionicHistory. From documentation:
clearCache()
Removes all cached views within every ionNavView. This both removes the view element from the DOM, and destroy it's scope.
In your listDashboardCtrl write this:
function listDashboardCtrl($scope, $ionicHistory){
$ionicHistory.clearCache();
}
Well this is an old issue, but for anyone that's coming 2017 or later I will explain what really happens and how to solve it:
The code of $ionicHistory.clearCache():
clearCache: function(stateIds) { return $timeout(function() {
$ionicNavViewDelegate._instances.forEach(function(instance) {
instance.clearCache(stateIds); }); }); }
So, as you can see, it takes 1 parameter cllaed stateIds which is an array of stateId. Indeed i struggled to find out that stateId is nothing more than stateName.
So, let's go deeper. The code of $ionicNavView.clearCache which is used in the line above "instance.clearCache(stateIds)" is:
self.clearCache = function(stateIds) {
var viewElements = $element.children();
var viewElement, viewScope, x, l, y, eleIdentifier;
for (x = 0, l = viewElements.length; x < l; x++) {
viewElement = viewElements.eq(x);
if (stateIds) {
eleIdentifier = viewElement.data(DATA_ELE_IDENTIFIER);
for (y = 0; y < stateIds.length; y++) {
if (eleIdentifier === stateIds[y]) {
$ionicViewSwitcher.destroyViewEle(viewElement);
}
}
continue;
}
if (navViewAttr(viewElement) == VIEW_STATUS_CACHED) {
$ionicViewSwitcher.destroyViewEle(viewElement);
} else if (navViewAttr(viewElement) == VIEW_STATUS_ACTIVE) {
viewScope = viewElement.scope();
viewScope && viewScope.$broadcast('$ionicView.clearCache');
}
}
};
And as you can see in the code, this clearCache DOES NOT CLEAR ALL CACHES, instead, it destroy all cached views that matches a value in the stateIds array. If there's no parameter IT JUST DESTROY THE ACTUAL VIEW.
So the solution for this, using just the Ionic way is to call $ionicHistory.clearCache() with all your state names in an array as parameter.
E.g:
$ionicHistory.clearCache(['login', 'map', 'home']);
I cannot belive any Ionic developer didnt dug into the code before, or missed this simple datail.
I Hope someone takes advantage of this, even being so late.
UPDATE: Just to make it crystal clear, i want to point out where the bug itself is (if we can call it bug), maybe can be handy for devs:
self.clearCache = function(stateIds){
[...]
var viewElements = $element.children();
}
What the whole function does is basically:
Get all elements using JQLite
Loop the elements
Check if an element equals one in the StateIds array and destroy it; go to next element.
Check if element in the loop is cached or active, and in both true cases destroy it
I wont dig deeper into this but debugging it i could see that the elements gotten from var viewElements = $element.children(); is not an array of all your views content, not even the cached ones, intentionally or not it does not loop through out all your states to clear all those that matches 'ACTIVE' or 'CACHED'. If you want it to loop through ALL your states and destroy all cached views and data you need to explicity pass the stateIds array parameter.
Besides there's another strange behavior, because when i was debugging it i saw when the var viewElements array was filled up with 2 elements, and these 2 elements were from the same state, one resolved to 'CACHED' another resolver to 'ACTIVE', even resolving to the 2 types used in the if conditions the cache was not cleared at all.
I personally think that this is somekind wrong implemented or is wide used wrongly. The fact is that there's a lot of people cracking their heads on this and devs don't even give this simple explanation.
Related
I'm would like to know the recommended way to bind to ReactiveCommand's IsExecuting.
The problem is the initial command execution (started at the end of the constructor) is not updating the WPF control using IsLoading as a binding, although subsequent calls work as expected.
Update 2 Add test binding code
This shows the adorner content when IsLoading is true
<ac:AdornedControl IsAdornerVisible="{Binding IsLoading}">
<ac:AdornedControl.AdornerContent>
<controls1:LoadingAdornerContent/>
</ac:AdornedControl.AdornerContent>
<fluent:ComboBox
ItemsSource="{Binding Content, Mode=OneWay}"
DisplayMemberPath="Name"
SelectedValuePath="ContentId"
SelectedValue="{Binding SelectedContentId}"
IsSynchronizedWithCurrentItem="True"
/>
</ac:AdornedControl>
Update
I found this:
https://github.com/reactiveui/rxui-design-guidelines
and figured I should be able to do something like:
this._isLoading = this.WhenAnyValue(x => x.LoadCommand.IsExecuting)
.ToProperty(this, x => x.IsLoading);
but it gives the compilation error:
The type arguments for method
'ReactiveUI.OAPHCreationHelperMixin.ToProperty<
TObj,TRet>(System.IObservable< TRet>, TObj,
System.Linq.Expressions.Expression< System.Func< TObj,TRet>>, TRet,
System.Reactive.Concurrency.IScheduler)' cannot be inferred from the
usage. Try specifying the type arguments explicitly.
I also tried:
this._isLoading = this.WhenAnyValue(x => x.LoadCommand.IsExecuting)
.ToProperty<TheViewModel, bool>(this, x => x.IsLoading);
but get the compilation error:
'System.IObservable< System.IObservable< bool >>' does not contain a
definition for 'ToProperty' and the best extension method overload
'ReactiveUI.OAPHCreationHelperMixin.ToProperty<
TObj,TRet>(System.IObservable< TRet>, TObj,
System.Linq.Expressions.Expression< System.Func< TObj,TRet>>, TRet,
System.Reactive.Concurrency.IScheduler)' has some invalid arguments
and
Instance argument: cannot convert from
'System.IObservable>' to
'System.IObservable'
Original Below
The code listed at the end of my post works for the initial bind by accessing the IsLoading property and it sounds like that kicks off a subscription. But from further reading it seems I should be using WhenAny and I can't seem to figure out what has been put in front of my nose:
ToProperty and BindTo - Get initial value without Subscribing
Adding:
this.WhenAnyValue(x => x.LoadCommand.IsExecuting);
also works, but is there a better way?
I was thinking removing the ObservableAsPropertyHelper as it doesn't seem to be doing much for me and making IsLoading a normal property like:
private bool _isLoading;
public bool IsLoading
{
get { return _isLoading; }
set { this.RaiseAndSetIfChanged(ref _isLoading, value); }
}
And doing something like the following, but it doesn't compile because it is trying to assign a IObservable< bool> to a bool:
this.WhenAnyValue(x => x.LoadCommand.IsExecuting)
.Subscribe(x => IsLoading = x);
Current code:
private readonly ObservableAsPropertyHelper<bool> _isLoading;
public bool IsLoading
{
get { return _isLoading.Value; }
}
LoadCommand = ReactiveCommand.CreateAsyncTask(async _ =>
{
//go do command stuff like fetch data from a database
}
LoadCommand.IsExecuting.ToProperty(this, x => x.IsLoading, out _isLoading);
//works if I have this line
var startSubscription = IsLoading;
LoadCommand.ExecuteAsyncTask();
and figured I should be able to do something like:
You've got the right idea, but the syntax is a bit off, try:
this.LoadCommand.IsExecuting
.ToProperty(this, x => x.IsLoading, out _isLoading);
If you were to do this with objects that can change (i.e. you've got a long expression), there's a special method called WhenAnyObservable that you use instead of WhenAnyValue:
this.WhenAnyObservable(x => x.SomeObjectThatMightBeReplaced.IsExecuting)
.ToProperty(this, x => x.IsLoading, out _isLoading);
I have run into this before and I think what you are experiencing lies here.
ToProperty / OAPH changes
ObservableAsPropertyHelper no longer is itself an IObservable, use WhenAny to observe it.
ObservableAsPropertyHelper now lazily Subscribes to the source only when the Value is read for the first time. This significantly improves performance and memory usage, but at the cost of some "Why doesn't my test work??" confusion. If you find that your ToProperty "isn't working", this may be why.
It is lazy, so you must subscribe to it (i.e. request a value from the property if using OAPH) for it to work. That is why you notice that your var startSubscription = IsLoading; 'fixes' the issue.
Knowing that made it easier for me to determine whether or not this was even an issue, or just something to keep in mind during my unit tests, knowing that in my application these properties would be bound to and hence subscribed to, making it moot in practice. You know, the whole "tree falling in the forest with no one there to hear it" idea.
I think you should stick with the ToProperty that you have, that seems the way to go IMHO.
I am wondering if there's a way to create a promise chain that I can build based on a series of if statements and somehow trigger it at the end. For example:
// Get response from some call
callback = (response) {
var chain = Q(response.userData)
if (!response.connected) {
chain = chain.then(connectUser)
}
if (!response.exists) {
chain = chain.then(addUser)
}
// etc...
// Finally somehow trigger the chain
chain.trigger().then(successCallback, failCallback)
}
A promise represents an operation that has already started. You can't trigger() a promise chain, since the promise chain is already running.
While you can get around this by creating a deferred and then queuing around it and eventually resolving it later - this is not optimal. If you drop the .trigger from the last line though, I suspect your task will work as expected - the only difference is that it will queue the operations and start them rather than wait:
var q = Q();
if(false){
q = q.then(function(el){ return Q.delay(1000,"Hello");
} else {
q = q.then(function(el){ return Q.delay(1000,"Hi");
}
q.then(function(res){
console.log(res); // logs "Hi"
});
The key points here are:
A promise represents an already started operation.
You can append .then handlers to a promise even after it resolved and it will still execute predictably.
Good luck, and happy coding
As Benjamin says ...
... but you might also like to consider something slightly different. Try turning the code inside-out; build the then chain unconditionally and perform the tests inside the .then() callbacks.
function foo(response) {
return = Q().then(function() {
return (response.connected) ? null : connectUser(response.userData);
}).then(function() {
return (response.exists) ? null : addUser(response.userData);//assuming addUser() accepts response.userData
});
}
I think you will get away with returning nulls - if null doesn't work, then try Q() (in two places).
If my assumption about what is passed to addUser() is correct, then you don't need to worry about passing data down the chain - response remains available in the closure formed by the outer function. If this assumption is incorrect, then no worries - simply arrange for connectUser to return whatever is necessary and pick it up in the second .then.
I would regard this approach to be more elegant than conditional chain building, even though it is less efficient. That said, you are unlikely ever to notice the difference.
I am following these examples of C# code. But I am little confused by the Pseudo Code comments all over the place.
For example:
public void addToHead(Object value)
// pre: value non-null
// post: adds element to head of list
{
SinglyLinkedListElement temp =
new SinglyLinkedListElement(value);
if (tail == null) {
tail = temp;
tail.setNext(tail);
}
else {
temp.setNext(tail.next());
tail.setNext(temp);
}
count++;
}
What does Pre and Post mean here?
I've never seen Post used here. I know what Post means in the context of the Web and HTML etc, but not in pure code.
"Pre" indicates an assumption made at the beginning of execution. In this case, it's indicating that the value passed in is assumed to be not null.
"Post" indicates an assumption made at the end of the execution, i.e. what the routine actually does. In this case, when the routine finishes a new element will have been added to the end of the list. If the routine modifies its parameters or has any other side effects, those modifications should be listed in the "Post" as well.
I running into some performance issues with a jquery script I wrote when running in IE so I'm going through it trying to optimize any way possible. Apparently using for loops is way faster than using the jQuery .each method. This has led me to a question regarding the equivalent of $(this) inside a for loop. I'm simplifying what I'm doing in my loop down to just using an attr() function as it gets across my main underlying question.
Im doing this with each(simplified)
var existing = $('#existing').find('div');
existing.each(function(){
console.log($(this).attr('id'));
});
And I've tried rewriting it as a for loop as such:
var existing = $('#existing').find('div');
for(var i = 0;i < existing.length;i++)
{
console.log(existing[i].attr('id'));
}
Its throwing an error saying:
Uncaught TypeError: Object #<HTMLDivElement> has no method 'attr'
You need existing.eq() to get jQuery object, existing[] gives you DOM object. The function attr() should be called with jQuery object but not with DOM (javascript) object.
var existing = $('#existing');
for(var i = 0;i < existing.length;i++)
{
console.log(existing.eq(i).attr('id'));
}
You can use each to get index without for loop.
existing.each(function(index, item){
alert(index);
alert(item);
});
To get the id of an element just do
existing[i].id
Note that you jQuery loop would also be faster as
existing.each(function(){
console.log(this.id);
});
More generally, you should not use attr('id'), especially if you're concerned by performances, as a DOM object has a property id.
.I have to ask you a question before I give my answer, why would you need to perform a loop on a single element, #existing is an Id not, therefore it's a unique element on your page.
you could simply do
$('#existing').prop('id');
In case your have more than one elements, you should be using a class or another attribute, if that is the case, you could use the following:
$.each('.existing',function(index,item){
console.log(item.prop('id'));
});
better use prop() insted of attr() as attr is deprecated
i'm just starting prototype, i was on jquery before.
I can't find easy examples on the internet about how :
Selecting all elements having the same id on a page
(I'm doing this but it only works for the first element : $('mydiv').hide() )
Selecting a div that is contained in another div by their id.
hiding all elements that have myClass class.
As mentioned above you shouldn't have the same ID on a page more then once. Besides being against standards it's a recipe for potential problems since you don't know how your JavaScript will react to it. Uses classes instead.
Selecting all elements having the same
id class on a page (i'm doing this but it
only works for the first element :
$('mydiv').hide() )
Use $$:
$$('.myclass')
Selecting a div that is contained in
another div by their id.
Use $$:
$$('div#outer div#inner')
hiding all elements that have myClass
class.
Use $$, each(), and hide()
$$('.myClass').each(function(d) {
d.hide();
});
$$ is your friend.
A few things i would add.
$$('.myClass').each(function(d) {
d.hide();
});
can be replaced with this:
$$('.myClass').invoke("hide");
Also, be careful with your use of $$, within a page with a large dom it is usually faster to target a parent element with $ and then use select() for your selector
so
$$('div#outer div#inner') etc....
can be rewritten like this:
$('parent_of_inner_and_outer').select('div#outer div#inner') etc....
This isn't particularly pretty, but if you run into a situation like I did recently, where there could potentially be multiple items with the same id on the page and you don't have the ability to change that, then something like this will work. In my case, I at least knew they were all in span tags.
var spans = $$('span');
for (i = 0; i < spans.length; i++) {
var span = spans[i];
if (span.id == 'add_bookmark_image_' + id) {
span.hide();
}
if (span.id == 'is_bookmarked_image_' + id) {
span.show();
}
}