So here I'm using checkbox to hide and show templates. I just want code that will make checkbox remains checked after page refresh in angular 9 .Please help me.
You can do 2 things:
Use RxJS and as soon as the checkbox is checked, you can use Behavior Subject to store the data and on reload or refresh event, read the behavior subject and patch the value
Use sessionStorage, and do the same. As soon as its checked, store it in sessionStorage, on reload, try to read from sessionStorage and patch the value again.
Implementation:
// Using session storage
someForm.controls.checkboxFormControl.valueChanges.subscribe(
data => {
if (data) {
sessionStorage.setItem('checkboxChecked', true)
}
})
onRefreshEvent() {
let sessionStorageValue = sessionStorage.setItem('checkboxChecked');
if (sessionStorageValue && (sessionStorageValue === true)) {
this.someForm.contols.checkBoxControl.patchValue(true)
}
}
//// Same with Behavior Subject as well
// Create a Behavior Subject in your Service file
checkboxCheckedSource: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
checkBoxObservable: Observable<boolean> = this.checkboxCheckedSource.asObservable();
setCheckboxValue(checked: boolean) {
this.checkboxCheckedSource.next(checked);
}
getCheckboxValue(): Observable<boolean> {
return checkBoxObservable;
}
// In your component.ts file
someForm.controls.checkboxFormControl.valueChanges.subscribe(
data => {
if (data) {
this.yourService.setCheckboxValue(true)
}
})
onRefreshEvent() {
this.yourService.getCheckboxValue().subscribe(checked => {
if (checked) {
this.someForm.contols.checkBoxControl.patchValue(true);
}
})
}
If its only one checkbox, i would prefer the sessionStorage way of doing it, else if you are storing a lot of checkboxes use BehaviorSubject!!
Related
I have a component that loads some HTML (converted from MD via marked library) and diplays it to the page and if a row is clicked, the document will scroll to the appropriate HTML element with the matching headerID on the page.
import { marked } from 'marked';
const [content, setContent] = React.useState('');
React.useEffect(() => {
if (!source) {
return;
}
getRequest(source)
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
return response.text().then(faq => {
// sets page to html
setContent(marked(faq));
const nearestHeader = document.getElementById(headerID);
if (nearestHeader) {
// if search result is clicked, jump to result's nearest header
nearestHeader.scrollIntoView(true);
setRowClicked(false);
}
});
})
.catch(e => Dialog.error('Failed to load page.', e));
}, [source, rowClicked]);
However, when I go to test this code the 'nearestHeader' object is always null even after I verified that the headerID matches up with the existing HTML element's ID I want to navigate to. How can I make sure document is ready/loaded before attemping the getElementById call without using extra libraries?
Solved by adding another useEffect call which waits on content of page to be set first. Removed nearestHeader code from the initial useEffect() call that sets content
React.useEffect(() => {
const nearestHeader = document.getElementById(headerID);
if (nearestHeader) {
nearestHeader.scrollIntoView(true);
setRowClicked(false);
}
}, [content]);
I am trying to display a routerlink name based on a condition. I want to display the div section routerLink name if condition is true.If i check {{isNameAvailable}}, first it displays false and after this.names got the values it shows true.Since in the component getDetails() method is asynchronous this.names getting the values after html template render.Therefore this routerLink does n't display.Therefore I want to display div section after some time. (That 's the solution i have) Don't know whether is there any other solution.
This is my html file code.
<main class="l-page-layout ps-l-page-layput custom-scroll bg-white">
{{isNameAvailable}}
<div class="ps-page-title-head" >
<a *ngIf ="isNameAvailable === true" [routerLink]="['/overview']">{{Name}}
</a>
{{Name}}
</div>
</main>
This is my component.ts file
names= [];
isNameAvailable = false;
ngOnInit() {
this.getDetails()
}
getDetails() {
this.route.params.subscribe(params => {
this.names.push(params.Names);
console.log(this.names);
this.getValues().then(() => {
this.isNameAvailable = this.checkNamesAvailability(this.names);
console.log(this.isNameAvailable);
});
});
}
resolveAfterSeconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 900);
});
}
checkNamesAvailability(names) {
console.log(names);
return names.includes('Sandy');
}
async getValues() {
await this.resolveAfterSeconds(900);
}
And console.log(this.isLevelAvailable); also true. What I can do for this?
1.You do not have anything to show in the HTML only the isNameAvailable, because you do not have any assignment in the Name variable.
2.It is better to use the angular build-in async pipe,
when you want to show the returned value from observables.
3.When you are using the *ngIf directive you can skip *ngIf ="isNameAvailable === true" check because the variable is boolean type, you gust write *ngIf ="isNameAvailable", it will check also for null but NOT for undefined
It is working because the *ngIf directive is responsible for checking and rendering the UI, you can see how many times the directive is checking by calling an function and print and answer in the console.
By any chance do you have changeDetection: ChangeDetectionStrategy.OnPush docs set in component annotation? That might explain this behaviour. With it Angular run change detection only on component #Input()'s changes and since in your case there were non it did not run change detection which is why template was not updated. You could comment that line to check if that was cause of the issue. You are always able to run change detection manually via ChangeDetectorRef.detectChange() docs which should solve you problem
constructor(private cd: ChangeDetectorRef) {}
...
getDetails() {
this.route.params.subscribe(params => {
...
this.getValues().then(() => {
this.isNameAvailable = this.checkNamesAvailability(this.names);
this.cd.detectChanges(); // solution
console.log(this.isNameAvailable);
});
});
}
This stackblitz show this bug and solution. You can read more about change detection here
You could use RxJS timer function with switchMap operator instead of a Promise to trigger something after a specific time.
Try the following
import { Subject, timer } from 'rxjs';
import { takeUntil, switchMap } from 'rxjs/operators';
names= [];
isNameAvailable = false;
closed$ = new Subject();
ngOnInit() {
this.getDetails()
}
getDetails() {
this.route.params.pipe(
switchMap((params: any) => {
this.names.push(params.Names);
return timer(900); // <-- emit once after 900ms and complete
}),
takeUntil(this.closed$) // <-- close subscription when `closed$` emits
).subscribe({
next: _ => {
this.isNameAvailable = this.checkNamesAvailability(this.names);
console.log(this.isNameAvailable);
}
});
}
checkNamesAvailability(names) {
console.log(names);
return names.includes('Sandy');
}
ngOnDestroy() {
this.closed$.next(); // <-- close open subscriptions when component is closed
}
So, similar to the code in router.js, in the boilerplate at https://github.com/erikras/react-redux-universal-hot-example/ master branch
const requireLogin = (nextState, replace, cb) => {
if(!isLoggedIn) {
store.dispatch(getUserInfo(somePayLoad)).then(checkLoginStatus);
}
function checkLoginStatus() {
const {authorization: { user }} = store.getState();
if(!user.typeA) {
replace('/home');
}
cb();
}
}
So, when I replace(), and if the if condition !user.typeA fails, then it goes to the usual called route, access the user details that were fetched as in above code and also, I can easily call logout dispatcher I wrote whenever user wants to exit. But when if condition is passed, it replaces to /home page and opens(redirects) to home page successfully. But I can no longer have user details in authorization store. Neither I can call logout dispatcher.
Note:
if(!user.typeA) {
const path = '/home';
const query = {error:'anything'};
const state = store.getState();
replace({ path, query, state });
}
instead of
if(!user.typeA) {
replace('/home')
}
also doesn't work.
How to achieve this?
When using a generic modal or toast with a confirm button, it becomes useful to be able to pass an action into this component so it can be dispatched when you click confirm.
The action may look something like this:
export function showConfirm({modalConfirm}) {
return {
type: 'MODALS/SHOW_MODAL',
payload: {
modalId: getUuid(),
modalType: 'CONFIRM',
modalConfirm : modalConfirm,
},
};
}
Where modalConfirm is another action object such as:
const modalConfirm = {
type: 'MAKE_SOME_CHANGES_AFTER_CONFIRM',
payload: {}
}
The modalConfirm action is dispatched inside the modal component using dispatch(modalConfirm) or even dispatch(Object.assign({}, modalConfirm, someResultFromTheModal)
Unfortunatley this solution only works if modalConfirm is a simple redux action object. This system is clearly very limited. Is there anyway you can pass a function (such as a thunk) in instead of a simple object?
Ideally, something full featured likes this:
const modalConfirm = (someResultFromTheModal) => {
return (dispatch, getState){
dispatch({
type: 'MAKE_SOME_UPDATES',
payload: someResultFromTheModal
})
dispatch({
type: 'SAVE_SOME_STUFF',
payload: http({
method: 'POST',
url: 'api/v1/save',
data: getState().stuffToSave
})
})
}
}
Funny, putting an action object in the store and passing it as a prop to a generic dialog is exactly the approach I came up with myself. I've actually got a blog post waiting to be published describing that idea.
The answer to your question is "Yes, but....". Per the Redux FAQ at http://redux.js.org/docs/FAQ.html#organizing-state-non-serializable , it's entirely possible to put non-serializable values such as functions into your actions and the store. However, that generally causes time-travel debugging to not work as expected. If that's not a concern for you, then go right ahead.
Another option would be to break your modal confirmation into two parts. Have the initial modal confirmation still be a plain action object, but use a middleware to watch for that being dispatched, and do the additional work from there. This is a good use case for Redux-Saga.
I ended up using string aliases to an actions library that centrally registers the actions.
Modal emmiter action contains an object with functionAlias and functionInputs
export function confirmDeleteProject({projectId}) {
return ModalActions.showConfirm({
message: 'Deleting a project it permanent. You will not be able to undo this.',
modalConfirm: {
functionAlias: 'ProjectActions.deleteProject',
functionInputs: { projectId }
}
})
}
Where 'ProjectActions.deleteProject' is the alias for any type of complicated action such as:
export function deleteProject({projectId}) {
return (dispatch)=>{
dispatch({
type: 'PROJECTS/DELETE_PROJECT',
payload: http({
method: 'DELETE',
url: `http://localhost:3000/api/v1/projects/${projectId}`,
}).then((response)=>{
dispatch(push(`/`))
}),
meta: {
projectId
}
});
}
}
The functions are registered in a library module as follows:
import * as ProjectActions from '../../actions/projects.js';
const library = {
ProjectActions: ProjectActions,
}
export const addModule = (moduleName, functions) => {
library[moduleName] = functions
}
export const getFunction = (path) => {
const [moduleName, functionName] = path.split('.');
// We are getting the module only
if(!functionName){
if(library[moduleName]){
return library[moduleName]
}
else{
console.error(`Module: ${moduleName} could not be found.`);
}
}
// We are getting a function
else{
if(library[moduleName] && library[moduleName][functionName]){
return library[moduleName][functionName]
}
else{
console.error(`Function: ${moduleName}.${functionName} could not be found.`);
}
}
}
The modalConfirm object is passed in to the modal by props. The modal component requires the getFunction function in the module above. The modalConfirm object is transformed into a function as follows:
const modalConfirmFunction = (extendObject, modalConfirm) => {
const functionFromAlias = getFunction(modalConfirm.functionAlias);
if(functionFromAlias){
dispatch(functionFromAlias(Object.assign({}, modalConfirm.functionInputs, extendObject)));
}
}
As you can see, this function can take in inputs from the modal. It can execute any type of complicated action or thunk. This system does not break time-travel but the centralized library is a bit of a drawback.
How can I prevent a form button being clicked lots of times very fast?
I'm using html5 and angular2.
Currently they can just click it loads.
I need to only allow once if possible or atleast they have to wait 2 seconds before clicking it again.
You could try something like that:
#Component({
(...)
template: `
<div (click)="handleClick()">Click</div>
`
})
export class MyComponent {
constructor() {
this.clicked = false;
}
handleClick() {
if (!this.clicked) {
this.clicked = true;
setTimeout(() => {
this.clicked = false;
}, 2000);
}
}
}
I think that you could also leverage Rx (it depends on the use case) and the debounceTime operator:
Emits an item from the source Observable after a particular timespan has passed without the Observable omitting any other items.
This allows to trigger only the last event after an amount of time (here 500 milliseconds) when clicked a lot of time very fast:
this.ctrl.valueChanges.debounceTime(500).mergeMap(
val => {
return this.http.get('https://api.github.com/users?d='+val);
}).subscribe(
data => {
console.log(data);
}
);
This sample shows such behavior when a user fills an input.
You can mix this with a setTimeout from enabling again the processing again an amount of time.
Rob Wormald made a proposal regarding such issues. See this link for more details:
https://github.com/angular/angular/issues/4062
What type of job the button does? is it a form submission or a refresh page button! The best solution would be to disable the button immediately after user clicks it, in case you are not navigation to a different page.
Refer this
https://stackoverflow.com/a/18130876/5888071
Hope this helps.
It's not specific to angular2, but I use this to debounce function calls:
function debounce(delay, fn, fnArgsArray){
if(typeof fn.debouncing === 'undefined' || fn.debouncing == false){
fn.debouncing = true;
setTimeout(() => fn.debouncing = false, delay);
fn.apply(this, fnArgsArray);
}
}
example:
<button onclick="debounce(2000, add, [2, 4]);">testen</button>
function add(a, b){
var sum = a + b;
console.log(a + " + "+ b + " = " + sum);
}