Embedding tracking codes in Angular 2 - javascript

I'm looking for the best way to implement tracking codes into my Angular 2 app. Not Google Analytics, but 3rd party suppliers like Marketo and others. I need the codes to fire each time I load a component(page). I've tried using the router changes with only partial success (and some unexpected results). Some version of this kind of worked but not fully.
this.router.events.subscribe(() => {
//tracking code goes here
});
Has anyone else had success with 3rd party tracking on Angular2 or other SPAs? Putting the tags in the template files doesn't work. Thanks.

Just use a guard on any route you want to track. It will be called every time the route is activated:
import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot} from "#angular/router";
import {Observable} from "rxjs";
export class TrackingGuard implements CanActivate
{
canActivate(route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean>|boolean
{
console.log(state.url);
console.log(state.queryParams);
return true;
}
}
And in your route definitions where you specify which component for the route just add canActivate: [TrackingGuard] under it. In the above example you will have access to the url and any query params. Would recommend making some other service which makes the request to the tracking api and just call if from the guard.

Related

router.navigate(url) doesn't load the component but typing the url directly into the browser does

I'm trying to load an Angular 8 component by clicking on a div like this:
showMovie(movieId) {
this.router.navigate([`/movie/${movieId}`]);
}
This used to work, and in my deployed version on netlify it still works, but when I try to do it from localhost it doesn't anymore. If I type the url directly in the browser tho, it works just fine.
Try to use this code, using queryParams, I'm not sure about the syntax:
this.router.navigate(['/movie'], {queryParams: {movieId: movieId }})
You can try this:-
At first import router
import { ActivatedRoute, Router } from '#angular/router';
declare router in constructor:
private router: Router,
then,
this.router.navigate(['/movie', movieId]);
thank u

How to go back in Ionic 4

I want to add a back button in Ionic4(Angular 7).
But I can't find the proper method in Angular Router.
import {Router} from '#angular/router';
How do we go back when clicking a button, in the component handler?
I'd like to implement it using '#angular/router' not '#angular/common' => Location
Since you are using ionic 4 then to go backward, you can do the following:
constructor(private navCtrl: NavController) {}
btnClick(){
this.navCtrl.navigateBack('/home');
}
With Angular routing you could use the Location API:
constructor(private location: Location){}
and then when you need to navigate back call:
this.location.back();
Keep in mind that for Angular 7 you have to import Location from #angular/common
Just do the following if you wanna go back to your previous route.
constructor(private navCtrl: NavController) {}
goBack(){
this.navCtrl.pop();
}
This'll pop the current page from the navigation stack and return you to the previous page and the part of the page from where you had navigated forward.

ionic - Using arguments with navCtrl.push()

I am calling a method from home.html
(click)="openPage(EventsPage)"
I am aware if I just use this method
openPage() {
this.navCtrl.push(EventsPage)
}
in home.ts it will work.
Basically I want to take in a different argument depending on whats clicked but only use one method to navigate to the selected page e.g.
(click)="openPage(EventsPage)"
(click)="openPage(TimetablePage)"
openPage(page) {
this.navCtrl.push(page)
}
Do I need to just use a switch statement or ifs? I was hoping I could keep it to just the one or two lines.
Any suggestions or advice appreciated thanks.
Since this seems like a project that you are just starting with or fiddling with, this is what I'd suggest.
when you create any new pages, create page.module.ts along with them.
a typical page.module.ts would look like this
import {IonicPageModule} from 'ionic-angular';
import {NgModule} from '#angular/core';
import {YourPage} from './your-page.ts';
#NgModule({
declarations: [YourPage],
imports: [IonicPageModule.forChild(YourPage)]
})
export class YourPageModule{}
this is how your-page.ts would look like
import {Component} from '#angular/core';
import {IonicPage} from 'ionic-angular';
#IonicPage()
#Component({
selector: 'your-page',
templateUrl: 'your-page.html'
})
export class YourPage{}
when you have all of this setup, you can just send 'YourPage' string to the openPage(page) function to navigate to YourPage component.
Pushing a string instead of an imported page instance via ionics NavController also enables lazy-loading (the js-bundle for page is fetched when needed, not all at once at startup). This can significantly improve your startup-time if you have an app with many pages. I also suggest reading the IonicPage docs, they are quite useful when dealing with navigation.

Angular 2 Shared Data Service is not working

I have built a shared data service that's designed to hold the users login details which can then be used to display the username on the header, but I cant get it to work.
Here's my (abbreviated) code:
// Shared Service
#Injectable()
export class SharedDataService {
// Observable string source
private dataSource = new Subject<any>();
// Observable string stream
data$ = this.dataSource.asObservable();
// Service message commands
insertData(data: Object) {
this.dataSource.next(data)
}
}
...
// Login component
import { SharedDataService } from 'shared-data.service';
#Component({
providers: [SharedDataService]
})
export class loginComponent {
constructor(private sharedData: SharedDataService) {}
onLoginSubmit() {
// Login stuff
this.authService.login(loginInfo).subscribe(data => {
this.sharedData.insertData({'name':'TEST'});
}
}
}
...
// Header component
import { SharedDataService } from 'shared-data.service';
#Component({
providers: [SharedDataService]
})
export class headerComponent implements OnInit {
greeting: string;
constructor(private sharedData: SharedDataService) {}
ngOnInit() {
this.sharedData.data$.subscribe(data => {
console.log('onInit',data)
this.greeting = data.name
});
}
}
I can add a console log in the service insertData() method which shoes the model being updated, but the OnInit method doesn't reflect the change.
The code I've written is very much inspired by this plunkr which does work, so I am at a loss as to what's wrong.
Before posting here I tried a few other attempts. This one and this one again both work on the demo, but not in my app.
I'm using Angular 2.4.8.
Looking through different tutorials and forum posts all show similar examples of how to get a shared service working, so I guess I am doing something wrong. I'm fairly new to building with Angular 2 coming from an AngularJS background and this is the first thing that has me truly stuck.
Thanks
This seems to be a recurring problem in understanding Angular's dependency injection.
The basic issue is in how you are configuring the providers of your service.
The short version:
Always configure your providers at the NgModule level UNLESS you want a separate instance for a specific component. Only then do you add it to the providers array of the component that you want the separate instance of.
The long version:
Angular's new dependency injection system allows for you to have multiple instances of services if you so which (which is in contrast to AngularJS i.e. Angular 1 which ONLY allowed singletons). If you configure the provider for your service at the NgModule level, you'll get a singleton of your service that is shared by all components/services etc. But, if you configure a component to also have a provider, then that component (and all its subcomponents) will get a different instance of the service that they can all share. This option allows for some powerful options if you so require.
That's the basic model. It, is of course, not quite so simple, but that basic rule of configuring your providers at the NgModule level by default unless you explicitly want a different instance for a specific component will carry you far.
And when you want to dive deeper, check out the official Angular docs
Also note that lazy loading complicates this basic rule as well, so again, check the docs.
EDIT:
So for your specific situation,
#Component({
providers: [SharedDataService] <--- remove this line from both of your components, and add that line to your NgModule configuration instead
})
Add it in #NgModule.providers array of your AppModule:
if you add it in #Component.providers array then you are limiting the scope of SharedDataService instance to that component and its children.
in other words each component has its own injector which means that headerComponentwill make its own instance of SharedDataServiceand loginComponent will make its own instance.
My case is that I forget to configure my imports to add HttpClientModule in #NgModules, it works.

Multipart file upload and ng serve: Angular 2

I am working on the front end of a file upload service. I am currently ignoring the service path with respect to the backend. I have run into a strange problem. I have a few generated components that sit within the app component. When I end the serve from console and do ng serve again, it errors out. It says:
The only way I have found to get rid of this is to erase my uploader service injection, save the file, then re-insert the injection. This is how it is supposed to look:
The only way to get ng serve to work is to by erasing the line private service: UploaderService
Any idea why this is happening? Am I missing something with my injection? My UploaderService is marked as Injectable() and the components that use it are under Directives.
Update:
What I found out is that it is unrelated to the UploaderService. I have a component that does not inject the UploaderService. I fix it the same way I fix the other components that inject the UploaderService. By deleting the parameters of the constructor, saving, and then putting the parameters back. Then it will serve
Update2:
The generated componenet, upload.component.t, has a spec file that is generated with it, upload.component.spec.ts
It has a error that asks for parameters like so:
My UploadComponent constructor has a parameter in it, where i inject the UploaderService. In the spec.ts file, a new UploadCompent is created, but does not contain any arguments. I am guessing this is where I am going wrong. How do I work around this?
Here is my UploaderService:
import { Injectable } from '#angular/core';
import {Http, Response, HTTP_PROVIDERS, Headers, HTTP_BINDINGS, RequestOptions} from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';
import { ItemEntryComponent } from './item-entry';
import { Query } from './Query';
#Injectable()
export class UploaderService {
public URL: string;
private query: Query;
public filesSelected: Array<ItemEntryComponent> = [];
progress$: any;
progress: any;
progressObserver: any;
//CONSTRUCTOR
constructor(private http: Http) {
//***SET URL***
this.URL = 'http://localhost:7547/api/picker';
//Create Query for url
this.query = new Query(this.URL);
//Create progress attribute
this.progress$ = Observable.create(observer => {
this.progressObserver = observer
}).share();
}
}
Problem solved!
I had not realized the generated files included a spec testing file, in my example it was upload.component.spec.ts. Getting rid of those files gets rid of the errors that ask for parameters to be filled in inside the test files and now ng serve works.

Categories