I know I can enableTracing on the Angular 2 router:
export const routing: ModuleWithProviders =
RouterModule.forRoot(routes, { enableTracing: true });
Is there any way to programatically set that value?
I have a config.json file that has a number of application settings. I would like to include a boolean property that can be used to control whether tracing is on or not. I'd rather not have the customer have the ability to modify the file that contains my routes, but still have the ability to turn on tracing to help debug edge cases that didn't get caught by tests.
I'm OK with having to restart the application, but not OK with having to rebuild.
[Update]
After looking at the router source, it doesn't look do-able without a pull request. Simple explanation of what the change would need to be:
In router.ts, add a public property called tracing: boolean
In router_module.ts::setupRouter:
Change
if (opts.enableTracing) {
to
router.tracing = opts.enableTracing</pre>
Change the observable:
router.events.subscribe(e => { ...
to
router.events
.filter(e => router.tracing)
.subscribe(e => { ...</li>
3. Probably need to add some validation on the tracing property.
With these changes, one could import Router and then set the router.tracing property to turn it on and off.
I have no idea what the performance difference is between emitting all of the events with no subscriber and emitting all of the events with a filtered subscription.
For now there is no explicit way to do it programmatically. Our workaround is to enable it only for local development so that we can get all the details/exception-stacktrace etc. Something like:
let routes = [{path: ..., component : ...}, ...];
RouterModule.forRoot(routes, {
enableTracing: /localhost/.test(document.location.host)
});
Related
I am building a Remix app, and wanted to record some user analytics in my database based on what page the user was viewing. I also wanted to do so on a route by route basis, rather than just simply the raw URL.
For example: I wanted to know "user viewed URL /emails/123" as well as "user viewed route /emails/$emailId"
This problem could be generalized as "I want to run a piece of server code once per user navigation"
For my tracking I'm assuming users have javascript enabled in their browser.
Solutions I tried:
Record in the loader
This would be something like:
export const loader: LoaderFunction = async ({ request, params }): Promise<LoaderData> => {
myDb.recordPageVisit(request.url);
}
This doesn't work because the loader can be called multiple times per page visit (eg. after an action is run)
It's possible that there's some value hidden in the request parameter that tells us whether this is an initial page load or if it's a later visit, but if so I couldn't find it, including when I examined the raw HTTP requests.
It's also annoying to have to put this code inside of every loader.
Record the URL in the node code
I'm using #remix-run/node as my base remix server, so I have the escape hatch of setting up node middleware, and I thought that might be a good answer:
const app = express();
app.use((req, res, next) => {
if (req.url.indexOf("_data") == -1) {
myDb.recordPageVisit(req.url);
}
next();
});
I tried ignoring routes with _data in them, but that didn't work because remix is being efficient and when the user navigates, it is using an ajax-y call to only get the loaderData rather than getting the full rendered page from the server. I know this is the behavior of Remix, but I had not remembered it before I went down this path :facepalm:
As far as I can tell it's impossible to stateless-ly track unique pageviews (ie based purely on the current URL) - you need see the user's previous page as well.
I wondered if referer would allow this to work statelessly, but it appears that the referer is not behaving how I'd hoped: the referer header is already set to the current page in the first loader request for the data for the page. So initial load and load-after-mutation appear identical based on referer. I don't know if this is technically a bug, but it's certainly not the behavior I'd expect.
I ended up solving this by doing the pageview tracking in the client. To support recording this in the DB, I implemented a route that just received the POSTs when the location changed.
The documentation for react-router's useLocation actually includes this exact scenario as an example.
From https://reactrouter.com/docs/en/v6/api#uselocation:
function App() {
let location = useLocation();
React.useEffect(() => {
ga('send', 'pageview');
}, [location]);
return (
// ...
);
}
However, that doesn't quite work in remix - the location value is changed after actions (same text value, but presumably different ref value). So I started saving the last location string seen, and then only report a new pageview when the location string value has changed.
So after adding that stateful tracking of the current location, I landed on:
export default function App() {
// ...other setup omitted...
const [lastLocation, setLastLocation] = useState("");
let location = useLocation();
const matches = useMatches();
useEffect(() => {
if (lastLocation == location.pathname) {
return;
}
// there are multiple matches for parent route + root route, this
// will give us the leaf route
const routeMatch = matches.find((m) => m.pathname == location.pathname);
setLastLocation(location.pathname);
fetch("/api/pageview", {
body: JSON.stringify({
url: location.pathname,
// routeMatch.id looks like: "/routes/email/$emailId"
route: routeMatch?.id }),
method: "POST",
}).then((res) => {
if (res.status != 200) {
console.error("could not report pageview:", res);
}
});
}, [location]);
The matches code is not necessary for tracking just raw URLs, but I wanted to extract the route form (eg /emails/$emailId), and matches.id is a close match to that value - I strip "routes/" serverside. Matches docs: https://remix.run/docs/en/v1/api/remix#usematches
Client side pageview tracking is a bit annoying since clients are flaky, but for the current remix behavior I believe this is the only real option.
Did it a different way for routes, remix is funny cuz of the whole parent route thing * so I use a route logger
Beans.io
https://www.npmjs.com/package/beansio
Context
I've built an app that renders mails from your outlook accounts into a web page in react.
I'm trying to set a "viewed" boolean as a class property fed by redux store and change it from within the component (that change must impact in redux to manage that change on the overall app )
Problem
As you might see on below's code, i initiate the instance variable in the constructor with the given information from redux reducer,
I've tested with a bunch of console logs if the action creator successfully updates that information on the store and it actually does.
My problem is that my instance variable (this.viewed) isn't updating with redux's reducer information (that actually does update)
import React from "react"
import {connect} from "react-redux"
import { bindActionCreators} from "redux"
import * as QueueActions from "../redux/actions/actionCreators/queueActions"
class Mail extends React.Component {
constructor (props){
super(props)
this.id = props.id
this.viewed = props.mails.find(mail => mail.id = this.id).viewed
}
}
componentDidMount = () => {
this.props.queueActions.setMailToViewed(this.id);
}
function mapStateToprops () {
return {
mails : store.queueReducer.mails,
}
}
function mapDispatchToProps() {
return {
queueActions : bindActionCreators( QueueActions, dispatch ),
}
}
export default connect ( mapStateToprops, mapDispatchToProps ) (Mail)
Question
what am i doing wrong here?
why does the viewed property on redux updates but my instance variable that feeds from that very same information doesn't?
shouldn't this.viewed update whenever the props that provided the information update?
Can't i update this information from props without using a state?
I think the issue is because the assignment to this.viewed happens in the constructor, which is only called once. When the redux store updates, the component will get new props but the constructor will not be called again, so the value will not be updated. Hopefully these links will help explain the issue:
ReactJS: Why is passing the component initial state a prop an anti-pattern?
https://medium.com/#justintulk/react-anti-patterns-props-in-initial-state-28687846cc2e
I'd also recommend reading up on functional components v class components and why functional components are used alot now instead of class ones. A starting point:
https://medium.com/#Zwenza/functional-vs-class-components-in-react-231e3fbd7108
If you used a functional component, you could use the useSelector hook to access the store and update your components.
Hope this this useful, I'm quite new to react so apologies if you're looking for something more, but I hope this helps.
I'm new to NGXS and I'm trying to fully understand the docs so I can start using it knowing what I'm doing.
There is one thing I don't understand in this code snippet from here.
export class ZooState {
constructor(private animalService: AnimalService) {}
#Action(FeedAnimals)
feedAnimals(ctx: StateContext<ZooStateModel>, action: FeedAnimals) {
return this.animalService.feed(action.animalsToFeed).pipe(tap((animalsToFeedResult) => {
const state = ctx.getState();
ctx.setState({
...state,
feedAnimals: [
...state.feedAnimals,
animalsToFeedResult,
]
});
}));
}
}
Just below this code, it says:
You might notice I returned the Observable and just did a tap. If we
return the Observable, the framework will automatically subscribe to
it for us, so we don't have to deal with that ourselves. Additionally,
if we want the stores dispatch function to be able to complete only
once the operation is completed, we need to return that so it knows
that.
The framework will subscribe to this.animalService.feed, but why?
The action, FeedAnimals, uses the injected service, AnimalService to feed the animals passed in the action's payload. Presumably the service is operates asynchronously and returns an Observable. The value of that Observable is accessed via the tap function and is used to update the ZooState state context based on completing successfully.
In order to use NGXS specifically and Angular in general, you really have to understand RxJS... here's my goto doc page for it
I'm creating a model that I will use to authenticate users for API access, and I have a secret field where I want to store a Base64 encoded uuid/v4 generated value.
I went through the different field types and options, but still not seeing how I could achieve this.
Is there a way to hook in model instance creation, and set the value of my secret field ?
Yes, you can use the pre hooks.
In your situation, the basics would be:
AuthenticationModel.schema.pre("save", function(next) {
const secretValue = generateSecretValue();
this.secret = secretValue;
next();
});
That would go before your final AuthenticationModel.register(); in your model.js file.
This is how I set it up, also with the pre-save hook. My problem before was that I was getting the same random number again until I restarted the server.
Store.schema.pre('save', function (next) {
if (!this.updateId && this.isNew) {
// generates a random ID when the item is created
this.updateId = Math.random().toString(36).slice(-8);
}
next();
});
Using this.isNew was also useful in my case.
I'm writing a front-end to my RESTful API using Backbone... and I'm really enjoying it so far. Learning this framework continues to be super interesting. However, I am now stumped on something that seems like, to me at least, that it should be straight forward.
I now have a single (and only) html page where the main application resides that lists one or more products. And, lets say it resides here: http://localhost/index.html
I would like to be able to switch from the product list view to the new product view (via click event on a button at the top). And that, from what I understand, I need to begin using a router for switching using the pattern described in How to switch views using Backbone.js.
Is view-switching what I need to be doing to achieve this behavior?
This looks hokey: http://localhost/index.html#product/newAnd, since I'm using [tornado](http://tornadoweb.org) as my web server for both my API and static content, I can't just implement a rewrite rule easily. I may switch to using nginx for static content in the near future, but I haven't yet. If I'm to use a router to switch views like when going from Review to Create (of CRUD operations), how do I change the URL/URI to look something more along the lines of thishttp://localhost/product/new
In order to receive hashless url changes, your browser has to support pushstate. If I am not mistaken, Backbone will fallback to using hashes if your browser does not support pushstate. You would initialize your router with the following in order to use pushstate in your application:
Backbone.history.start({pushState: true})
I like #alexanderb's use of view switching. Just MAKE sure when you are changing views, you dispose of them properly. Otherwise you will run into some complex problems that are difficult to debug. Read more here.
Yes, you need 2 things - Router and ApplicationViewManager (some class, that is responsible for changing the view).
define(function () {
var ViewManager = function () {
return {
show: _showView
};
};
function _showView(view) {
if (this.currentView) {
this.currentView.close();
}
this.currentView = view;
this.currentView.render();
$("#app").html(this.currentView.el);
}
return ViewManager;
});
In router, you do something like:
// router
var ApplicationRouter = Backbone.Router.extend({
initialize: function () {
this.viewManager = new ViewManager();
},
routes: {
'': 'dashboard',
'configure/sites/:id': 'configure'
},
dashboard: function () {
var app = require('./apps/DashboardApp');
app.run(this.viewManager);
},
configure: function (id) {
var app = require('./apps/ConfigureApp');
app.run(id, this.viewManager);
}
});
Some code examples, you can take from this repository.