ion-view cache & states with URL Parameters - ionic-framework

The cache mechanism of the ion-views seems broken when using URL Parameters in the view.
$stateProvider.
.state('a', {
url: '/a',
templateUrl: 'templates/a.html',
controller: 'ACtrl'
})
.state('b', {
url: '/b/:id',
templateUrl: 'templates/b.html',
controller: 'BCtrl'
})
In this example the state 'a' is cached and works as expected. But the state 'b' is just cached if the same id appears twice. So /b/1 and /b/2 results in different ion-view dom elements.
I would expect that the same ion-view dom element is used for all calls to /b/:id.
Is this a bug or works as expected? In my situation I want the "other" behaviour, any idea how to archive this?

You can try this to delete the ui-routing cache.
.run(function($ionicPlatform,$rootScope,$templateCache) {
$rootScope.$on('$viewContentLoaded', function() {
$templateCache.removeAll();
});
})
Worked for me.

Related

How to get data from an API only once (on app creation, outside component or view) in Vue3 SPA, with Pinia store

Is it possible and is it a good practice to avoid fetching data from an API every time the router view is loaded or the component is Mounted?
The thing is that some data rarely changes (like a dropdown options list, imagine allowed animal picks for my app) and it's logical not to send a request every time to a server, instead on app creation would be sufficient.
Tried in App.vue, is that a common thing?
IN APP.vue
import { computed, onMounted, onUpdated, ref } from 'vue';
onMounted(()=>{
axios.get('/data')....
.then((res)=>{
store.property = res.data
...
})
})
I think having it on mount in the App.vue component is acceptable since the App component would not be remounted.
The ideal setup, however, depends on some other parameters like size of application and size of team that's maintaining it. In a large applications you might want to organize things in amore structured and consistent way so you and other folks working on the code know where to find things.
You could consider moving the API call into the pinia action.
store.loadMyData()
// instead of
axios.get('/data')
.then((res)=>{
store.property = res.data;
})
That way you have fewer lines of code in the component. Having "lean" components and moving "business logic" out of components usually makes for better organization which makes it easier to maintain.
Within the action, you can track the state of the API
const STATES = {
INIT: 0,
DONE: 1,
WIP: 2,
ERROR: 3
}
export const useMyApiStore = defineStore('myapi', {
state: () => ({
faves: [],
favesState: STATES.INIT
}),
actions: {
loadMyData() {
this.store.favesState = STATES.WIP;
axios.get('/data')
.then((res) => {
this.store.property = res.data;
this.store.favesState = STATES.DONE;
})
.catch((e) => {
this.store.favesState = STATES.ERROR;
})
},
},
getters: {
isLoaded(){
return this.store.favesState === STATES.DONE;
}
isLoading(){
return this.store.favesState === STATES.WIP;
}
}
})
This is, obviously, more verbose, but allows for the components to be smaller and contain less logic. Then, for example, in your component you can use the getter isLoading to display a loading indicator, or use isLoaded inside another component to determine whether to show it.
Yes, this is a oft used way to load some data into the Vue App.
You could also load data before the Mounting in beforeMount() or created() Lifecycle Hooks (see Vue Lifecycle Diagram) to prevent unnecessary HTML updates.

How to use the nextHandler functionality as shown in the Infinite Ajax Scroll JSON example

I’m hoping to be able to use Infinite Ajax Scroll for a project I’m working on.
I’ve been looking at the Infinite Scroll JSON example here (https://infiniteajaxscroll.com/examples/json/) and I’m finding it difficult to understand how it works. I was wondering if there is any further documentation or code examples on how to use a JS or jQuery handler as shown in the example.
Ultimately what I want to do is load my container "items" using my own ajax function and then have Infinite Ajax Scroll display them. I want to do this because my container "items" are not located at URLs, but are saved as Wordpress transients.
Any help I could get with this would be very much appreciated.
Thanks,
David.
Thank you for your question. The docs on using the nextHandler could indeed use improvement. Regardless, I'll try to explain how it works.
Normally IAS uses a selector to find the url of the next page. Then it loads the page and extracts the elements and appends them to the DOM. If you use the nextHandler, you will completely bypass this behavior. That means you will have to fetch data (in this case JSON) yourself and also insert new elements in the DOM.
Here is an example with comments to explain what it does.
First, let's assume our movie(1..n).json has the following format:
[
{
Title: 'item1',
Plot: 'description1'
}, {
Title: 'item2',
Plot: 'description2'
}
]
Now the implementation of the nextHandler:
import InfiniteAjaxScroll from "#webcreate/infinite-ajax-scroll";
function nextHandler(pageIndex) {
// we use the fetch api to load the next page. Any http client library will do, like axios.
return fetch("./static/movies"+pageIndex+".json")
// use the response as json
.then((response) => response.json())
// process the actual json data
.then((jsonData) => {
// create an array to store our html elements in memory
let elements = [];
// we loop over the items in our json and create an html element for each item
jsonData.forEach(function (item) {
const template = `<div class="item">
<h1>${item.Title}</h1>
<p>${item.Plot}</p>
</div>`;
const element = document.createElement("div");
element.innerHTML = template.trim();
elements.push(element.firstChild);
});
// now use IAS's append method to insert the elements
// it's import that we return the append result, as it's an promise
return this.append(elements);
})
// page 3 returns a 404, returning false here indicates there are no more pages to load
.catch(() => false);
}
window.ias = new InfiniteAjaxScroll(".container", {
item: ".item",
next: nextHandler,
pagination: false
});
I also prepared an interactive demo on Codesandbox:
https://codesandbox.io/s/serene-einstein-f73em

Ionic: reloading current child state from side menu causes loss of header

I'm creating a simple Ionic menu app and I would like to reload the current state from the side menu.Here's a Plunkr to illustrate the problem.
In the 'Search' state, the current time is displayed. The desired behavior is the following: when I click 'Search' from the side menu, the time is refreshed, i.e. the controller is reloaded. This should happen even when I'm already on the Search page. In reality, the controller is of course much more complex, but this example is enough to illustrate the problem.
I started from the ionic 'menu' starter template. To be able to reload the state, I changed two things:
disabled view caching in app.js config function: $ionicConfigProvider.views.maxCache(0);
In menu.html, I'm passing ui-sref-opts to explicitly reload the state:
ui-sref="app.search" ui-sref-opts="{reload: true}"
The result is that the time is indeed updated, however, the header of the view is gone.
Any suggestions as to what I'm doing wrong?
try something like this in the routing:
.state('tab.home', {
url: '/home',
cache: false, //<-- FORCE TO CLEAR CACHE
views: {
'tab-home': {
templateUrl: 'templates/home/home.html',
controller: 'HomeController'
}
}
})
You can broadcast an event to achieve that. But if you want to keep it like that, you could use a parameter and it would be all right because basically the one in control of the date var is your menu and not your state.
So you could do this:
app.js
.state('app.search', {
url: "/search",
params:{
date: new Date()
},
views: {
'menuContent' :{
templateUrl: "search.html",
controller: 'SearchCtrl'
}
}
})
Menu item
<ion-item nav-clear menu-close ui-sref="app.search({date: updateDate()})">
Search
</ion-item>
controllers.js (App controller or specific menu controller)
.controller('AppCtrl', function($scope) {
$scope.updateDate = updateDate;
function updateDate(){
return new Date();
}
})
controllers.js
.controller('SearchCtrl', function($scope, $stateParams) {
$scope.now = $stateParams.date;
})

collection-repeat with angular component, what is happening?

I'm trying to use collection-repeat to display an angular component for each object in an array. I pass each object as parameter to an angular component but when I try to access the object in my component's controller I get undefined.
<ion-content>
<ion-list>
<ion-item
collection-repeat="user in users"
item-width="100%"
item-height="90px">
{{user}} //renders my user data correctly instantly
<usser user="user"></user>
</ion-item>
</ion-list>
</ion-content>
My component
angular
.module('app')
.component('user', {
templateUrl: 'components/user.html',
scope: true,
bindings: {
user: '<'
},
controller: function() {
console.log(self.user) //prints undefined
}
})
I've tried wrapping the console.log in a $timeout without success
Printing self displays {user: undefined} in my chrome console, but if I expand the object I can see that user contains the correct data (only for the some of the items)
Accessing self.user doesn't work
EDIT: I can't really understand what's going on..
controller: function() {
console.log(self.user) //prints undefined
setTimeout(function() {
console.log(self.user) // prints my data
}, 2000)
}
What am I doing wrong?
Thanks in advance!
Lost 3 hours to figure out this
This is a known issue at the moment with collection repeat. A fix
would require a refactor of collection repeat, which would be too big
of a change the moment.
Always check the issues on Github, [V] lesson learned

Routing in AngularJS with variable in the beginning of the URL

I'm experiencing some behavior that I can't explain, but I'm sure somebody else can. I'm setting up my app module like so:
var appmodule = angular.module('appmodule', ['ui']).config(
['$routeProvider', function($routeProvider) {
$routeProvider.
when('/dashboard', {
templateUrl: 'partials/dashboard.html',
controller: dashCtr,
layer: 'main'
}).
when('/:campaignId/edit', {
templateUrl: 'partials/edit.html',
controller: campaignCtr,
layer: 'layer1'
}).
when('/:campaignId', {
templateUrl: 'partials/campaign_info.html',
controller: campaignCtr,
layer: 'layer1'
}).
when('/inbox', {
templateUrl: 'partials/inbox.html',
controller: inboxCtr,
layer: 'main'
}).
otherwise({redirectTo: '/dashboard'});
}]);
When I navigate to app.com/:campaignId or app.com/:campaignId/edit, all works as expected, but if I try app.com/inbox (or anything else), I get redirected back to /dashboard. I've read in a few other places a URL can't begin with a variable, but I'm new to this concept (usually just to design side of the front-end) and don't understand why.
Thanks.
Well it is simply because inbox route is below ''/:campaignId' route. Angular checks rules in the order they were defined, and ''/:campaignId' rule is matching every single url, so there is no chance to get to 'inbox' route.
Just define '/:campaign' routes after all other routes and everything should be working.