I want to fetch all data from remote service at once before routing to Profile.
I find typescript version angular have Resolve to help to fetch data before navigating. But I cannot find any example in Dart version ( angular2: ^3.0.0).
#RouteConfig(const [
const Route(path: '/', name: 'Home', component: HomeComponent, useAsDefault: true]),
const Route(path: '/profile', name: 'Profile', component: ProfileComponent),])
class AppComponent implements OnInit{
}
Anyone know how to handle it by using Dart?
The current router in Angular.dart doesn't have Resolve.
Depending on what exactly you try to accomplish, there might be different workarounds.
Usually just wrapping the content of the components template with
<template [ngIf]="data != null"> ... </template>
should do, and then assign to data when it's available and the component will be shown.
Another more generic way might be to create a custom MyRouterOutlet that might extend RouterOutlet and customize it so that it delays adding the component until an event was received.
Add to the RouterOutlet
#HostBinding()
dataArrived() {
// call the code that actually adds the component
}
then use it like
<router-outlet fetchDataDirective></router-outlet>
and a directive like
#Directive(selector: '[fetchDataDirective]')
class FetchDataDirective implements OnInit {
final DataService dataService;
FetchDataDirective(this.dataService);
#Output()
get dataArrived => _dataArrived.stream;
final _dataArrived = new StreamController();
ngOnInit() async {
await dataService.fetchData();
}
}
that emits the event the RouterOutlet waits for.
It was also mentioned that a new router for Angular.dart is work in progress (might still take a while though).
Related
I'm hoping there's no such thing as a dumb question in this forum.
I've seen the ngx-extended-pdf-viewer demo at https://www.pdfviewer.net/extended-pdf-viewer/multiple-documents which loads a new pdf when selected from a list on that demo page, but I'm wondering how I could pass the source variable to the pdf-viewer via the URL Like:
www.myviewer/index.html?source=pdfNAME
(index.html is the 'starter' version I built according to https://pspdfkit.com/blog/2021/how-to-build-an-angular-pdf-viewer-with-pdfjs/)
and then have that variable used to select the source location from an array. Such as:
var pdfSource = [
{
selector: 'app-root_Selector',
templateUrl: './app.component.html',
styleUrls: ['./app.component_A.css'],
name:'myNewPdf'},
}]
(( and where would I put that array?)
Are you familiar with Angular routing? Here's a very short run-down:
You need to define a route with a variable:
{ path: ':path', component: ExampleComponent },
Now your component can query the variable part of the URL:
export class ExampleComponent implements OnInit {
public pdfFile!: string;
constructor(private router: Router, private currentRoute: ActivatedRoute) { }
ngOnInit(): void {
this.currentRoute.paramMap.subscribe(route => {
this.pdfFile = route.get("path");
});
}
When you call your application with an URL like https://example.com/example.pdf, the URL snippet example.pdf is copied to the variable pdfFile, and you can use is for the [src] attribute:
<ngx-extended-pdf-viewer [src]="pdfFile">
I suppose that was a pretty fast description, but now you're better equipped to understand the official and lengthy documentation.
Side remark: The instructions on https://pspdfkit.com/blog/2021/how-to-build-an-angular-pdf-viewer-with-pdfjs/ are good, but setting up ngx-extended-pdf-viewer has become even simpler in the meantime (see the instructions on the showcase.
How can I set initial state of Angular 2 app from view?
I have a controller that must pass initial state throw the view to angular 2 component.
Currently you can't pass data to the root level component via a property, but you can however define a global variable outside the component and refer to it in your component. This might not be ideal, but perhaps not too bad either.
jquery is an example of this. In the below example the global jquery variable is referenced from my component. You could do the same with your own global variable. You could use server side rendering to dynamically create your global variable when the page renders.
import {Component, ElementRef, Inject, OnInit} from 'angular2/core';
declare var jQuery:any;
#Component({
selector: 'jquery-integration',
templateUrl: './components/jquery-integration/jquery-integration.html'
})
export class JqueryIntegration implements OnInit {
elementRef: ElementRef;
constructor(#Inject(ElementRef) elementRef: ElementRef) {
this.elementRef = elementRef;
}
ngOnInit() {
jQuery(this.elementRef.nativeElement).find('.moving-box').draggable({containment:'#draggable-parent'});
}
}
Why don't you hydrate the initial state of the application by calling a WebApi endpoint in ngOnInit()? That way your application is relying on a standard HTTP response rather than a piece of MVC infrastructure...
I'm messing around with Angular2 and I'm wanting the ability to inject one component into another based on the bootstrapped bindings.
class HelloComponent {
name: string;
}
#Component({
selector: 'hello'
}
#View({
template: `<h3>Hello {{ name }}</h3>`
})
class HelloBobComponent extends HelloComponent {
constructor() {
this.name = 'Bob';
}
}
#Component({
selector: 'app'
}
#View({
directives: [HelloComponent]
template: `<h1>Welcome to my Angular2 app</h1>
<hello></hello>`
}
class AppComponent {
}
bootstrap(AppComponent, [
bind(HelloComponent).toClass(HelloBobComponent)
]);
Here I'm using HelloComponent as a token that I want Angular2's Injector to resolve HelloBobComponent. I'm doing this so that I can swap components in and out based on the current app configuration. The above example obviously doesn't work. Is this possible using one of the frameworks decorators? I haven't found an answer yet digging though blogs or the source.
edit: To clarify, how do I get the directives property on the View decorator to treat HelloComponent as a di token instead of a type.
This is currently not supported as of alpha37. The compiler resolves directives passed in the View decorator by either type or binding but does not look up from the parent injector.
For example:
#View({
url: '...',
directives: [
Directive1,
bind(Directive2).toClass(Directive2Impl),
]
})
The intention for the "directives" property here was only to prevent selector naming collision. Later bind support was added to aid in testing.
The only solution I can think of without editing the compiler function would be to maintain an external Injector and resolve types on component declaration.
I want to wait until my component is fully loaded. The current approach would be to implement the ShadowRootAware interface. However this does not work, if the component disables the use of shadow dom:
#Component(
selector: 'travel-step',
templateUrl: 'packages/TravelPlanner/travelstep/travel_step_component.html',
useShadowDom: false,
publishAs: 'cmp')
class TravelStepComponent extends AttachAware{
I need to disable the usage of ShadowDom, because I want to use styles from my parent object (e.g. Bootstrap). Is there another way to wait for the dom to be ready?
I want to reference a file upload input. At the moment (angular v.012) there seems to be no other way to upload a file.
You can implement ShadowRootAware interface. For example:
class NgFreeTree implements ShadowRootAware {
void onShadowRoot(ShadowRoot shadowRoot) { ... }
}
It should work regardless of useShadowDom attribute.
It does not give you the error message if you use the following signature:
void onShadowRoot(Node n) {
HtmlElement element = n;
...
}
I've tried to bind a custom event handler to a WebComponent that has an EventStreamProvider exposed via a getter, but it comes back with "Class 'DivElement' has no instance getter 'onMainAction'.".
Trimmed down component .dart code...
import 'dart:html';
import 'dart:async';
import 'package:web_ui/web_ui.dart';
class SegmentedButtonsListComponent extends WebComponent {
static const EventStreamProvider<CustomEvent> mainActionEvent = const EventStreamProvider<CustomEvent>("MainActionEvent");
Stream<CustomEvent> get onMainAction => mainActionEvent.forTarget(this);
}
Trimmed usage of component…
<x-segmented-buttons-list id="segmented-buttons-list" on-main-action="eventHandler($event)"></x-segmented-buttons-list>
Trimmed code from main.dart…
import 'dart:html';
import 'dart:async';
import 'package:web_ui/web_ui.dart';
const EventStreamProvider<CustomEvent> mainActionEvent = const EventStreamProvider<CustomEvent>("MainActionEvent");
void eventHandler(CustomEvent event) {
print("""
Yabba Dabba Doo!
Type: ${event.type}
Detail: ${event.detail}
""");
}
void main() {
mainActionEvent.forTarget(query('#segmented-buttons-list')).listen(eventHandler);
}
The "MainActionEvent" custom events are being dispatched by components instantiated within this "list" component.
As you can see from the above example I can catch the events if I create an EventStreamProvider in main.dart and target the component, that works fine (but by-passes the Stream getter in the component).
It would be great though if I could dispense with the EventStreamProvider in main.dart and simply bind to the onMainEvent getter on the component.
Is that possible?
Update 2013-05-05:
Siggi explains below that at present it is not possible to do this, but there is a way to reference the component's CustomEventProvider's getter via the element's xtag.
I found that I had to use a Timer to query the DOM after main() has completed because xtags aren't populated until the main() event loop has finished.
void postMainSetup() {
query('#segmented-buttons-list').xtag.onMainAction.listen(eventHandler);
}
void main() {
Timer.run(postMainSetup);
}
With the above setup a new CustomEventProvider isn't needed to monitor the component.
Good question!
I see a couple parts to this question:
using custom events directly on a component: Currently web_ui uses different objects to represent your component and the actual dom element it represents. In the future, we plan to extend directly from "DivElement" instead of "WebComponent" and that will allow you to do what you wrote.
Meanwhile, you'll have to be more explicit when you want to use the host or shadow root of your component. In your example, it seems like you want to attach the event to the host, so you would need to write something more like this:
Stream<CustomEvent> get onMainAction => mainActionEvent.forTarget(this.host);
using 'on-someting-foo' syntax in a component: you probably found a bug/missing feature =). Currently we treat attributes in a special way and bind their values to fields of a component if we identify that the target was corresponds to a component. We do this for value bindings, but not yet for binding custom events. A workaround before this feature is added, would be to query for your element and attach the event by hand:
query('#host-node').xtag.onMainAction.listen(...);