Store HTML page in memory with Angular2 - html

I would like to keep web page in memory so that when I click on back button (not the one on web browser) or on a routerlink, the HTML page instantly loads if I already visit it(because I have some data to load that I don't want to be reload).
I've seen a method with tabbed interface : https://www.w3.org/Style/Examples/007/target.en.html#tab1
but it is not adapted for my code architecture.
I'm using routerlinkin angular2 to navigate threw pages and a back button calling a function on click to go to the previous page.
I try to detail as far as I can so people can understand better my code architecture and the way routerlink method works.
Back button function (works independently from routerlink) :
goBack() {
window.history.back();
}
The router link method from page 1 to page 2:
page1.html :
<a[routerLink]="['PAGE2']"> go to page 2</a>
page1.ts component:
import { Router, ROUTER_DIRECTIVES } from '#angular/router-deprecated';
#Component({
selector: 'page1',
templateUrl: 'page1.html',
styleUrls: ['page1.css'],
directives: [ROUTER_DIRECTIVES]
})
main.ts :
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '#angular/router-deprecated';
#Component({
providers: [ROUTER_PROVIDERS]
})
#RouteConfig([
{ path: '/page2', name: 'PAGE2', component: Page2}]) //component representing class Page2 in page2.ts
Any idea to help me manage it is welcomed, thanks by advance !

Just cache the data in the service like explained in What is the correct way to share the result of an Angular 2 Http network call in RxJs 5?
There is currently no way to prevent the router from re-creating the component when you route away and back to the component.

Well, for those having the same problem, I think the best way to manage it is to map the data into a localStorage key like this :
localStorage.setItem('favoris',JSON.stringify(my_array)); //set my data array
array = JSON.parse(localStorage.getItem('key_name')); //get in array
And then ngOnInitin the class called by the router will call the initial function depending of localStorage key being true or not.

Related

Angular: Using (click) and routerLink on the same Button

I have a button that redirects to a new page and at the same time should save data to a Service. As I use it now it looks like this:
<button [disabled]="!isValid" (click)="saveToService()" routerLink="/link">Next</button>
Now I wonder if this is best practice. It feels like the html button is somewhat cluttered by so many seperate functionalities. The obvious alternative is to move the router navigation to a function that does both things, as in:
<button [disabled]="!isValid" (click)="saveAndNavigate()">Next</button>
and in ts:
private saveAndNavigate():void { this.service.setData(data); this.router.navigate(['/link]); }
Is there a 'right' way to do this? Are there some unwanted side effects from doing both actions in html?
Thanks
I would suggest you to do it in router promises. So you can:
this.router.navigate(['/link]).then(() => {
this.service.setData(data);
});
I would implement the OnDestroy function in your component, so you can store the data when the component terminates.
Something like this in HTML:
<button [disabled]="!isValid" routerLink="/link">Next</button>
And like this in your component:
import { Component } from '#angular/core';
#Component({...})
export class ThisComponent implements OnDestroy {
ngOnDestroy(){
saveToService()
}
}
If your navigation is performed regardless of the outcome of the service call, then Fatih's answer would work just fine.
On the other hand, and what I've normally seen, is that page navigation should only occur after (successful) completion of the request. If this is the case, I would remove the routerLink directive from your button and keep the (click) function. That function could look like this:
// if your service is making an Http request
public saveToService() {
this.service.saveStuff().pipe(
tap(() => this.router.navigate(['/somewhere']))
)
}
tap simply performs some action without affecting the data stream, so it's perfect for router navigation.

How to navigate in React-Native?

I am using ReactNavigation library in my react-native project and since 6 hours I am trying to navigate from one screen to others screen and have tried every possible way but I think I am not able to get the logic properly.
This is my project structure.
Here
The way I am doing it.
const AppStack = StackNavigator({ Main: Feeds });
const AuthStack = StackNavigator({ Launch: LaunchScreen, });
export default SwitchNavigator({
Auth: AuthStack,
App: AppStack
});
In my LaunchScreen.js
const SimpleTabs = TabNavigator(
{
Login: {
screen: Login,
path: ""
},
SignUp: {
screen: SignUp,
path: "doctor"
}
},
);
<SimpleTabs screenProps={{rootNavigation : this.props.navigation }}/>
But the problem is in my LaunchScreen Component there is a TabNavigator which contains my other two components Login.js and SignUp.js but the button in my Login.js doesn't navigate it to Feed.js.
When you click on the button this is performed.
signInAsync = async () => {
await AsyncStorage.setItem('userToken', 'abc');
this.props.navigation.navigate('Main');
console.log("AAAAAsSSS");
};
My LaunchScreen.js contains a TabNavigation which lets you slide between two components ie. Login.js and SignUp.js.
Now when you click on the Login button which is in Login.js component it will authenticate the user and will switch the entire LauchScreen.js component with the Feed.js component.
I am a noob to react-native.
You can use react-native-router-flux (npm install --save react-native-router-flux)
just make one Navigator.js file and define each page you wanted to navigate.
import React from 'react';
import { Router, Scene } from 'react-native-router-flux';
import LaunchScreen from '../components/LaunchScreen.js';
import Feed from '../components/Feed.js';
const Navigator = () => {
return (
<Router>
<Scene key="root">
<Scene key="lauchscreen" component={LaunchScreen} hideNavBar initial />
<Scene key="feedscreen" type="reset" hideNavBar component={Feed} />
</Scene>
</Router>
);
};
export default Navigator;
now in your App.js file add this:
import Navigator from './src/Navigator.js';
export default class App extends Component<Props> {
render() {
return (
<Navigator />
);
}
}
now in your login.js when you click on login button write this:
import { Actions } from 'react-native-router-flux';
onLoginClick() {
Actions.feedscreen();
}
Thats it.. happy coding.
If you want to navigate to Feeds.js then navigate as
this.props.navigation.navigate('App');
not as
this.props.navigation.navigate('Main');
because your
export default SwitchNavigator({
Auth: AuthStack,
App: AppStack // here is your stack of Main
});
refer example
I came across the same issue few months ago. Thank god you have spent just 6 hours, i almost spent around 4 days in finding a solution for it.
Coming to the issue, Please note that in react-navigation you can either navigate to siblings or children classes.
So here, You have a swtichNavigator which contain 2 stack navigators (say stack 1 and stack 2), stack1 has feeds and stack2 has a tab navigator with login and signup.
Now you want to navigate from login.js to feeds.js(say file name is feeds.js). As mentioned already you can not navigate back to parent or grandparent. Then how to solve this issue?
In react native you have the privilege to pass params (screenprops) from parent to children. Using this, you need to store this.props.navigation of launchScreen into a variable and pass it to tab/login (check the tree structure). Now in the login.js use this variable to navigate.
You are simply passing the navigating privilege from parent to children.
Editing here:
<InnerTab screenProps={{rootNavigation : this.props.navigation }} />
Here, InnerTab is the tab navigator.
export const InnerTab = TabNavigator({
login: {
screen: login,
},
},
signup: {
screen: signup,
},
},
},
in login class, use const { navigate } = this.props.screenProps.rootNavigation;
Now you can use variable navigate.
I know its little tricky to understand but i have tried and it works.
Write your Navigator.js file as below,
import React from 'react'
import { NavigationContainer, useNavigation } from '#react-navigation/native'
import { createStackNavigator } from '#react-navigation/stack'
const SwitchNavigatorStack = () => {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName='{nameofscreen}' screenOptions={screenOptions}>
<Stack.Screen name='{nameofscreen}' component={{nameofscreen}}/>
<Stack.Screen name='{nameofscreen}' component={{nameofscreen}}/>
<Stack.Screen name='{nameofscreen}' component={{nameofscreen}}/>
<Stack.Screen name='{nameofscreen}' component={{nameofscreen}}/>
</Stack.Navigator>
</NavigationContainer>
)
}
export default SwitchNavigatorStack
Once, you are done with that change your App.js file to,
import SignedInStack from './navigation'
import React from 'react'
export default function App() {
return <SwitchNavigatorStack/>
}
After this, you are done with setting your project for navigating. In all the components where you want to add navigation feature make sure you use the navigation.navigate() (or) navigation.push() method. Also make sure you hook navigation constant by import useNavigation library. For example,
const Login = () => {
const navigation = useNavigation()
< Button title = 'Login' onPress={() => navigation.navigate('{nameofscreen}')} />
}
with this code snippet you can implement navigation between screens using #react-navigation/native and #react-navigation/stack

ngFor not updating when #Input updated from google map marker event

I'm trying to build a simple app with angular2, I have the below component:
#Component({
selector: 'map-menu',
templateUrl: './angular-app/components/map-menu.html'
})
export class MapMenuComponent {
#Input() selectedMarkers: Array<google.maps.Marker>;
constructor() {
// setInterval(() => {
// console.log(this);
// }, 1000);
}
}
when my map-menu.html is:
<ul class="nav nav-sidebar">
<li *ngFor="#marker of selectedMarkers #i = index">{{marker.data.name}}</li>
</ul>
and in my app html I have:
<map-menu [selectedMarkers]="selectedMarkers"></map-menu>
and the list is not being updated, BUT when I'm adding the commented setInterval it is working fine. what am I missing there?
I've created a plunker with the solution
From the User Input dev guide:
Angular only updates the bindings (and therefore the screen) if we do something in response to asynchronous events such as keystrokes.
I'm guessing you don't have an asynchronous event when the markers array is updated, hence nothing is causing Angular to run its change detection algorithm.
Update: The issue was not that there wasn't an asynchronous event, the issue was that the asynchronous event (google.maps.Marker.addListener()) is not known by Angular, hence Zone does not monkey patch it, hence Angular doesn't run its change detection algorithm when the data changes. The solution is to emit the event inside Zone's run() function:
marker.addListener('click', () => {
this.zone.run(() => {
this.markerClickEvent.next({data:{name: 'another'}});
});
});
This will cause Angular to run its change detection algorithm, notice the change, and update the view.
The reason setInterval() works is because it is monkey patched by Zone.
See DiSol's plunker for more details.
If you want to learn more about Zone, watch Miško's video.
See also NgZone API doc.

ionic - $state.go with params issue1

I just create the ionic project and I'm trying to make the sign in and sign up page and I just implement the HTML and CSS. but the problem is I can't change position between controllers.
The URL of controller is changes but the page is not changed. I was trying to import the correct module but I can't find the method.
Make sure in app.js, your templateURL , url is properly defined.
Most of the time it might be your 'URL' problem.
If you navigated from 'main', your next url should be : /main/success or something like that.
.state('tab.main', {
cache: false, //if you want to disable cache in ionic
url: '/main',
views: {
'tab-cases': {
templateUrl: 'templates/tab-main.html',
controller: 'MainCtrl'
}
}
})

Filter table rows depending on value in searhbox

My application consists of the following:
One indexview which my navbar is placed in together with a searchbox, this has a navcontroller.
Two html pages which is placed in a ngview element with 2 different controllers.
customercontroller and contactscontroller. Theese 2 pages have tables which gets data from a service.
How do i pass the value from the navbarcontroller to the other 2 controllers to filter my table?
The Ng-View is not nested inside the navController scope.
This is my route.
app.config(function ($routeProvider) {
$routeProvider
.when("/Customers", {
templateUrl: "app/customers-list.html",
controller: "customerController"
})
.when("/Contacts", {
templateUrl: "app/contacts-list.html",
controller: "contactController"
})
.when("/Prospects", {
templateUrl: "app/saleprospect-list.html",
controller: "prospectController"
})
.when("/Prospects/add", {
templateUrl: "app/salesprospect-add.html",
controller: "addController"
})
.when("/Prospects/:index", {
templateUrl: "app/salesprospect-add.html",
controller: "editController"
})
.otherwise({
redirectTo: "/Customers"
})
});
You can use angularjs custom events to pass information from one place to another.
As shown in this link:
How do I create a custom event in an AngularJs service
Working with $scope.$emit and $scope.$on
You can pass data with these events which can be used by functions listening for them.
You will find many solutions on how to communicate between controllers. As #ArslanW has pointed out, you can use events. Some answers will ask you to use services.
I believe that, as a best practice, you need to store the search text in the URL as query parameters. This has the benefit that if the user wishes to refresh the screen, the search results will not be reset because you can read the search text from the URL's query parameters.
Check Github.com's issue tracker for example. There you can search for issues and even if you refresh, you search result or text is not lost.
To carry this out, you need two watchers.
One watcher in your NavController to watch the search text and to copy it to the URL.
So, if your search text has the following markup:
<input type="text" data-ng-model="searchText">
... you need to have the following watcher on the input model (in your NavController)
$scope.$watch('searchText', function () {
//This causes the URL to look like
//http://localhost/#!/path?searchText=my%20search%text
//where the search text is "my search text"
$location.search('searchText', $scope.searchText);
});
Now that the URL will get updated, you can use the URL itself to determine which search text to use.
Thus, in the controller for your two HTML views (CustomerController and ContactsController that will display tables, you can then watch the search text for changes:
$scope.$watch(function () {
return $location.search().searchText;
}, function (value) {
//The search text may be undefined - possible when the page
//loads and the user has not entered anything in the search box
if (value) {
//"value" is the search text entered by the user.
//You can then use this and proceed accordingly.
}
});
You have thus hit two bushes with one stone. Your search text is stored in the URL which causes the application to not lose the search text on refresh. You have also communicated with each controller about the search text entered.
Note that since you are adding query parameters to the search text, your page will refresh. To prevent this, add the reloadOnSearch property to your route and set it to false:
$routeProvider
.when("/Customers", {
templateUrl: "app/customers-list.html",
controller: "customerController",
reloadOnSearch: false
});