Angular: pass complex JSON via routerLink - json

I'm using Angular 5 and I have this ngFor:
<div class="row margin-v-20">
<my-tile [routerLink]="['/secondPage', item.id, 'item-list']" *ngFor="let item of listaOfItem" [item]="item"></my-tile>
</div>
and I need to access from the routed component "secondPage/:id/item-list" to the json object "item" of the list.
How can I achieve this?

This article explains several ways to achieve this. I recommend to use a service to share the complex data between components ("Using Application Providers" section in the article).

Related

Pass data from nested Components Angular 2

I have a component where with a *ngFor print some boxes defined in an other component like this:
<div>
<commponentB *ngFor="let a of list"></componentB>
</div>
where "list" is a list ok object like this:
this.list=[{"id":"3","nome":"app3","posizione":3},{"id":"1","nome":"app1","posizione":1},{"id":"2","nome":"app2","posizione":2}];
I have to populate all the component. How can I transfer data from this two component?
TY
EDIT1:
the problem is that the list is splitted into 2 list for the repeat in 2 different bootstrap columns so the situation is this:
<div>
<commponentB *ngFor="let a of list1"></componentB>
</div>
<div>
<commponentB *ngFor="let a of list2"></componentB>
</div>
and the component is like that:
<div>
<span>{{name}}</span>
</div>
if I pass all the list I can't know how to populate the component at the right position (sorry if I don't explain the problem very well)
Since your components are directly linked, you can use the #Input() component interaction to send data from the parent to the child.
Here is a link from the official documentation :
https://angular.io/guide/component-interaction#pass-data-from-parent-to-child-with-input-binding
If you can't do that model because of a router interaction, or components are too "far" away so it's too much work to setup several inputs, you can use a shared service to share the data from one to another component.
https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service
if you want to pass your list to componentB you can define Input() property in componentB and then pass your list
<div>
<commponentB [list]="list"></componentB>
</div>
export class ComponentB{
#Input() list: any[];
}
Update Ok maybe I'm not getting your issue, even if you have to pass different inputs to same component you can do that. Check out this plunker:

Angular4 ng-content gets built when ngIf is false

I have a problem with the new ng-content transclusion.
Let's say I have a component my-component that, in its ngOnInit() function does some heavy operation on load (for now, just a console.log()).
I have a wrapper, that displays the content via transclusion (my-wrapper.component.html).
<ng-content></ng-content>
If I set the surroundings up like this, the log statement doesn't show:
<my-wrapper *ngIf="false">
<my-component></my-component>
</my-wrapper>
I assume, the my-wrapper component does not get built, so the content is ignored.
But if I try to move the logic into the my-wrapper component like this (my-wrapper.component.html):
<ng-container *ngIf="false">
<ng-content></ng-content>
</ng-container>
I always see the console.log() output. I guess, the my-component gets built and then stored away until the *ngIf becomes true inside my-wrapper.
The intention was to build a generic "list-item + detail" component. Say I have a list of N overview-elements (my-wrapper), that get rendered in a *ngFor loop. Every of those elements has its own detail component (my-component) that is supposed to load its own data, once I decide to show more infos to a specific item.
overview.html:
<ng-container *ngFor="let item of items">
<my-wrapper>
<my-component id="item.id"></my-component>
</my-wrapper>
</ng-container>
my-wrapper.component.html:
<div (click)="toggleDetail()">Click for more</div>
<div *ngIf="showDetail">
<ng-content></ng-content>
</div>
Is there a way to tell Angular, to ignore the transcluded content until it is necessary to be added to the page? Analogously to how it was in AngularJS.
Based on the comment of #nsinreal I found an answer. I find it to be a bit abstruse, so I'm trying to post it here:
The answer is to work with ng-template and *ngTemplateOutlet.
In the my-wrapper component, set up the template like this (my-wrapper.component.html):
<div (click)="toggleDetail()">Click for more</div>
<div *ngIf="showDetail" [hidden]="!isInitialized">
<ng-container *ngTemplateOutlet="detailRef"></ng-container>
</div>
Note, that the [hidden] there is not really necessary, it hides the "raw" template of the child until it decides it is done loading. Just make sure, not to put it in a *ngIf, otherwise the *ngTemplateOutlet will never get triggered, leading to nothing happening at all.
To set the detailRef, put this in the component code (my-wrapper.component.ts):
import { ContentChild, TemplateRef } from '#angular/core';
#Component({ ... })
export class MyWrapperComponent {
#ContentChild(TemplateRef) detailRef;
...
}
Now, you can use the wrapper like this:
<my-wrapper>
<ng-template>
<my-component></my-component>
</ng-template>
</my-wrapper>
I am not sure, why it needs such complicated "workarounds", when it used to be so easy to do this in AngularJS.
By doing this:
<my-wrapper *ngIf="false">
<my-component></my-component>
</my-wrapper>
You are not calling MyComponent component, because the *ngIf is false. that means, that not calling it you are not instancing it and, therefore, not passing through its ngOnInit. And that's why you are not getting the console log.
By doing this:
<ng-container *ngIf="false">
<ng-content></ng-content>
</ng-container>
You are inside the component, you are just limiting what to render in your template, but you already instanced your component and, therefore, you passed through your ngOnInit and you get your console log done.
If, you want to limit something (component call with selector or a ng-content or even a div) until you have some data available, you can do the following:
datasLoaded: Promise<boolean>;
this.getData().subscribe(
(data) => {
this.datasLoaded = Promise.resolve(true); // Setting the Promise as resolved after I have the needed data
}
);
And in your template:
<ng-container *ngIf="datasLoaded | async">
// stuff here
</ng-container>
Or:
<my-component *ngIf="datasLoaded | async">
// Didn't test this one, but should follow the same logic. If it doesn't, wrap it and add the ngIf to the wrapper
</my-component>
It’s because Ng content happens at the build time and when you pass the content it is actually not removed or recreated with the ngIf directive. It is only moved and the component is instantiated .
I encountered this problem recently as well but settled on a different solution than the currently accepted one.
Solution (TL;DR)
(Solution is for AngularDart; I figure it's similar in Angular though)
Use a structural directive; tutorials linked below.
Instead of:
<my-wrapper>
<my-contents></my-contents>
</my-wrapper>
your usage becomes:
<div *myWrapper>
<my-contents></my-contents>
</div>
which is shorthand for the following (in AngularDart; I think Angular uses <ng-template>)
<template myWrapper>
<div>
<my-contents></my-contents>
</div>
</template>
The MyWrapper directive logic is similar to NgIf except it has its own logic to compute the condition. Both of the following tutorials explain how to create an NgIf-like directive and how to pass it your own inputs using the special microsyntax (e.g. *myWrapper="myInput: expression"). Note that the microsyntax doesn't support outputs (#Output), but you can mimic an output by using an input that is a function.
Tutorial for Angular
Tutorial for AngularDart
Caveat: Since this is just a directive, it shouldn't do anything more complicated than instantiating a template ref at the appropriate time and maybe specifying some DI providers. For example, I would avoid trying to apply styles or instantiating a complex tree of components in the directive. If I wanted to create a list component, I would probably take the #ContentChild(TemplateRef) approach described in another answer; you would lose the asterisk shorthand for creating <template> but you would gain the full power of components.
My problem
My team owns an app that's part of a larger web application with other apps owned by other teams. Our components assume they can inject a MyAppConfiguration object, but this object can only be injected after it is loaded with an asynchronous request. In our app this is not a problem: we have a "shell" component that hides everything behind an ngIf until the configuration is loaded.
The problem is when other teams want to reference our components. We don't want them to duplicate the "wait until configuration is loaded" logic every time, so I tried creating a wrapper component that can be used like so:
<my-app-wrapper>
<my-app-component></my-app-component>
</my-app-wrapper>
The wrapper injects a service object and hides its contents behind an ngIf until the service says that the configuration is loaded.
Like the question poster, I discovered that the ng-content approach doesn't work as intended: while the contents are correctly hidden from the DOM, Angular still instantiates the components causing dependency injection to fail.
The solution that I settled on was to rewrite the wrapper component as a structural directive.

How to dynamically display part of webpage using angularJs

I have a angular js application, in which I am retrieving http response data using rest services. I want to conditionally display the part of the page. For eg: if the response data in angular is "manager" , the page should have different options displayed and if the response data is "employee" , same part of webpage should display different options.
How do I do that?
This is single page application.
For your requirement you can use ng-switch directive. The ngSwitch directive is used to conditionally swap DOM structure on your template based on a scope expression.
For your example:
<div ng-switch="expression">
<div ng-switch-when="manager">...</div>
<div ng-switch-when="employee">...</div>
</div>
Here expression will contain info you get from response data of either manager or employee.
Hope this will be helpful to you!
You can use ng-show directive like:
<div>
<div ng-show="employeeType == 'Manager'"> </div>
<div ng-show="employeeType == 'employee'"> </div>
<div>
For more information refer to this link: https://docs.angularjs.org/api/ng/directive/ngShow

Cannot generate correct HTML from directive tree

I have a directive structure that I'm using to simplify the user interface. The implementation of the directives should handle the complexity. The directive structure is as follows:
<re-widget id="widget_html_content">
<re-list name="HtmlContent" query="all">
<re-items each="item">
<div>
<span> {{item.content}} </span>
</div>
</re-items>
</re-list>
</re-widget>
The above should generate HTML of the following flavor:
<div id="widget_html_content">
<div ng-controller="ListCtrl" name="HtmlContent" query="{}" class="HtmlContent">
<div ngRepeat="item in ListCtrl.items">
<div item="item" ng-controller="ItemCtrl">
<div>
<span> {{item.getContent()}} </span>
</div>
</div>
</div>
</div>
</div>
This is basically a transformation task (transform the directive HTML to the standard HTML) with some caveats. The following are the requirements:
(1) the re-widget creates isolated scopes since it can be reusable in the page, possibly with the same data input
(2) The re-list tag transforms itself to use a ListCtrl, which the ListCtrl knows how to get the list data by using the input NAME and QUERY attributes/parameters
(3) the re-items tag displays each item in the list by using its inner template defined by the user. So basically it is an ng-repeat over the list items. However, each item ng-repeated will have its own ItemCtrl controller instance. The ItemCtrl instance would get a reference to the item model and data so that it can expose an API for the item data. Note: the item model is the value of the each attribute in the re-items tag.
I have a plunkr with most of the code at http://plnkr.co/edit/Ja0uDb630RYsSjVOGNRb?p=preview
Any help is greatly appreciated!

Multiple API calls inside a Polymer Custom Element

I have put together a custom element using Polymer - <x-flickr> (http://tamas.io/x-flickr-custom-polymer-element/ || https://github.com/tamaspiros/x-flickr) - it essentially makes a REST call to the Flickr API and returns photos based on a search:
<polymer-jsonp id="ajax" auto url="http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key={{apikey}}&tags={{tag}}&per_page={{amount}}&page=1&format=json&jsoncallback="></polymer-jsonp>
This call returns some information about a particular photo but not everything. I'd like to reuse the unique ID returned via this call and make a second REST call to get further detail about the image. At the moment I display the photos via:
<template id="photos" repeat="{{photo in photos}}">
<div class="thumbnail">
<img src="http://farm{{photo.farm}}.staticflickr.com/{{photo.server}}/{{photo.id}}_{{photo.secret}}.jpg" class="img-thumbnail">
</div>
</template>
but I'd like to extend that so that I get the description of the photo as well (and this is the information that is not being returned by the first REST call displayed above) - so my template would look something like this:
<template id="photos" repeat="{{photo in photos}}">
<div class="thumbnail">
<img src="http://farm{{photo.farm}}.staticflickr.com/{{photo.server}}/{{photo.id}}_{{photo.secret}}.jpg" class="img-thumbnail">
<p>{{photo.description}}</p> <!-- this should come from the 2nd API call -->
</div>
</template>
What's the best way of achieving this?
I forked your project so you can see the details of what I'm about to describe here https://github.com/sjmiles/x-flickr.
The short answer to your question is to include a request element (polymer-ajax) inside the template repeat. That causes a request to spawn for each item in the repeat, and gives you easy access to the item data (the photo id in this case).
As I mentioned, you can see an example of how this can be done in the source here: https://github.com/sjmiles/x-flickr/blob/master/x-flickr.html#L46.
A few other notes:
You don't need JSONP for these requests, simple Ajax calls will do, you can access the JSON directly. I used polymer-ajax instead of polymer-jsonp.
You almost never need addEventListener in Polymer because you can listen to events using on-<eventName>="methodName" syntax directly in the HTML. In this case, you don't need to use events at all, because you can do the work with data binding.
This could be done completely script-free, but I kept your script for setting photos property and sending the x-flickr-load event.
HTH
One way to tackle this: http://jsbin.com/yihovepi/1/edit (also includes some refactoring)
The <p>{{photo.description}}</p> is always in the template, but the .description is filled later by dynamically creating <polymer-jsonp> for each photo.
Note, I'm also using binding to the main polymer-jsonp element's response property:
<polymer-jsonp id="ajax" response="{{response}}">
This is super changed because the corresponding responseChanged() callback is called when that value is populated from the request.