I'm using react-router 1.0.3. Say I define these routes
<Route path="/" component={App} onEnter={appOnEnter}>
<Route path="test" component={Test} onEnter={testOnEnter} />
</Route>
Both appOnEnter and testOnEnter are asynchronous functions. By default testOnEnter waits for appOnEnter to finish and call the callback to run, but I want those 2 onEnter functions to run simultaneously, how can I do that?
This won't work with the react-router 1.0.3. I checked the source-code of it and don't see a way to call both hooks at the same time.
Here is a reference to the onEnter-Hook handling, if you might want to take a deeper look into it: https://github.com/rackt/react-router/blob/v1.0.3/modules/TransitionUtils.js
Maybe you can find a way to trick the system...
But I also would not recommend doing it. I don't have an idea of what you want to achieve but I think you should find a different approach, using other life-cycle hooks that react provides.
Related
I am using redux-observable and in my epics I am changing the route in some of them using browserHistory.push. Is this standard/good practice? my gut feeling tells no, however need your opinion. In addition to this after upgrading to react-router 4, I cannot get access to browserHistory how do I do this?
Imperatively calling browserHistory.push() is totally fine. If it works for your use case and you don't need to know about the route changes in your reducers, carry on!
That said, the "purist" way would be for your epic to emit an action that will cause the route change. This can be done in react-router v4 by using the new react-router-redux, which supersedes the old one with the same name.
Once added, you can use the provided actions creators:
import { push } from 'react-router-redux';
export const somethingEpic = action$ =>
action$.ofType(SOMETHING)
.switchMap(() => {
somethingLikeAjaxOrWhatever()
.mergeMap(response => Observable.of(
somethingFulfilled(response),
push('/success-page')
))
});
To be clear, push() replace() etc in react-router-redux are action creators--aka action factories. They don't actually cause the route change themselves, or perform any side effects at all. They return an action that needs to be dispatched somehow for the route to change.
The other benefit of using actions to signal route change intents is testability. You no longer have to mock a history API, you can just assert that the epic emits the expected action. This is known as "effects as data", since your code doesn't perform the actual side effect, you just generate the intent.
I'm loving v4 but in 4.1.2 this keeps tripping me up when using the browser router:
With a component in a Route component I have these props passed in: {computedMatch, location, path} although the documentation tells me to expect {match, location, history} which is what I get with the hash router.
To get the history passed in I have to use the withRouter wrapper which feels very clunky because the relevant component is the component prop to a Route component.
The documentation sounds right to me. Is this a bug?
You can get access to {match, location, history} if you use Route as
<Route path="/" component={myComponent}
In above code you will have match location and history accessible inside myComponent.
Or else you have to use withRouter
I am creating a blog using React/React-Router/Redux, and depending on the location, I want to show different blog posts, so if you are on /category/cat1 you don't have the same list of blogs than if you are on /category/cat2.
So I'm thinking on storing the blog list in the store and my react component would pick it up from there so I could just have actions updating my state.
My thinking is listening on the browserHistory so I can get when the location changes and trigger the action which would update the new blog list, but I can't find how to get the params from my location path like I would from my component props /category/:catName
How can I get those params?
Is it actually the way to do it or should I use a different approach?
Thanks.
From the version 2.0.0 upgrade guide:
RoutingContext -> Router render prop
You can now pass a render prop to Router for it to use for rendering.
This allows you to create "middleware components" that participate in
routing. Its critical for integrations with libraries like Relay,
Redux, Resolver, Transmit, Async Props, etc.
The default is basically this:
<Router render={props => <RouterContext {...props} />} />
This render prop on the Router is a hook into the render stage, and those props that are passed to the function contain the parsed route params, as well as lots of other routing context. Just make sure your function returns a RouterContext, or some equivalent, in the end.
I have designed a webpage with a single portlet.
I want to pass parameters to this portlet.
So I am calling my webpage this way (on a local liferay instance):
http://localhost:8080/group//?param=1
To avoid misunderstandings, I am not just trying to pass parameters between pages of a portlet (controller to view). I know how to do this and it works fine.
In this case I am trying to start my portlet with parameters from the URL hosting it.
My understanding from the docs is that the portlet is not able to access the URL parameters
The advised solution that I read about was to use "friendly-url-mapper".
I did not manage to get it to work. Here is what I did so far:
I added into liferay-portlet.xml:
<friendly-url-mapper-class>com.liferay.portal.kernel.portlet.DefaultFriendlyURLMapper</friendly-url-mapper-class>
<friendly-url-mapping>view</friendly-url-mapping>
<friendly-url-routes>friendly-url-routes-for-parameters.xml</friendly-url-routes>
Is view the correct value to set? Real doubt about it.
I created friendly-url-routes-for-parameters.xml with this content:
<routes>
<route>
<pattern>/result?{match}</pattern>
<implicit-parameter name="param">{match}</implicit-parameter>
</route>
</routes>
/result is mapped to a result.jsp page into my controller class (this part works):
I would expect that :
http://localhost:8080/group//?param=
and / or
http://localhost:8080/group///-/view/?param=
would redirect to my result.jsp with the ?param= parameter.
What did I not understand correctly?
Ok you can and you can't but basically you shouldn't, but I'm no one to tell you what you should and shouldn't do .
What you should do to get this parameter is get hold of the original servlet
HttpServletRequest httpReq = PortalUtil.getOriginalServletRequest(PortalUtil.getHttpServletRequest(req));
Then you can simply get the parameter you wish by using the getParameter
method
String myParam = httpReq.getParameter("param");
Hope this helps
I have the following three components defined in the Caste-Windsor XML configuration for my application:
<component id="StringFactory"
service="IStringFactory, MyApp"
type="DefaultStringFactory, MyApp"
lifestyle="singleton"
/>
<component id="TheString"
type="System.String"
factoryId="StringFactory"
factoryCreate="CreateString"
>
<parameters>
<name>SomeString</name>
</parameters>
</component>
<component id="TheTarget"
service="ITarget, MyApp"
type="TheTarget, MyApp"
lifestyle="transient"
>
<parameters>
<aString>${TheString}</aString>
</parameters>
</component>
And the following facility defined:
<facility id="factory.support"
type="Castle.Facilities.FactorySupport.FactorySupportFacility, Castle.MicroKernel"
/>
When I run the application and set a breakpoint in the constructor of the TheObject class, the value passed in as the aString parameter is "${TheString}" when I expect it to resolve to the value of the component with that name.
Also, I have a breakpoint in the StringFactory constructor and CreateString method, neither of which are hit. I know the configuration is being used as other components are resolving correctly.
What am I missing or doing wrong here?
UPDATE
In light of the huge tangient this topic has taken, I've refactored the code above to remove anything to do with connection strings. The original intent of this post was about injecting a property with the value returned from a method on another object. Somehow that point was lost in a discussion about why I'm using XML versus code-based configuration and if this is a good way to inject a connection string.
The above approach is far from an original idea and it was pulled from several other discussions on this topic and our requirements are what they are. I'd like help understanding why the configuration as it is in place (whether the right approach or not) isn't working as expected.
I did verify that the first two components are being instantiated correctly. When I call Container.Resolve("TheString"), I get the correct value back. For whatever reason, The parameter syntax is not working correctly.
Any ideas?
While not a definitive solution to what I need to do in my application, I believe I've figured out what is wrong with the code. Or at least I've found a way to make it work which hints at the original problem.
I replaced the String type for TheString with a custom class. That's it. Once I did that, everything worked fine.
My guess is that it has something to do with the fact that I was trying to use a ValueType (primitive) as a component. I guess Castle doesn't support it.
So, knowing that's the case, I can now move on to figuring out if this approach is really going to work or if we need to change direction.
UPDATE
For the sake of completeness, I thought I'd go ahead and explain what I did to solve my problem AND satisfy my requirements.
As before, I have access to my configuration settings through an IConfigurationService defined as:
<component id="ConfigurationService"
service="MyApp.IConfigurationService, MyApp"
type="MyApp.RuntimeConfigurationService, MyApp"
lifestyle="singleton"
/>
This is automatically injected into my (new) IConnectionFactory which is responsible for generating IDbConnection objects based on the connection strings defined in the application's configuration file. The factory is declared as:
<component id="ConnectionFactory"
service="MyApp.Factories.IConnectionFactory, MyApp"
type="MyApp.Factories.DefaultConnectionFactory, MyApp"
lifestyle="singleton"
/>
In order to resolve what connection is used by my repository, I declare each connection as a component using the ConnectionFactory to create each instance:
<component id="MyDbConnection"
type="System.Data.IDbConnection,
System.Data, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
factoryId="ConnectionFactory"
factoryCreate="CreateConnection"
lifestyle="transient"
>
<parameters>
<connectionStringName>MyDB</connectionStringName>
</parameters>
</component>
Notice the fully described reference to System.Data. I found this is necessary whenever referencing assemblies in the GAC.
Finally, my repository is defined as:
<component id="MyRepository"
service="MyApp.Repositories.IMyRepository, MyApp"
type="MyApp.Sql.SqlMyRepository, MyApp.Sql"
lifestyle="transient"
>
<parameters>
<connection>${MyDbConnection}</connection>
</parameters>
</component>
Now everything resolves correctly and I don't have ANY hard-coded strings compiled into my code. No connection string names, app setting keys or whatever. The app is completely reconfigurable from the XML files which is a requirement I must satisfy. Plus, other devs that will be working with the solution can manage the actual connection strings in the way they are used to. Win-win.
Hope this helps anyone else that runs into a similar scenario.
You don't really need XML registrations here, since you probably don't need to swap components or change the method used without recompiling. Writing a configurable app does not imply having to use XML registrations.
The problem with this particular XML registration you posted is that the connection string is a parameter, but it's treated like a service.
Doing this with code registrations is much easier, e.g.:
var container = new WindsorContainer();
container.Register(Component.For<IConfigurationService>().ImplementedBy<RuntimeConfigurationService>());
container.Register(Component.For<ITheRepository>().ImplementedBy<TheRepository>()
.LifeStyle.Transient
.DynamicParameters((k, d) => {
var cfg = k.Resolve<IConfigurationService>();
d["connectionString"] = cfg.GetConnectionString();
k.ReleaseComponent(cfg);
}));
Or if you don't want to depend on IConfigurationService, you could do something like:
container.Register(Component.For<ITheRepository>().ImplementedBy<TheRepository>()
.LifeStyle.Transient
.DependsOn(Property.ForKey("connectionString")
.Is(ConfigurationManager.ConnectionStrings[ConfigurationManager.AppSettings["connName"]].ConnectionString))