The goal: trigger a component residing in a module, so my subscription in the ctor of the component is activated.
I'm using PreloadAllModules as a preloadingStrategy. But it's not happening.
I need to subscribe to some events in de constructor of my FriendsComponent.
the setup is like this:
FriendsComponent is shown in the template of the SocialComponent, which is part of the SocialModule.
social.component.html
<div>
<friends-component></friends-component>
</div>
the SharedModule declares the FriendsComponent.
AppModule imports SharedModule,
RouterModule for AppModule is like this:
{
path: 'social',
component: SocialModule,
children: [
{
path: 'friends',
component: FriendsComponent
}
]
},
I think the problem is because the FriendsComponent is not part of a router-outlet?
Can it be done without a router-outlet?
If a module would be pre- or eager loaded, would it automatically trigger the constructors (and the subscription)?
Is the issue with my preloading strategy?
I have tried adding: data:{preload:true} to the paths declared in routermodule.
Everything works fine, when the user activates the SocialModule (for instance by clicking on a button with a routerLink to social/friends), but I want it activated on startup (just not shown on any html)
I'm working with Angular Ivy, but think I'm still missing the points. Any help is appreciated
You need to handle your initial subscriptions in a service and have the component subscribe to that service. You won't need to touch the routes. It what services are for.
You subscribe to the value you need in your FriendService and have FriendComponent subscribe to your FriendService.
Related
What is the way to go to load GoogleMaps via importmaps in Rails7?
I've seen folks import it via the script-tag like this:
<script src="https://maps.googleapis.com/maps/api/js?key=<%= Rails.application.credentials[:p10_google_maps_js_api_key] %>&callback=initMap" async defer data-turbolinks-eval="false"></script>
but I would like to take advantage of the new importmap feature of Rails 7. Unfortunately I don't understand how i could trigger the initMap callback without using it as a script.
So there is a Stimulus component that you could use stimulus-places-autocomplete. However, you could easily implement this yourself and save you the trouble of pulling in a dependency.
At the end of the day, the google callback needs to be fired off. The way most have gotten around this is by creating an event and attaching that to the window. You then add a data-action to your views controller div that will look for this even and fire a callback of its own. That callback being an initializer within your Stimulus controller itself.
##########places_controller.rb##########
import { Controller } from "#hotwired/stimulus";
export default class extends Controller {
static targets = ["street", "city", "state", "zip"];
connect() {
// Can also initialize this with a CustomEvent
const event = new Event('google-maps-callback', {
bubbles: true,
cancelable: true,
})
window.dispatchEvent(event);
}
// additional actions
}
##########index.html.erb##########
<div
data-controller="places"
data-action="google-maps-callback#window->places#initMap"
>
<%# view code here %>
</div>
This still is not a solution that I love. However, its the workaround that we have for now.
I'm looking for a little guidance and suggestions here. My attempts and theories will be at the bottom.
I have a NextJS project from which I want to export the top level component (essentially the entry file) so that I can use it as a preview in my dashboard.
The nextjs project is very simple. For the sake of simplicity, let's imagine that all it renders is a colored <h1>Hello world</h1>. Then in my dashboard, I want to render a cellphone with my NextJS component embedded and then from the dashboard change the color of the text, as a way to preview how it would look like. I hope this makes sense.
I'm lost at how I could export this component from NextJS and import it into my dashboard. The dashboard is rendered in Ruby on Rails. It would be simple enough to just import the repo from git and access the file directly form node_modules, but I'm looking for a solution that doesn't require installing npm on our Rails project.
Paths I have thought about:
1 - Install npm on Rails and just import the source code from NextJS repo and access the file and render with react (Simple, but we're looking for a non-npm solution)
2 - Bundle the component with webpack and load it directly into rails (does this even work?) - I exported the js and all it did was freeze everything :P Still trying this path for now
3 - Using an iframe and just accessing the page (then I can't pass any callbacks into the iframe to change the color directly from the dashboard)
4 - I cannot separate this component from NextJS to use as a library in both repos. The component we are exporting is the "ENTIRE" NextJS app jsx and it wouldn't make sense to separate in a different repo
Does anyone have a suggestion on how I could achieve this?
I think you could use an iframe with the nextjs app url. Then if you want to change the color, simply add the color in query parameter of the iframe and handle it on nextjs app.
Simple example
Rails view (erb)
<iframe src="#{#nextjs_url}?color=#{#color}" />
NextJS
# do something to get the query param of the page and and set to prop of the component
const YourComponent = ({color}) => {
return <h1 style={{color}}>Lorem</h1>;
}
While trying Hoang's solution, I decided to dive deeper into how to communicate with an iframe and the solution actually feels quite good.
You can set up listeners on either side and post messages in between the projects.
So in my dashboard:
function handleEvent(e) {
const data = JSON.parse(e.data)
if (data.type === "card_click") {
//if type is what we want from this event, handle it
}
}
// Setup a listener with a handler
// This will run every time a message is posted from my app
window.addEventListener("message", handleEvent, false)
const postMessage = (color) => {
const event = JSON.stringify({
type: "color_update",
color,
})
// Find the iframe and post a message to it
// This will be picked up by the listener on the other side
document.getElementById("my-iframe-id").contentWindow.postMessage(event, "*")
}
And on my app:
function handleEvent(e) {
const data = JSON.parse(e.data)
if (data.type === "color_update") {
// Do whatever is necessary with the data
}
}
// Setup listener
// This will fire with every message posted from my dashboard
window.addEventListener("message", handleEvent, false)
const handleCardClick = (cardIndex) => {
const event = JSON.stringify({
type: "card_click",
cardIndex,
})
// post message to parent, that will be picked up by listener
// on the other side
window.parent.postMessage(event, "*")
}
It feels pretty straight forward to communicate with an iframe with this solution.
I am trying create a storybook for my react-realy app, but i don't know how to set mockup data for that component. For simple a component it is ok, because i can use dummy UI component vs Container approach, but i can't use this for nested relay components, for example there is a UserList component, which i want add to storybook, i can split relay fragment part to container and UI part to the component, but what if UserList children are too relay component? I can't split their when they are a part of the composition of UserList?
Is there some solution for add relay components to the storybook?
I created a NPM package called use-relay-mock-environment, which is based on relay-test-utils which allows you to make Storybook stories out of your Relay components.
It allows nesting of Relay components, so you can actually make stories for full pages made out of Relay components. Here's an example:
// MyComponent.stories.(js | jsx | ts | tsx)
import React from 'react';
import { RelayEnvironmentProvider } from 'react-relay';
import createRelayMockEnvironmentHook from 'use-relay-mock-environment';
import MyComponent from './MyComponentQuery';
const useRelayMockEnvironment = createRelayMockEnvironmentHook({
// ...Add global options here (optional)
});
export default {
title: 'MyComponent',
component: MyComponent,
};
export const Default = () => {
const environment = useRelayMockEnvironment({
// ...Add story specific options here (optional)
});
return (
<RelayEnvironmentProvider environment={environment}>
<MyComponent />
</RelayEnvironmentProvider>
);
};
export const Loading = () => {
const environment = useRelayMockEnvironment({
forceLoading: true
});
return (
<RelayEnvironmentProvider environment={environment}>
<MyComponent />
</RelayEnvironmentProvider>
);
};
You can also add <RelayEnvironmentProvider /> as a decorator, but I recommend not doing that if you want to create multiple stories for different states/mock data. In the above example I show 2 stories, the Default one, and a Loading one.
Not only that, it requires minimal coding, where you don't need to add the #relay-test-operation directive to your query, and the mocked data is automatically generated for you using faker.js, allowing you to focus on what matters, which is building great UI.
Feel free to review the source code here if you want to implement something similar: https://github.com/richardguerre/use-relay-mock-environment.
Note: it's still in its early days, so some things might change, but would love some feedback!
I also created relay-butler, which is a CLI that takes in GraphQL fragments and outputs Relay components, including a auto-generated query component that wraps the fragment component, and Storybook stories (the Default and Loading ones by default) that wrap that query component. And literally within minutes, I can create beautiful Relay components that are "documented" within Storybook.
Would also love some feedback for it!
I have a React Native app using the Redux framework and I'm using the Navigator component to handle navigation. I've had a little bit of trouble getting the navigation working and I'm not able to find any good examples of how to do it correctly so I'm looking for some help and clarification.
Here's the gist of what I currently have, which is working but I don't know if I'm doing it right:
Root Component
...
renderScene(route, navigator) {
console.log('RENDER SCENE', route);
const Component = route.component;
return (
<Component navigator={navigator} route={route} {...this.props} />
)
}
render() {
return (
<Navigator
renderScene={(route, nav) => this.renderScene(route, nav)}
initialRoute={{ name: 'Signin', component: Signin }} />
)
}
Signin Component
...
componentWillReceiveProps(nextProps) {
if (!this.props.signedIn && nextProps.signedIn) {
console.log('PUSHING TO MAIN');
nextProps.navigator.push({ name: 'Main', component: Main });
}
}
Questions:
1: My first thought here is that I should probably move the navigator.push code into an action. However is componentWillReceiveProps the right place to call the action? When the Signin component is loaded it fires an action to sign in the user if they already have an active session. By default they are not signed in so when the next props come through I check if it changed and then push to Main.
2: In my console log immediately after 'PUSH TO MAIN' is logged I see two 'RENDER SCENE' logs:
[info] 'RENDER SCENE', { name: 'Signin', component: [Function: Signin] } (EXPECTED)
[info] 'PUSHING TO MAIN'
[info] 'RENDER SCENE', { name: 'Signin', component: [Function: Signin] } (WHY?)
[info] 'RENDER SCENE', { name: 'Main', component: [Function: Main] }
I'm curious as to why RENDER SCENE is called twice (the first one being the Signin component) if I'm only pushing the Main component.
Also originally in the componentWillReceiveProps method I was only checking:
if (nextProps.signedIn) {
nextProps.navigator.push({ name: 'Main', component: Main });
}
but this caused the Main component to be pushed twice.
NOTE: The GitHub link below is now deprecated, in the comments the
author suggested react-native-router-flux which is by the same
author.
I've just added support for Redux, with my new component https://github.com/aksonov/react-native-redux-router with makes navigation pretty easy, like calling Actions.login
This is not a redux implementation but for routing I found react-native-router-flux to be really useful.
You can do things like call
Actions.login
to navigate to the login view. The routes are defined in your index file and have optional schemas to define navbar elements.
https://github.com/aksonov/react-native-router-flux
1) Yes, move it to method, componentWillReceiveProps is probably not correct. It’s difficult to refactor that part for you as I would not have that logic from that method on that component, i.e. the signinComp should receive state of whether it has an auth session or not (and not figure it out for itself). It leads to it getting processed for no reason; since if he is logged in you redirect. I would personally do that check in renderScene, since the nav gets passed down you can just make a push/pop on child components and handle all your logic in one renderScene.
2) See the navigation stack like a deck of cards. When you set the scene it is one card, but when you push it adds another card to the pile. So when you push and have two cards, it checks to make sure that all the cards are face up and rendered so that when you press back or pop it moves back to a completed card or scene. Imagine pushing 5 scenes immediately onto the stack. So when you change the stack it checks to ensure everthing is rendered and ready for that back button. Personally I don’t find that optimal (but it has to since you can pass different properties with each scene that you push).
You can probably change this line to:
renderScene={this.renderScene}
Question1:
I recommend you process navigator logic in shouldComponentUpdate method, like below,
shouldComponentUpdate(nextProps, nextState){
if(nextProps.isLoggedIn != this.props.isLoggedIn && nextProps.isLoggedIn === true){
//will redirect
// do redirect option
return false;
}
return true;
}
The Question 2:
I think this is the bug of react-native.
I've got an AngularDart application that is working, but I feel like I'm doing it wrong...
The application shows a list of events as they are received. The events get to the app via SignalR, but I don't think that's really relevant - the issue is the way I'm having to update the component's state in order to see the changes in the state get displayed on the page.
Here is a cut down version of my component:
#Component( selector: 'liveEvents', templateUrl: 'live_events.html', useShadowDom: false )
class LiveEvents implements ScopeAware {
VmTurnZone _vmTurnZone;
EventReceiver _eventReceiver;
LiveEvents( this._vmTurnZone, this._eventReceiver );
List<Event> events = new List<Event>();
void _onEventReceived(Event event) {
//TODO: This just does not seem right...
_vmTurnZone.run(() => events.add(event));
}
void set scope(Scope scope) {
var _events = _eventReceiver.subscribeToAllEvents( "localhost", _onEventReceived );
}
}
The EventReceiver class is responsible for connecting to a SignalR server, receiving messages from that server and converting them into Event objects, and then calling whatever function was specified in the subscribeToAllEvents (in this case, _onEventReceived)
The angular template html is very simple:
<ul>
<li ng-repeat="event in events">Event: {{event.Id}}</li>
</ul>
This all works fine, but the bit I don't like is having to inject a VmTurnZone object into the component, and to have to update the events property from within a call to run on that VmTurnZone. It just seems a bit overly complicated to me. But if I DON'T do it in the VmTurnZone.run method, when the events property gets updated, the view is not updated to reflect that change. Only from within VmTurnZone.run does it work.
Is there a simpler way to do what I'm after here? I've looked at adding a watch to the scope, but that didn't seem to work (and looks to me like it should be used for changes that happen the other way around - ie, the view updates the scope, and you want to do something when that happens).
Can any AngularDart experts let me know if there's a better way to do what I'm after?
https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart:async.Stream
You could add your events to stream and then listen for these and signalr will see these changes, for example:
#Component( selector: 'liveEvents', templateUrl: 'live_events.html', useShadowDom: false )
class LiveEvents implements ScopeAware {
EventReceiver _eventReceiver;
LiveEvents( this._eventReceiver );
List<Event> events = new List<Event>();
void set scope(Scope scope) {
subscribe().listen((event){
events.add(event);
});
}
Stream<Event> subscribe(){
var streamController = new StreamController<Event>();
_eventReceiver.subscribeToAllEvents( "localhost", (event){
streamController.add(event);
});
return streamController.stream;
}
}
As far as I know this is necessary when the source of the change is outside of the default Angular zone (for example some JavaScript library) for Angular to recognize model changes.