CanDeactivate guard has no access to functions of component it's guarding - angularjs

I am trying to use a CanActivate guard in angular2 to ask user to save changes.
I am using angular RC4
Boot.ts
bootstrap(App, [
...HTTP_PROVIDERS,
StorageService,
PortfolioNavigationGuard,
other...
Routes.ts
export const PortfolioRoutes: RouterConfig = [
{ path: 'portfolios', component: 'PortfolioListComponent' },
{ path: 'portfolios/:id', component: 'PortfolioModelComponent', canDeactivate: [PortfolioNavigationGuard] },
{ path: 'portfolios/:id/:action', component: 'PortfolioModelComponent', canDeactivate: [PortfolioNavigationGuard] }
Guard
#Injectable()
export class PortfolioNavigationGuard implements CanDeactivate<PortfolioDetailComponent> {
constructor(private router: Router) { }
canDeactivate(component: PortfolioDetailComponent) {
component.canDeactivate();
component.anyfunction() --- anyfunction is not a function
return true;
}
The error
EXCEPTION: Error: Uncaught (in promise): TypeError: component.canDeactivate is not a function
The component
export class PortfolioDetailComponent {
canDeactivate() {
console.log("WOW");
}
Why can't I call any functions on the component inside my guard?

I have made some changes:
Switched the guard implementation to a different component ( same one I defined in the route config) :
export class PortfolioNavigationGuard implements CanDeactivate<PortfolioDetailComponent> {
to
export class PortfolioNavigationGuard implements CanDeactivate<PortfolioModelComponent> {
There are two things to note. The first component is not loaded through router outlet, while the second is. I was thinking that components that belong to another(used as tag in template) still can access the route data (You can subscribe to the route id change let's say in components that were brought using a selector and not a router outlet), however it seems that for the guard, the component that you are putting the guard on must be brought with router outlet.

Related

TypeError: Cannot call a class as a function in stenciljs

I trying to use multiselect-react-dropdown package inside my stencil project, but while rendering I'm getting this error, I found many solution for react but nothing really worked, Can anyone help do same in stenciljs.
import { Component, h, State } from '#stencil/core';
import { Multiselect } from 'multiselect-react-dropdown';
#Component({
tag: 'ui-card',
styleUrl: 'style.scss',
shadow: true
})
export class UiCard {
#State() state: any;
constructor() {
this.state = {
options: [{ name: 'Srigar', id: 1 }, { name: 'Sam', id: 2 }]
};
}
onSelect(selectedList, selectedItem) {
console.log(selectedItem)
console.log(selectedList)
}
onRemove(selectedList, removedItem) {
console.log(selectedList)
console.log(removedItem)
}
render() {
return (<div>
<Multiselect
options={this.state.options}
selectedValues={this.state.selectedValue}
onSelect={this.onSelect}
onRemove={this.onRemove}
/>
</div>)
}
}
StencilJS is not related to React.
Indeed they both are using JSX/TSX as a markup language, but React uses it's own React implementation of it, while StencilJS uses h jsxFactory.
As you may know, JSX is transpired to the regular function calls like createElement which return a Virtual DOM object to render.
So, to render react Virtual DOM object to the page you need to call ReactDOM.render(), meanwhile Stencil compiles it to the web-components.
Despite a lot of common things like JSX, props/state and other - React and StencilJS are completely different tools.
You can use <slot> as a workaround, but I think there might be some performance issues.
Though I wouldn't recommend it, it's possible to use react components inside Stencil, by including the react runtime in your component (yeah sounds pretty ineffective).
See my answer here: https://stackoverflow.com/a/62046928/2897426. It's for using a react-bootstrap component which is fairly similar. Note that Stencil's JSX (or really TSX) is not compatible with React's JSX, thus you can't use JSX to pass the properties. It'll be something like
import { Component, h, State } from '#stencil/core';
import { Multiselect } from 'multiselect-react-dropdown';
import ReactDOM from 'react-dom';
import React from 'react';
#Component({
tag: 'ui-card',
styleUrl: 'style.scss',
shadow: true
})
export class UiCard {
#Element() host: HTMLUiCardElement;
#State() state: any;
#Watch('state')
onStateChange() {
const props = {
options: this.state.options,
// ...
};
ReactDOM.render(React.createElement(Multiselect, props), this.host);
}
componentWillLoad() {
this.onStateChange();
}
render() {
return <Host />;
}
}
Again, I don't recommend doing this, it'll completely bloat your component with the whole react runtime. Just saying it's possible.

React-Redux how to export component function to window object

I am building a React-redux library that displays a widget chat. The library should provide an interface allowing the client to configure and understand the state of the library and, render widgets into their document.
I use Webpack for the build process.
I use Twilio-Chat library for the chat features.
My index file looks like this:
Index.js
import MyChatWidget from 'components/MyChatWidget';
export default {
widgets: {
MyChatWidget: {
render: (args) => {
ReactDOM.render(
<MyChatWidget />
);
},
logout: () => {
// this function should call a the logout in the MyChatWidget
// React Component
}
}
}
MyChatWidget
import Chat from 'twilio-chat';
class MyChatWidget extends Component {
logout() {
Chat.shutdown()
}
}
export default connect()(MyChatWidget);
The logout is exposed to the client and it should call a function inside the MyChatWidget component.
How can I achieve this behaviour?
Am I including the Twilio Chat in the wrong place(MyChatWidget Component)?
I read this article for the building with webpack (https://codeburst.io/building-react-widget-libraries-using-webpack-e0a140c16ce4)
Disclaimer: I'm not a huge fan of putting anything on the window object, especially because if you use server rendering the window isn't available. But passing an instance of your class to the window object would make it accessible anywhere JS has access to the window object.
constructor() { super(); if ( window ) { window.mychatwidget = this; } }
You can declare the logout method static and access that method as MyChatWidget.logout() in your index.js

Can't resolve parameters for a component from inside an injectable

I'm trying to import a component into my injectable service, but it seems that I'm doing something wrong. This happens for other pages in similar situations as well. The current situation seems to happen because the LoginPage that I'm trying to import into SessionService requires SessionService, which smells of an infinite loop somewhere. Any ideas on how I should resolve it other than not including the page component in the injectable SessionService?
My current architecture is built so that I need to redirect to login page if session does not exist. Ionic NavController requires a page component for pushing onto navigation stack. Alternate ways of solving the redirection problem are welcome, but I would also like to understand why this is happening.
Uncaught Error: Can't resolve all parameters for LoginPage: (?, ContentService).
session.service.ts
#Injectable()
export class SessionService {
constructor(private content: ContentService) {}
}
login.page.ts
#Component({
templateUrl: './login.page.html'
})
export class LoginPage {
constructor(public session: SessionService,
public content: ContentService) {
}
and for those who requested it, content.service.ts
#Injectable()
export class ContentService {
constructor(private authHttp: AuthHttp) {
}
}
app.module.ts
#NgModule({
declarations: [
LoginPage
],
entryComponents: [
LoginPage
],
providers: [
SessionService,
ContentService
]
})
export class AppModule {
}
I'm using Angular 2.2.1 with Ionic 2.0.0-rc.4.
content.service.ts
#Injectable()
export class ContentService {...}
Add this also like SessionService

Angular2 routing v3.0.0 with deeply nested routes

right now migrating from router v2 to v3 (feels like a deja vu). The routing configuration is now decoupled from the components again. It overthrows the logic I considered quite sensible. They have introduced a children property in the RouterConfig which gives me headache. Assume an application with many routes similar to this
/club/123/member/98/tasklist/921/edit
The route was spread over the following components with the following #Routes decorators
#Routes([{path: '/club/:id', component: DelegateClubComponent}])
export class MainApp {...}
#Routes([{path: 'user/:id', component: DelegateUserComponent}])
export class DelegateClubComponent {...}
#Routes([{path: 'tasklist/:id', component: DelegateTaskListComponent}])
export class DelegateUserComponent {...}
#Routes([{path: 'edit', component: EditTaskListComponent}])
export class DelegateTaskListComponent {...}
Each of the DelegateXComponents were responsible for resolving the respective document and making it available in a Service the other components get injected. Moreoever, all of the DelegateXComponents rendered a little template with some data of the documents they were in charge of.
How is this done with router v3 ? It makes no sense to map the entire tree in a nested RouterConfig with 5 levels of children. On the other hand, do separate RouterConfigs work at all?
export const clubRoute: RouterConfig = [
{ path: 'club/:id', component: DelegateClubComponent }];
export const userRoute: RouterConfig = [
{ path: 'user/:id', component: DelegateUserComponent }];
As long as there is no magic happening behind the scenes, how would the router know that the userRoute should be considered as a child route for clubRoute.
Confused greetings
You can define configs in the same files as the components and then just combine them to a complete tree before passing it to the router.
import {childRoutes} from './child.component'
export const clubRoute: RouterConfig = [
{ path: 'club/:id', component: DelegateClubComponent, children: childRoutes }];

How to render a reactjs component stored in a redux reducer?

I have a redux reducer loaded with several reactjs components.
I want to load these inside other components through this.props
Like: this.props.components.MyReactComponent
class OtherComponent extends Component {
render() {
const Component = this.props.components.MyReactComponent
return (
<div>
<Component />
</div>
)
}
}
Is this possible? If so, how?
EDIT The component is a connected component. I am able to load it but it is broken. In this case, it is a counter, when you click to increment or decrement nothing happens. In the console, there is this error:
Uncaught ReferenceError: _classCallCheck is not defined
if I convert the component into a dumb component (without connecting it), the error is this:
Uncaught ReferenceError: _classCallCheck3 is not defined
EDIT 2
I found out why those errors show up. It is because the react component gets stripped out when stored in the reducer:
A react component would look something like this:
{ function:
{ [Function: Connect]
displayName: 'Connect(Counter)',
WrappedComponent: { [Function: Counter] propTypes: [Object] },
contextTypes: { store: [Object] },
propTypes: { store: [Object] } } }
However, after I store it inside a reducer, it loses its properties and ends up looking something like this:
{ function:
{ [Function: Connect] } }
After reading the comments below, I thought of an alternative. I can store in a reducer the path to each component, then make a new wrapper component that could render those other components from those paths.
I tried it but encoutered a different problem with the funcion require from nodejs that for some weird reason is not letting me user a variable as an argument. For example:
This works:
var SomeContent = require('../extensions/myContent/containers')
This does not:
var testpath = '../extensions/myContent/containers'
var SomeContent = require(testpath)
Giving me the following error:
Uncaught Error: Cannot find module '../extensions/myContent/containers'.
It is adding a period at the end of the path. How can I prevent require to add that period?
If you can think of any other alternative I can implement for what I am trying to do, I would greatly appreciate it.
EDIT 3 Following Thomas advice...
What I am trying to accomplish is this:
I want to be able to render react components inside other react components, I know how to do it the same way most us know how to; however, I want to be able to do it by importing a file that would contain all the components without actually having to import and export each one of them:
OtherComponent.js
import React, { Component } from 'react'
import { SomeComponent } from '../allComponentes/index.js'
export default class OtherComponent extends Component {
render() {
return (
<SomeComponent />
)
}
}
SomeComponent.js
import React, { Component } from 'react'
export default class SomeComponent extends Component {
render() {
return (
<div>
Hello
</div>
)
}
}
allComponents/index.js
import SomeComponent from '../allComponents/SomeComponent/index.js'
export { SomeComponent }
What I am trying to do in allComponents/index.js is to avoid having import/export statements for each component by reading (with fs module) all the components inside the allComponents folder and export them.
allComponents/index.js (pseudocode)
get all folders inside allComponents folder
loop through each folder and require the components
store each component inside an object
export object
When I tried that, I encountered multiple issues, for one, export statements have to be in the top-level, and second, fs would work only on the server side.
So, that is why I thought of loading all the components in a reducer and then pass them as props. But as I found out, they got stripped out when stored them in a reducer.
Then, I thought of only storing the path to those components inside a reducer and have a wrapper component that would use that path to require the needed component. This method almost worked out but the nodejs function require wont allow me to pass a variable as an argument (as shown in EDIT 2)
I think your question is not really to do with redux but rather is (as you say):
What I am trying to do in allComponents/index.js is to avoid having import/export statements for each component by reading (with fs module) all the components inside the allComponents folder and export them.
By way of example, I have all of my (dumb) form components in a folder path components/form-components and the index.js looks something like:
export FieldSet from './FieldSet'
export Input from './Input'
export Label from './Label'
export Submit from './Submit'
export Select from './Select'
export Textarea from './Textarea'
Then when I want to import a component elsewhere, it is import { FieldSet, Label, Input, Submit } from '../../components/form-components/';

Resources