Angular 2 - How to add permalink? - angular2-routing

I try to add permalinks to my angular 2 app, but I didn't find a way yet.
My app-routing.module.ts:
const routes: Routes = [
{ path: '', pathMatch: 'full', component: TagelerComponent },
{ path: 'tageler', component: TagelerListComponent },
{ path: 'tageler-details/:id', component: TagelerDetailsComponent },
{ path: 'group/:id', component: GroupDetailsComponent },
{ path: 'tageler/admin', component: AdminComponent },
];
#NgModule({
imports: [ RouterModule.forRoot(routes) ],
exports: [ RouterModule ]
})
export class AppRoutingModule {}
And my code looks like this:
<div *ngFor="let group of groups | groupTypeFilter: 'Trupp' ">
<a routerLink="/group/{{group._id}}">
<li class="list-group-item">
{{group.name}}
</li>
</a>
</div>
In this example the URL is something like: http://localhost:4200/group/5910c2282249261cc61d0a7e
Instead of the group id, I would like to display the name of the group (e.g. dogs) without changing the routing, so the URL changes to: 'http://localhost:4200/group/dogs'.
I'm new to Angular 2, so I don't really know whether this is actually possible.
I would be glad if someone could help me!

The answer is you can not replace the id with the name in the routerLink, and then query the group by id, assume a user opened a new tab and entered the URL
http://localhost:4200/group/dogs
The app will load and the only reference you have is dogs, where will the id come from? unless you have already defined a dictionary that returns the id of the given group name, or by using some black magic
However in case you only let the user to access the detail page through its parent, then you can pass the group instance by using a shared service and set the router link to group.name.
<div *ngFor="let group of groups | groupTypeFilter: 'Trupp' ">
<a [routerLink]="shared.setGroupLink(group)">
<li class="list-group-item">
{{group.name}}
</li>
</a>
</div>
constructor(public shared: SharedService) { }
in SharedService
export class SharedService{
currGroupId: number;
constructor() { }
setGroupLink(group) {
// you can pass the object as a whole but I'll use `group.id` in this example
// this.currGroup = group;
this.currGroupId = group.id;
return '/group/' + group.name;
}
}
In GroupDetailsComponent
groupId: number;
constructor(public shared: SharedService) { }
ngOnInit() {
this.groupId = this.shared.currGroupId;
}
NOTE: this approach will not work if the user entered http://localhost:4200/group/dogs because groupId is undefined
by the way, avoid prefixing your variables' names with the underscore _, check Angular Style Guide

Related

Vue3 router: How to make an active menu item when on a sub route?

So, it looks I am doing something wrong.
From the docs, if I have a <router-link to="/articles">Articles</router-link> if the current routes are either /articles, /articles/1, /articles/2, articles/2/foo should always get at least router-link-active class... and when it is "root" like /articles it should also get a router-link-exact-active
but mine doesn't... I only get both (router-link-active and router-link-exact-active) when on /articles... but when on /articles/1 (or any other sub route) I get nothing.
this is my router main
const routes = [
{
path: "/",
name: "Landing",
component: LandingPage,
},
{
path: "/articles",
name: "ArticlesList",
component: ArticlesListPage,
},
{
path: "/articles/:id",
name: "ArticleDetails",
component: ArticleDetailsPage,
}, ...
]
const router = createRouter({
history: createWebHistory(),
routes,
});
I would get it right, if the path "/articles/:id" would be a part of "/articles" children, but then I would need a nested router-view, which I don't want

How to use Aurelia's router with server-side routing

I have an Aurelia project that uses server-side routing with MVC. I am using a layout file that points to a main "app" component, like this:
<div aurelia-app="main" start="app" data-model='#Json.Encode( Model )'></div>
Each of my views contain a reference to the layout file and something like this:
<div aurelia-app="main" start="sample-module" data-model='#Json.Encode( Model )'></div>
My main.js is configured like this:
export function configure(aurelia) {
aurelia.use.standardConfiguration()
aurelia.container.registerInstance('viewModel',
Object.assign({}, JSON.parse(aurelia.host.dataset.model)));
aurelia.start().then(a => {
let start = a.host.attributes.start.value;
a.setRoot(start);
});
}
And I am leveraging Aurelia's router (in app.js) like this:
export class App {
constructor() {
}
configureRouter(config, router) {
config.title = 'Aurelia';
config.options.pushState = true;
config.options.root = '/';
config.map([
{ route: ['', 'Aurelia/Home'], name: 'home', moduleId: 'home', title: 'home', nav: false },
{ route: 'Aurelia/SampleModule', name: 'sample module', moduleId: 'sample-module', title: 'sample module', nav: true },
]);
this.router = router;
}
}
This almost works. If I navigate to SampleModule using the Aurelia navigation link, it loads the module but doesn't hit the server - doesn't even hit the View. If I navigate to Aurelia/SampleModule manually, it loads sample-module twice, including the data from the server. The Aurelia router updates the url exactly as I would expect, so if I navigate and then hit refresh it loads from the server correctly.
I want to be able to use the navigation to change the view without refreshing the entire page, but still leverage my server-side routing and hit the Views and Controllers.
I was able to solve my problem by removing the reference to the app from each of the views:
<div aurelia-app="main" start="sample-module" data-model='#Json.Encode( Model )'></div>
as well as removing the start logic from the layout file and main.js, so now my main.js looks something like this:
export function configure(aurelia) {
aurelia.use.standardConfiguration()
aurelia.container.registerInstance('viewModel',
Object.assign({}, JSON.parse(aurelia.host.dataset.model)));
aurelia.start().then(() => aurelia.setRoot());
}
In the back end, the model has a unique property to each page:
public class AppViewModel
{
public HomeViewModel Home { get; set; }
public SampleModuleViewModel SampleModule { get; set; }
}
From the view model (i.e. sample-module.js), it checks to see if the associated property (AppViewModel.SampleModule) is populated, and if not, it makes an ajax call.

Laravel 5.3 Vue 2.0 getting database data to Vue

I am not sure if I am confused, is it true that I should leave the job of getting data from database for controller and processing the data for vue? Or is this the best practice or something?
Here I am trying to get the subcat from my subcategories table, not error on devtool but subcats is not being set. Please help or feel free to suggest a better practice. Thanks!
Item-sorting-list.vue
<template>
<div>
<div v-show="isActive">
<li v-for="subcat in subcats" class="list-group-item">
<a name="" href="">{{subcategory.name}}</a>
</li>
</div>
</div>
</template>
<script>
export default {
props:[
],
data(){
return {
subcats:[]
}
},
mounted() {
this.getAllSubcat()
},
methods: {
getAllSubcat(){
var vm = this;
vm.$http.get('/getSubcat').then((response)=>{
vm.subcats = response.data.data.subcat.data;
});
}
}
}
</script>
SubcategoryController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Subcategory;
class SubcategoryController extends Controller
{
public function getSubcat()
{
$subcat = Subcategory::Orderby('name')->get();
$response = [
'data' => [
'subcat' => $subcat
]
];
return response()->json($response);
}
}
web.php
Route::get('/getSubcat', 'SubcategoryController#getSubcat');
Thanks for #saurabh that I double check and response.data.data.subcat should be the correct calling. paginate does read the data differently.

Aurelia How do I use a Child Router and dynamics child routes

Trying to use a child route in Aurelia. Can't seem to get my head around the workings of nested routes. Are all routes derived from the root of the app or relative to location of the current router?
Why wont my route-href work in this example? I have a route in this router named screen and it does have an :id parameter
screens/list.ts
#inject(Router)
export class ScreensList {
heading;
router;
screens: any[];
constructor(router){
this.heading = 'Child Router';
this.router = router;
this.screens = [
{
id: 1,
name: 'my screen'
},
{
id: 2,
name: 'my other screen'
}
]
router.configure(config => {
config.map([
// dynamic routes need a href, such as href: screen
{ route: 'screen/:id', moduleId: 'screens/screen/display', name: 'screen', title: 'Screen #1' }
]);
});
}
}
List View
screens/list.html
<li repeat.for="screen of screens">
<a route-href="route: 'screen', params: { id: screen.id }"/>Screen #${screen.id}</a>
</li>
I then have a dummy VM/V at screens/screen/display.
Do I really have to specify the full filepath for a module in a nested child router. I thought it would be routes relative to the location of the parent router or at least the name (root) of the parent?
vendor-bundle.js:11582 ERROR [route-href] Error: A route with name ''screen', params: { id: screen.id }' could not be found.
Check that `name: ''screen', params: { id: screen.id }'` was specified in the route's config.
In your example, you are injecting the router, which is the router configured in app.js, and then calling its configure method. Aurelia is Convention-Over-Configuration. So, use the convention and you will be fine. The configureRouter method will do the tricks for you. For instance:
export class ScreensList {
configureRouter(config, router) {
config.map([
{ route: 'screen/:id', moduleId: 'screens/screen/display', name: 'screen', title: 'Screen #1' }
]);
this.router = router;
}
}
Remember that ScreensList must be a screen of your router. It will not work if it is a custom element.
Take a look at the skeleton-navigation examples https://github.com/aurelia/skeleton-navigation. There are good examples, including child routing.

HTTP and nested objects (Angular2 RC1 + TS)

I'm having some trouble displaying my data in the browser. To explain my problem I'm using some dummy code. I have some nested objects that are causing my problem. Here I'll display one nested object to showcase my problem.
First of all, I only make http calls for the Car-object. So saveCar acts like updating the car as well, depending on what the user does in the app. All the methods in the service works as they should.
So my service looks something like this:
#Injectable()
export class Service {
constructor(private http: Http) { }
saveCar(car: Car) {
return this.http.post ....
}
getCars(){
return this.http.get...
}
getById(id: string) {
return this.http.get...
}
}
Then I have a Car-class, where the nested object "Brand" comes in to play, Brand then has it's own class, but I'll leave it out.
export class Car {
private brands: Array<Brand>;
constructor(public id: string, public name: string) {
this.brands = new Array<Brand>();
}
public getBrands(): Array<Brand> {
return this.brands;
}
public addBrand(value: Brand): void {
this.brands.push(value);
}
//some other methods.
}
Then I have a list-component that lists all cars, this works as it should!
#Component({
selector: 'car-list',
template: `
<h1>Add Car</h1>
<form (submit)="saveCar()">
<input required [(ngModel)]="name" placeholder="Add car">
</form>
<br>
<table>
<tr *ngFor="let car of cars" >
<td>{{car.name}}</td>
<td><button (click)="goToDetail(car)">Detail</button></td>
</tr>
</table>
`,
})
export class ListComponent implements OnActivate {
id: string
name: string;
cars: Array<Car>
constructor(public _service: Service, public _router: Router) { }
routerOnActivate(): void {
this._service.getCars()
.subscribe(cars => this.cars = cars);
}
saveCar() {
let car = new Car(this.id, this.name)
this._service.saveCar(Car)
.subscribe(car => this.cars.push(car));
this._service.getCars()//
.subscribe(cars => this.cars = cars);
}
goToDetail(car:Car) {
this._router.navigate(['/cardetail', car.id]);
}
}
The problem I have is in the detail-component, where the user gets navigated after clicking a specific car. The routing and retrieving the Car from the db works as it should. That I know, because if I remove all the template except <h1>Car: {{car?.name}}</h1> the name gets printed out fine with the elvis operator.
But my detail-component looks something like this:
#Component({
selector: 'car-detail',
template: `
<h1>Car: {{car?.name}}</h1>
<hr>
<button (click)="addBrand()">Add Brand</button>
<div *ngFor="let brand of car.getBrands(); let i=index">
<h2>Brand {{i+1}}</h2>
</div>
`,
})
export class DetailComponent implements OnActivate {
#Input() car: Car;
constructor(public _service: Service, public _router: Router) { }
routerOnActivate(curr: RouteSegment): void {
let id = curr.getParam('id');
this._service.getById(id)
.subscribe(car => {
this.car = car;
});
}
addBrand() {
this.car.getBrands().push(new Brand());
}
//some other methods
}
So in my detail component I call all methods like: car.someMethod() and further on the nested Brand object like: brand.someMethod() in the template. So the error comes at the call of the method e.g in the template 'cannot get getBrands of undefined' I've tried putting the elvis operator like this: car?.getBrands() It doesn't work. I've tried to wrap the whole thing in a div, both with elvis operator and a <div *ngIf = "car"></div>, doesn't work. Even tried with <template *ngIf="car"></template>, well that doesn't work either....
Edit: my mess-up, wrapping like below, it does "kind of" work, meaning, it gives a new error....
Template:
#Component({
selector: 'car-detail',
template: `
<h1>Car: {{car?.name}}</h1>
<hr>
<button (click)="addBrand()">Add Brand</button>
<div *ngIf="car">
<div *ngFor="let brand of car.getBrands(); let i=index">
<h2>Brand {{i+1}}</h2>
</div>
</div>
You mention <h1>Car: {{car?.name}}</h1> with ? but the full code example has <td>{{car.name}}</td> without ? which will cause an error.
<div *ngFor="let brand of car.getBrands(); let i=index">
also needs a ? to avoid errors when Angular tries to render the view and car is not yet set
<div *ngFor="let brand of car?.getBrands(); let i=index">

Resources