Angular UI Router - any way of referencing the same url variable more than once? - angularjs

I would like to implement finite netsing of ui router states (it is required by the client), the maximum depth of nesting can be a fixed value (for example 10) therefore i would like my states to look as such:
.state('page',{url:'page/{pageName}',templateUrl:...)
.state('page.page',{url:'/{pageName}',templateUrl:...)
.state('page.page.page',url:'/{pageName},templateUrl:...)
etc...
In the end i would like for user to be able to enter urls like:
'/page/page1/subpage1'
'/page/page2/subpage1/subsubpage2'
'/page/page3'
etc...
The obvious problem is that ui router will overwrite the 'pageName' variable for each (sub)state, therefore after navigating to /page/page1/subpage1 i will only have {pageName:'subppage1'} variable set in state 'page' and 'page.page'.
In the whole application the ui-sref would only use the RELATIVE states so when i am in the 'page' state i would go into 'page.page' state and so on. I would like to be able to move the html view of the page up or down the state hierarchy as needed, and i would like not to change every ui-sref call in such case (right now i would have to change the variable name because it has to have different name in every state)
Is there any possibility to achieve my desired scenario? i have tried defining 'pageName' as an array type, but it does not seem to work (gets overwriten for every substate). Using custom ui router type also seems to not work since it cannot modify stateParams objects, only returns the representation of single url value (therefore the 'pageName' is also overwritten).
Ui-router extras also seems not to help here, or maybe i am missing something.

Related

Render AEM pages in React dynamically independently of URL path

I have the need to be able to render any of the pages in my AEM model.json dynamically regardless of the current URL in a SPA React app.
My AEM model.json structure has pages following the /<country>/<language>/rest/of/path format, but I want to be able to strip the country/language and just use the rest of the URL path.
I am able to do this when I initialize the ManagerModel with a the desired path like this:
const path = `/path/to/<my_model>.model.json`
/* initialize the ModelManager with the path to the model.json */
ModelManager.initialize({ path })
/*
grab the desired section of the model and render by calling ReactDOM.render
By doing this I am able to render the section of the model that maps /us/en/user-account` for
example, and render the correct content even though the current browser path is `/`
*/
ModelManager.getData(`/us/en/<page_to_load>`).then(render)
When I handle navigation with history.push (I use react-router), I want to be able to render another page following the same logic. By default, having executed ModelManager.getData("/us/en/<page_to_load>"), every page that I navigate to then renders that same portion of the model.
To fix this, I have tried many variations of ModelManager.getData() calls with no success. The only thing that I have been able to have any success with is dynamically passing the path to the next page to render to a callback function that is defined on the index.js level and passed down as a prop to App.js. The callback triggers another ReactDOM.render call and loads the page correctly regardless of what the actual URL path is. That code looks something like this:
<App
reRender={(path) => {
/* manipulate the path so that it properly maps to the correct AEM model data */
const updatedPath = `/us/en/${path}`
/*
this works, but causes another ReactDOM.render call every time that the current page is
changed
*/
ModelManager.getData(updatedPath).then(render)
}}
/>
There are also cases where the page that has been navigated to doesn't have a corresponding path in the modelStore mapping. I am able to handle that like this:
const pathToInsert = `/<country>/<language>/${window.location.pathname}.model.json`
ModelManager.modelStore.insertData(pathToInsert)
ModelManager.getData(pathToInsert).then(render)
/*
I have been having issues with this, but can get the newly inserted model to load properly by
re-routing back to the current path from the current path
*/
this.props.history.push(window.location.pathname)
I have read and re-read the documentation here and I am unable to figure out the correct way to do what I want to do. The above solutions work for the most part, but are pretty hacky and I would like to find out the proper way to accomplish this. Any help is greatly appreciated!
Yes, I found a work around solution. The AEM library can't handle this requirement out of the box unfortunately. My solution was to make a wrapper for my App component. In that wrapper I initialize the model manager, clone the data and store it in local stage. Then you can conditionally modify the model and pass it along as a prop to your App component. If you have a good understanding of how AEM model data is mapped you should be able to figure out a way to display what you want. You can also fetch and insert models into your master model's ":children" prop (think that is the field name, have not looked in a while).

Possible to limit list of params when using React Router?

Using React with React-Router in a project and am wondering if it's possible to limit the possibilities for param names, like so:
www.mydomain.com/books/:id
and allowing 'Catcher in the Rye' and 'To Kill A Mockingbird' to be passed through, like so:
www.mydomain.com/books/catcher-in-the-rye
www.mydomain.com/books/to-kill-a-mocking-bird
I want to say that only a specific set of books can be used in place of :id (just so someone can't type in www.mydomain.com/books/whatever-they-want and have an empty React component render).
I do currently have a '*' route that catches anything not mentioned, but because params are dynamically generated based on whatever is passed, that won't help in this case.
Is this possible? Thanks.
You need to handle this logic in the component. Depending on if this is an already mounted component or not you will need to put the logic in the appropriate function (componentDidMount, componentWillReceiveProps)
if(!(this.props.params.id in myAcceptableParameters)){
redirect to a 404 here
}

Prevent deep link in react-router

In my application I'd like to have certain portions of the app not be able to deep linked to. For example our users have a list of surveys and I'd like if someone tried to go directly to a particular survey directly such as /survey/1 that react router would pick up on this and immediately redirect them back to /survey and they would have to select the one they want. I've tried to write onEnter hooks but they seem to be very cumbersome since the only way I've been able to get them to behave correctly is to store some global state that says they have been to the main page and inspect that every time the route is navigated to.
Im using pushstate in my application if that makes any difference and react-router 2.0
I'd like to try to avoid having to write server rewrite rules for this since there are a lot of areas in my application where this rule is applicable.
I have a suggestion which is similar to the onEnter hook:
Wrap the component of the survey/:id route with a function which verifies if deep linking is allowed or not, let's call this function preventDeepLinking
The preventDeepLinking function checks if the location state contains a certain flag, let's say allowDeep. This flag would be set in the location state when navigating from another page of your app. Obviously, this flag will not be set when the user tries to navigate directly to the page of a survey.
The preventDeepLinking function will render the wrapped component only if deep linking is allowed, otherwise will redirect to a higher route.
I created a sample on codepen.io. You can play with it in the debug view of the Pen: http://s.codepen.io/alexchiri/debug/GZoRze.
In the debug view, click the Users link and then on a specific user from the list. Its name will be displayed below. Notice that its id is part of the url. Remove the hash including the ?_ and hit Enter. You will be redirected to /users.
The code of the Pen is here: http://codepen.io/alexchiri/pen/GZoRze
The preventDeepLinking function can be improved, but this is just to prove a point. Also, I would use the browserHistory in react-router but for some reason I couldn't get it running in codepen.
Hope this helps.

Why does the URL in UI-Router keep resetting?

I'm using AngularJS for the framework of my web application, and UI-Router for nested states. While I'm changing be states in UI-Router, as long as I'm changing between sibling states, the URL gets updated correctly, but when I change to another parent state, the URL becomes:
http://{domain}:{port}/#/
What could be causing it?
This won't be a common problem for others, so I'll post what solved the issue in hopes no one gets stuck for as long as I did.
The common URL address for states (UI-Router concept) in my application is
xttp://domain.com/#/{siteId}/state1/state1a (where {siteId} is a variable integer)
The problem here is that when changing between states, siteId cannot be passed along the top level. This means that when a link takes a user from state 1a to state 2a, siteId will not be passed along, and routing will fail.
I solved the issue by putting siteId in a cookie rather than in the URL as a path variable. The common URL address after solving the issue then became:
xttp://domain.com/#/state1/state1a
Simon S.

Backbone - Get Router parameter and reload same route

I have a table and I'm deleting a row. When I delete this row, I want to reload the current Backbone route, but few things to take in count:
e.g I'm in this route
"oneurl#order-by-name/page-8"
If the table, at that page, has only one record and I delete it, I would like to redirect to #order-by-name/page-7, so I need to get the "page" parameter. If there are still records, I want to "reload" the same route (to add next page's row if there are). It seems is not possible (https://github.com/jashkenas/backbone/issues/1214).
Any ideas?
As discussed in the issue you link (mainly jashkenas's comment "trigger: true is usually a code smell"), you shouldn't really be using routes like that for a javascript application.
What you should be doing instead:
have somewhere to store the current application's state (e.g. a plain javascript object) which is passed to the function taking care of displaying the views and data you want
when a row is deleted, modify the application state object (e.g. changing the current page attribute) and call the same function as above, once again passing in the configuration
update the url with navigateonly if the fragment should change
In reverse, your router should parse the url fragment to generate a configuration object discussed above, and call the function to render the views (passing in the configuration info).
For more on how to manage routing without using trigger: true everywhere, take a look at http://lostechies.com/derickbailey/2011/08/03/stop-using-backbone-as-if-it-were-a-stateless-web-server/ and the free preview to my book (on Marionette, but the same applies to Backbone) which also contains a chapter discussing route handling.

Resources