I need to grab the value of a key form URL. Let's say the URL is http://localhost:8080/foo=bar. How do I grab the value 'bar' onEnter function with in react-router so that it can be used to trigger other functions.
Thank you in advance.
URL: http://localhost:8080/foo=bar
// routes.js
var React = require('react');
var ReactRouter = require('react-router');
var Main = require('./templates/main.jsx');
var Sub = require('./templates/sub.jsx');
module.exports = {
path: '/',
component: Main,
indexRoute: { component: Main },
childRoutes: [
{
path: '/foo=:bar',
component: Sub,
onEnter: function(){
// *****************************
// grab the value 'bar' from param
}
}
]
}
The onEnter function takes two arguments. nextState and transition. The value you're looking for is inside of nextState.params:
onEnter: function(nextState, transition) {
let bar = nextState.params.bar;
}
Related
Is it possible to know if route has resolvers or not with router.events?
I tried to find it with:
this.router.events.subscribe(e => {
console.log(e);
})
But it seems that there is no information about resolvers in router events. I need this for progress bar. Maybe ActivatedRoute can be useful in this case? But where exactly should i look? activateRoute.snapshot.data is always empty object
I also tried:
private isRouteHaveResolvers() {
let firstChild = this.activatedRoute.firstChild;
while (firstChild && firstChild.firstChild) {
firstChild = firstChild.firstChild;
}
return firstChild && firstChild.routeConfig && !!Object.keys(firstChild.routeConfig.resolve).length;
}
But it doesnt work properly. For example for this case:
{
path: 'edit',
component: EditComponent,
resolve: {
data: resolverData
},
children: [{
path: 'activity/:activityId',
component: ModalComponent,
outlet: 'modal',
}],
}
For this route activity/:activityId it return true
I am using vue-router to redirect to a new URL after clicking on a button. I would like the router to only add a query parameter into the URL if the query parameter is actually filled. So if it is null it should not add the parameter.
Right now this is not working as expected. Have a look at the following code snippet (choose each of the options and click the button).
(it seems you can't use routing on Stackoverflow so please also have a look at the snippet on JSFiddle: https://jsfiddle.net/okfyxtsj/28/)
Vue.use(VueRouter);
new Vue({
el: '#app',
router: new VueRouter({
mode: 'history',
}),
computed: {
currentRoute () {
return this.$route.fullPath
}
},
data () {
return {
some: this.$route.query.some || null
}
},
template: '<div><select v-model="some"><option :value="null">Option (value = null) which leaves empty parameter in URL</option><option value="someParameter">Option (value = someParameter) which shows parameter with value in URL</option><option :value="[]">Option (value = []) which removes parameter from URL</option></select><br><button #click="redirect">Click me</button><br><br><div>Current Path: {{ currentRoute }}</div></div>',
methods: {
redirect () {
this.$router.push({
path: '/search',
query: {
some: this.some || null
}
})
}
},
watch: {
'$route.query': {
handler(query) {
this.some = this.$route.query.hasOwnProperty('some') ? this.$route.query.some : null
},
},
},
watchQuery: ['some']
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app"></div>
When some is null the parameter will still be added to the URL. When some is an empty array it will not be added to the URL.
I don't want to use an empty array as default value since the value should always be a string.
So how can I make sure the query parameter is only added to the new route if it contains a value other than null?
Use a simple if-else or ternary construct. Also prefer computed property for some instead of watcher:
Vue.use(VueRouter);
new Vue({
el: '#app',
router: new VueRouter({
mode: 'history'
}),
computed: {
some() {
return this.$route.query.some || null;
}
},
data() {
return {};
},
methods: {
redirect() {
const routeConfig = this.some
? { path: '/search', query: { search: this.some } }
: { path: '/search' };
this.$router.push(routeConfig);
}
},
// Remaining config like template, watchers, etc.
});
We're using Angular-ui-router for our app state management.
A problem we're having is that every component refreshes when the $state changes, even if it's a variable that is updated that does not exist/not used in some components.
For example, we have a TickersPanel and a TagsPanel. When a Ticker is selected, the TagsPanel should be updated and refreshed, however when a tag is selected in the TagsPanel, the TickersPanel should NOT refresh, but it currently does.
I found out that { notify: false } is another object that can be passed in. However once I do that, NO components will refresh.
const save = (tag, terms) => {
CONTAINER.push(tag);
$state.go('charting', Term.write(terms), { notify: false }).then((stateResult) => {
console.log('stateResult', stateResult);
});
};
Has anyone found a way around this problem?
TagsPanel $onInit (Runs on every $state change)
this.$onInit = () => {
tagsCol.addEventListener('scroll', scrollingTags);
this.tags = [];
this.limit = 30;
const panelOpen = Dashboard.panelStatus(this.tagsOpen, this.chartMax);
panelOpen ? buildTagList() : collapsePanel();
};
TickersPanel $onInit (Runs on every $state change, if $state.params.ticker did not change, then this should not be ran)
this.$onInit = () => {
this.tickers = [];
this.spy = {
longname: "SPDR S&P 500",
ticker: "SPY",
};
this.showTickersPanel = Dashboard.panelStatus(this.tagsOpen, this.chartMax);
// const currentState = $state.params;
// const prevState = Dashboard.getPreviousState();
loadTickers().then(() => this.loadingTickersDone = true);
};
I've made a JsFiddle to describe a way to achieve what I've understood from your question using the uiRouter.
The fiddle uses state inheritance and named views to only reinitialize the tags state when the tickers param changes.
[..]
// Using # on the view names to refer to the absolute uiViews.
$stateProvider.state('tickers', {
url: "",
views: {
'tickersPanel#': {
template: [..]
bindToController: true,
controllerAs: "$ctrl",
controller: function() {
this.$onInit = function() {
console.info('Tickers Panel $onInit');
this.tickers = ["Ticker1", "Ticker2", "Ticker3"]
}
}
},
[..]
}
});
$stateProvider.state('tags', {
parent: 'tickers',
url: "/:ticker",
views: {
'tagsPanel#': {
template: [..]
controllerAs: '$ctrl',
bindToController: true,
controller: function($stateParams) {
this.$onInit = function() {
console.info('Tags Panel 2 $onInit');
this.ticker = $stateParams["ticker"];
}
}
}
}
});
[..]
Why not just maintain the state in your component and return at the beginning of the function if the state hasn't changed?
This is my component class (Part of it).
updateStore: function() {
console.log("Updating state in the Calendar.js");
this.setState(this.getInitialState());
},
componentDidMount: function() {
EventsStore.addChangeListener(this.updateStore, 'CHANGE');
},
componentDidUnmount: function() {
EventStore.removeChangeListener(this.updateStore);
},
This is my action (Tuxx)
var Actions = require('tuxx/Actions');
var eventsStore = require('../Stores/EventsStore');
var jQ = require('jquery');
var eventsActions = Actions.createActionCategory({
category: 'events',
source: 'standard',
actions: ['create', 'get']
});
eventsActions.register(eventsStore, {
create: eventsStore.onCreate,
get: eventsStore.onGet
});
eventsActions.before('get', function (nextCallback, actionBody) {
jQ.get('http://127.0.0.1:8181/events').done(function(resp) {
nextCallback(resp);
});
});
module.exports = eventsActions;
And this is part of my store
onGet: function(resp) {
resp = JSON.parse(resp);
this._events = resp;
console.log(this._events);
console.log("Emiting change")
this.emitChange('CHANGE');
},
And last, this is my init code:
eventsAction.get();
var App = React.createClass({
render: function() {
return (
<div>
<RouteHandler />
</div>
)
}
});
var routes = (
<Route name="app" path="/" handler={App}>
<DefaultRoute handler={Calendar} />
<Route name="event.edit" path="/event/:eventId" handler= {EventEditForm} />
</Route>
);
Router.run(routes, function(Handler) {
React.render(<Handler />, document.getElementById("main"));
});
As far as I understand, it should re render my component when emitChange is run.
This is my console output:
I think it should hit the
console.log("Updating state in the Calendar.js");
part, but it doesn't.
I am far from being competent in JS world, so I need help.
Thank you in advance.
This is how store is required:
var EventsStore = require('./Stores/EventsStore');
store is saved as follows:
Store is defined as:
var Stores = require('tuxx/Stores')
var eventsStore = Stores.createStore({
_events: [],
getAll: function () {
return Object.keys(this._events);
},
(...)
(...)
onGet: function(resp) {
resp = JSON.parse(resp);
this._events = resp;
console.log(this._events);
console.log("Emiting change")
this.emitChange();
},
register: function () {
return {
events: {
create: this.onCreate,
get: this.onGet
}
};
}
});
module.exports = eventsStore;
In the component I use it using EventsStore variable which was created from:
var EventsStore = require('./Stores/EventsStore');
Second edit.
I was still digging and I found out this:
componentDidMount: function() {
EventsStore.addChangeListener(this.updateStore);
console.log('Calendar::componentDidMount');
console.log(EventsStore.listeners());
console.log('----')
},
And the result in the console is:
[Log] Calendar::componentDidMount (app.js, line 36083)
[Log] [] (app.js, line 36084)
Having looked at the Tuxx source code, I believe that should work. You can also omit that second "CHANGE" parameter to emitChange and addChangeListener and it'll use a default. I assume it's the same instance of the store you're using everywhere?
Looking at Tuxx, they use the createOwnerClass and connectOwnerToStore combo to make all of this happen automatically - see the initial guide on the homepage. Perhaps using that approach would help you track down the bug?
I am getting the error:
Component [name] not available with base [path]
when trying to dynamically attach components to a network's ComponentLoader instance.
var components = []; // This is populated with noflo.Component instances at runtime.
var graph = {
properties: { name: 'Test components' },
processes: {
log: { component: 'log' },
split: { component: 'split' }
},
connections: [
{
data: 'John Doe',
tgt: { process: 'split', port: 'in' }
},
{
src: { process: 'split', port: 'left' },
tgt: { process: 'log', port: 'in' }
},
{
src: { process: 'split', port: 'right' },
tgt: { process: 'log', port: 'in' }
}
]
};
noflo.graph.loadJSON(graph, function(g) {
noflo.createNetwork(g, function(n) {
var getComponent = function(c) {
return c;
}
for (var i = 0; i < components.length; i++) {
var c = components[i];
n.loader.components[c.key] = {};
n.loader.components[c.key].getComponent = getComponent.bind(null, c);
};
});
});
I have also tried assigning the component directly to the property for the components collection in the loader:
n.loader.components[c.key] = c;
You can utilize the NoFlo ComponentLoader's registerComponent method for this. The trick is to start the NoFlo Network in delayed mode by passing it true as the third parameter. This way it won't start execution immediately, and you can first register your components to it.
// Create NoFlo network in delayed mode (see the third param in the end)
noflo.createNetwork(g, function(n) {
// Register the custom components
components.forEach (function (component) {
n.loader.registerComponent('myproject', component.key, {
getComponent: function () { return component; }
});
});
// Once the components have been registered we can wire up and start it
n.connect(function () {
n.start();
});
}, true);
The other option would be to register a custom ComponentLoader in your project. That way you wouldn't need to do anything special when starting networks.
See for example how this is done in noflo-polymer (manifest, custom loader) on browser or MicroFlo (manifest, custom loader) on Node.js.
Custom component loaders have been supported since NoFlo 0.5.1.