Binding and Displaying Data With Aurelia - aurelia

I am new to Aurelia. I have a WebApi that will return some data that I would like to populate into my exported model and then display the info on the screen.I'm thinking it would go into my run event but am not sure. Can anybody tell me how to do this . Any information would be most appreciated. My code is below.
--Jason
import 'fetch';
import {HttpClient, json} from 'aurelia-fetch-client';
import {inject} from 'aurelia-dependency-injection';
declare var window: { wcApiUrl: string, wcAmtInstanceId: string };
#inject(HttpClient)
export class BureauModUpdate {
files: string;
constructor(private http: HttpClient) {
http.configure(x => {
x.defaults.headers = { 'Authorization': 'Basic ' + window.wcAmtInstanceId }
});
}
public run(): void {
//Would I put it here ??
}
upload(): void {
var form = new FormData()
for (var i = 0; i <= this.files.length; i++) {
form.append('file', this.files[i])
this.http.fetch(window.wcApiUrl + '/Lookup/BureauModUpdate/CreateBureauModUpdates', {
method: 'post',
body: form
})
}
}
}
export class BureauModUpdateHistory {
public IndexId: number;
public UploadID:number;
public EmployeeNum: number;
public filename: string;
public Bureau: string;
public UploadedDate: Date;
public UploadedStatus: string;
public ErrorInfo: string;
public RecordCount: number;
}

To make something happen when the page loads, use the attached() Aurelia component lifecycle method, like this:
attached() {
// do something here
}
For more information on the Component Lifecycle, see the documentation on Aurelia's DocHub.
Example using Fetch to get data:
For getting HTTP data using fetch (or any other web service), you need to use an async call (using .then to chain the next event). For example:
this.http.fetch(url).then(response => {
this.data = response;
}
Then, just bind your data to this.data (depending on what type of data you're getting). For example:
<template>
Hello, ${data.fname}!
</template>

Related

How to declare model class in react native and pass whole model class in second screen by use of react navigation

Is proper way to declare model class like java and how to pass this into second screen in react native?
export default class UserModel {
stateName;
username;
email;
mobile;
gender;
address;
constructor() {}
setStateName(stateName) {
this.stateName = stateName;
}
setUserName(username) {
this.username = username;
}
setEmail(email) {
this.email = email;
}
setMobile(mobile) {
this.mobile = mobile;
}
setGender(gender) {
this.gender = gender;
}
setAddress(address) {
this.address = address;
}
}
Step 1: Make UserModel.js
class UserModel {
constructor() {
stateName,
username,
email,
mobile,
gender,
address;
}
}
Note: Do not Export it if you don't want to set globally.
Step 2 : Screen1.js - Set UserModel and pass from screen1.
_handlePress = async () => {
UserModel.username = "Vishal Patoliya"
this.props.navigation.navigate('UserList',{userData: UserModel});
}
Step 3 : Receiving model class at another screen.
render() {
console.log(TAG, "render() Called.")
const UserModel = this.props.navigation.getParam('userData');
console.log("Username", UserModel.username)
}
OutPut :
01-16 17:30:32.085 4541 5638 I ReactNativeJS: 'Username', 'Vishal Patoliya'
Edit:
After some discussion, this was required answer:
this.props.navigation.navigate('UserList', { userModel: userModel });
this.props.getParam('userModel', /* optional default value */);
I assume this is your UserModel.js.
Now you are able to import the model like other components:
import UserModel from './location/UserModel';
But if you do it like this, you'd have to instanciate UserModel every time you import it.
If you'd like to prevent this, just instanciate a UserModel and export it inside the UserModel.js and import the instance anywhere.
Like this:
class UserModel {
//...
}
export default new UserModel();
other way might be:
export class UserModel {
//...
}
const GlobalUserModel = new UserModel();
export default GlobalUserModel;
to choose in other files what to import:
import { UserModel } from './location/UserModel'; //get new instance
or
import GlobalUserModel from './location/UserModel'; //get global instance
If imported via { UserModel }, you have to instanciate first: new UserModel()
...or vice versa.

Angular 5 BehaviorSubject data not update UI

I use to change dynamic child component in body and keep static header, bottom and menu.
My problem: When use BehaviorSubject as shared-data between components, then UI (*ngFor) not be updated event shared-data transferred well. I am using Angular 5.2.0, RxJs 5.5.6
My app has flow:
user click search button on Layout-top.component.ts -> fetch data from Backend server by Home.service.ts-> set data in BehaviorSubject object.
On Home.component.ts constructor always subscribe shared-data from Home.service.ts -> change data of Home.component.ts -> display them.
1. App.compoenet.ts
#Component({
selector: 'xxx',
template:
`
<gotop position="200"></gotop>
<layout-top></layout-top>
<router-outlet></router-outlet>
<layout-bottom></layout-bottom>
`
})
export class AppbComponent implements OnInit, AfterViewInit{
public ngAfterViewInit(): void {
this.spinner.hide();
}
message:string;
constructor(private spinner:Spinner){
}
public ngOnInit(){
this.spinner.show();
}
}
Layout-top.component.ts
public doSearch(){
let filter = {
xx:'XXX'
};
this.homeService.setData(filter);
}
3.Home.service.ts
#Injectable()
export class HomeService extends BaseService{
public data =new BehaviorSubject<DataType>(<DataType>{});
public eventFilter: EventEmitter<{}> = new EventEmitter();
public constructor(private http: HttpClient,
private _const: Const,
private util:Util,
private appref: ApplicationRef) {
super(_const, util);
}
public listProduct(filter):Observable<any>{
const url = url to my backend api
let headers:HttpHeaders = this.util.header(this._const, null, 'application/json');
return this.http.post(
url,
filter,
{headers})
.map(res => {
return res;
});
}
public getData():Observable<DataType>{
return this.data.asObservable();
}
public setData(filter:any):void {
const listProduct$ = this.listProduct(filter);
listProduct$.subscribe(res => {
this.data.next({res:res, filter:filter});
});
}
public cleanData() {
this.data.next(null);
}
}
layout-top.html
5.home.html
<div class="product-item"
*ngFor="let item of listProducts">
<!--display some thing here-->
</div>
6.home.component.ts
constructor(private service: HomeService,
private cdRef:ChangeDetectorRef,
private zone:NgZone,private appref: ApplicationRef ){
this.subsListProduct = this.service.getData().subscribe(obj=>{
this.zone.run(()=>{
$("#in-blur").css("display", "block");
if(!obj){
return;
}
const res = obj.res;
const filter = obj.filter;
if(res && filter){
this.listProducts = res.list;
this.cdRef.detectChanges();
}
});
setTimeout(()=>{
$("#in-blur").css("display", "none");
}, 1000);//for test loading spinner. will be remove in product
});
}
"this.listProducts = res.list;" work fine, ther listProducts be updated, but UI is not any change.
Many people advised use zone.run() or ChangeDetectorRef.detectChanges() but not work in my app. Plz support me.
The best way to map observable data to views is to use the async pipe - https://angular.io/api/common/AsyncPipe - this way all the change management and unsubscribing form the observable is handled for you. So in your example:
The view:
<div class="product-item"
*ngFor="let item of (listProducts | async)?.list">
<!--display some thing here-->
</div>
The ? before .list makes the property optional, so nothing will break if list is is null or undefined
The home component:
constructor(private service: HomeService,
private appref: ApplicationRef ){
this.listProducts = this.service.getData();
}
If you need to manipulate any of the data from the observable before displaying in the view do that in a .map, eg.
constructor(private service: HomeService,
private appref: ApplicationRef ){
this.subsListProduct = this.service.getData()
.map(obj => {
if (obj.list && obj.filter) {
return obj;
} else {
return null;
}
});
}
You should also consider dropping jquery for doing your css changes and use ngClass in your view to do this instead - https://angular.io/api/common/NgClass

Aurelia: Declaring custom element at top-level and access across application

I have a custom element called loading-bar which is used in a number of pages in my app. It's purpose is to show a status message while loading, download of content, and give response messages for backend actions.
loading-bar.html:
<template show.bind="visible">
<i class="fa fa-circle-o-notch fa-spin fa-fw" if.bind="type==1"></i>
<i class="fa fa-check fa-fw" if.bind="type==2"></i>
<span id="loading-text">${message}</span>
</template>
loading-bar.ts:
import { customElement, bindable, bindingMode, inject } from 'aurelia-framework';
#customElement('loading-bar')
export class LoadingBarCustomElement {
private visible = false;
#bindable({ defaultBindingMode: bindingMode.twoWay }) message;
#bindable({ defaultBindingMode: bindingMode.twoWay }) type = 1;
constructor() {
this.visible = false;
}
messageChanged(newValue, oldValue) {
if (newValue && newValue !== '')
this.visible = true;
else
this.visible = false;
}
}
At the moment all pages using the loading bar have this element declared in its html:
<loading-bar message.bind="loadMsg" type.bind="loadType"></loading-bar>
The loading-Bar is controlled by changing the loadMsg and loadType local variables in every page.
What I would like to do is to declare the loading-bar html at just on place, and be able (from any page) to call a method like "showBar(msg, type)" that will affect the globally declared loading-bar.
My first though is to declare the loading-bar in app.html, (just like my nav-bar is declared here) and inject a class (in all the pages' ViewModel), which contain the showBar(msg, type) method that will control the loading-bar.
I am not sure if this is the correct way forward, or how it's best implemented, and would appreciate some help.
You can use the EventAggregator to enable what you want to do.
loading-bar.ts
import { customElement, bindable, bindingMode, autoinject } from 'aurelia-framework';
import { EventAggregator, Subscription } from 'aurelia-event-aggregator';
#customElement('loading-bar')
#autoinject()
export class LoadingBarCustomElement {
private visible = false;
private sub1: Subscription;
private sub2: Subscription;
#bindable({ defaultBindingMode: bindingMode.twoWay }) message;
#bindable({ defaultBindingMode: bindingMode.twoWay }) type = 1;
constructor(private ea : EventAggregator ) {
this.ea = ea;
this.visible = false;
}
attached() {
this.sub1 = this.ea.subscribe('show-loading', ({ message, type }) => {
this.type = type;
this.message = message;
});
this.sub2 = this.ea.subscribe('hide-loading', () => {
this.message = '';
});
}
detached() {
this.sub1.dispose();
this.sub2.dispose();
}
messageChanged(newValue, oldValue) {
if (newValue && newValue !== '')
this.visible = true;
else
this.visible = false;
}
}
and then publish the events elsewhere in your app like this:
import {autoinject} from 'aurelia-framework';
import {EventAggregator} from 'aurelia-event-aggregator';
#autoinject()
export class ExamplePage {
constructor(private ea: EventAggregator){
...
}
async methodUsingTheLoadingBar(){
...
this.ea.publish( 'show-loading, { message: 'Loading...', type: 1 });
const foo = await getData();
...
...
this.ea.publish('hide-loading');
}
}
Decided to solve it like this:
<loading-bar message.bind="lbc.loadMsg" type.bind="lbc.loadType"></loading-bar>
was added to app.html
Created a controller class which will be used as a singleton:
export class LoadingBarController {
private loadMsg = '';
private loadType = 1;
public showBar(message, type) {
this.loadType = type;
this.loadMsg = message;
}
public hideBar() {
this.loadMsg = '';
}
}
This is injected in app.ts (with alias "lbc") where it actually is connected to the loading-bar element, as well injected in every viewModel of pages that wants to use the loading bar.
The loading-bar is then controlled through the injected controller like this:
#inject(LoadingBarController)
export class ExamplePage {
constructor(private lbc: LoadingBarController){
...
}
methodUsingTheLoadingBar(){
...
this.lbc.ShowBar('Loading...', 1);
...
...
this.lbc.HideBar();
}
}

Aurelia Custom element with dialog

I am having trouble with creating a custom element that will be used like
<shimmy-dialog type="video" href="/test">Hi</shimmy-dialog>
The custim element will replace this code with a href that when clicked should popup a dialog of a particular type.
Everything seems to work up until the point I try to open the dialog.
This is when I get the error
Unhandled rejection TypeError: Cannot set property 'bindingContext' of null
I do sometimes find the Aurelia errors a little cyptic.
I suspect it has something todo with the element not having a view.
The code is as follows
enum DialogType {
video = 1,
iframe
};
#inject(Bcp, DialogController)
export class ShimmyDialogModel {
private type : DialogType;
constructor(private bcp: Bcp, private controller : DialogController){
console.log("here");
}
async activate(state){
this.type = state['type'];
}
get isVideo() : boolean {
return this.type == DialogType.video;
}
get isIframe() : boolean {
return this.type == DialogType.iframe;
}
}
#noView
#processContent(false)
#customElement('shimmy-dialog')
#inject(Element, App, Bcp, DialogService)
export class ShimmyDialog {
#bindable public type : string;
#bindable public href;
#bindable public name;
private originalContent : string;
constructor(private element: Element, private app: App, private bcp: Bcp,
private dialogService: DialogService) {
this.originalContent = this.element.innerHTML;
}
bind() {
this.element.innerHTML = '' + this.originalContent + '';
}
attached() {
let self = this;
this.type = this.element.getAttribute("type");
let dialogType = DialogType[this.type];
this.element.children[0].addEventListener("click", function(){
if(dialogType == DialogType.iframe) {
self.dialogService.open({ viewModel: ShimmyDialogModel, model: {'type' : dialogType}}).then(response => {
});
}
else if(dialogType == DialogType.video) {
self.dialogService.open({ viewModel: ShimmyDialogModel, model: {'type' : dialogType}}).then(response => {
});
}
return false;
});
}
async typeChanged(newValue) {
this.type = newValue;
}
async hrefChanged(newValue) {
this.href = newValue;
}
}
The template for the dialog is below.
<template>
<require from="materialize-css/bin/materialize.css"></require>
<ai-dialog>
<ai-dialog-header>
</ai-dialog-header>
<ai-dialog-body>
<div if.bind="isVideo">
Video
</div>
<div if.bind="isIframe">
IFrame
</div>
</ai-dialog-body>
<ai-dialog-footer>
<button click.trigger="controller.cancel()">Close</button>
</ai-dialog-footer>
</ai-dialog>
</template>
Thanks for any help.
I solved this by seperating the classes into their own files.
Aurelia did no like having two export classes there.

can not read get property of undefined angular 2 error

hi I am trying to get city name from google api but getting that error below is my code
appcomponent class
import {Component, OnInit} from 'angular2/core';
import {marketComponent} from './market.component';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {introComponent} from './intro.component';
import {geoService} from './service.geo';
import {JSONP_PROVIDERS} from 'angular2/http';
declare var google: any;
#Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
directives: [ROUTER_DIRECTIVES],
providers: [JSONP_PROVIDERS, geoService]
})
#RouteConfig([
{ path: '/intro', name: 'Intro', component: introComponent, useAsDefault: true },
{ path: '/market', name: 'Market', component: marketComponent },
])
export class AppComponent {
constructor(private _http: geoService) { }
public maps;
public cat_error: Boolean = false;
public xml_Latitude :string;
public xml_Lang: string;
ngOnInit() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(this.showPosition);
} else {
alert("Geolocation is not supported by this browser.");
}
var input: any = document.getElementById('google_places_ac');
var autocomplete = new google.maps.places.Autocomplete(input, {});
google.maps.event.addListener(autocomplete, 'place_changed', function () {
var place = autocomplete.getPlace();
console.log(place)
});
}
showPosition(position) {
this.xml_Latitude = position.coords.latitude;
this.xml_Lang = position.coords.longitude;
this._http.getPlaces(this.xml_Latitude, this.xml_Lang).subscribe(
data => { this.maps = data },
err => { this.cat_error = true }
);
var result = this.maps.results;
var city = result[0].address_components[4].long_name + "," + result[0].address_components[6].long_name;
alert(city);
}
}
and geoservice file
import {Injectable} from 'angular2/core';
import { Response, Jsonp} from 'angular2/http';
import 'rxjs/add/operator/map';
#Injectable()
export class geoService {
constructor(private http: Jsonp) { }
public xml_Latitude: string;
public xml_Lang: string;
public getPlaces(xml_Latitude, xml_Lang) {
return this.http.get(`http://maps.googleapis.com/maps/api/geocode/json?latlng=
'${this.xml_Latitude}','${this.xml_Lang}'&sensor=true`)
.map((res: Response) => res.json())
.catch(this.handleError);
}
private handleError(error: Response) {
console.error(error);
return error.json().error || 'Server error';
}
}
error also says getplaces is not a function, I think I am missing something but don't know what....
In addition to the callback ordering problem identified by Thierry, you have a lost this context on this line:
navigator.geolocation.getCurrentPosition(this.showPosition);
The Problem
You have the classic JavaScript problem known as the incorrect this context.
The this keyword in JavaScript behaves differently than in does in other languages like C# and Java.
How this works
The this keyword, in a function, is determined as follows:
* If the function was created through a call to .bind, the this value is the argument provided to bind
* If the function was invoked through a method call, e.g. expr.func(args), then this is expr
* Otherwise
* If the code is in strict mode, this is undefined
* Otherwise, this is window (in a browser)
Let's look at how this works in practice:
class Foo {
value = 10;
doSomething() {
// Prints 'undefined', not '10'
console.log(this.value);
}
}
let f = new Foo();
window.setTimeout(f.doSomething, 100);
This code will print undefined (or, in strict mode, throw an exception).
This is because we ended up in the last branch of the decision tree above.
The doSomething function was invoked, the function wasn't a result of a bind call, and it wasn't invoked in a method syntax position.
We can't see the code for setTimeout to see what its invocation looks like, but we don't need to.
Something to realize is that all doSomething methods point to the same function object.
In other words:
let f1 = new Foo();
let f2 = new Foo();
// 'true'
console.log(f1.doSomething === f2.doSomething);
We know that setTimeout can only see the function we passed it, so when it invokes that function,
there's no way for it to know which this to provide.
The this context has been lost due to our referencing the method without invoking it.
The Red Flag
Once you know about this problems, they're easy to spot:
class Foo {
value = 10;
method1() {
doSomething(this.method2); // DANGER, method reference without invocation
}
method2() {
console.log(this.value);
}
}
The Solution
You have a few options here, each with its own trade-offs.
The best option depends on how often the method in question is invoked from differing call sites.
Arrow Function in Class Definition
Instead of using the normal method syntax, use an arrow function to initialize a per-instance member.
class DemonstrateScopingProblems {
private status = "blah";
public run = () => {
// OK
console.log(this.status);
}
}
let d = new DemonstrateScopingProblems();
window.setTimeout(d.run); // OK
Good/bad: This creates an additional closure per method per instance of your class. If this method is usually only used in regular method calls, this is overkill. However, if it's used a lot in callback positions, it's more efficient for the class instance to capture the this context instead of each call site creating a new closure upon invoke.
Good: Impossible for external callers to forget to handle this context
Good: Typesafe in TypeScript
Good: No extra work if the function has parameters
Bad: Derived classes can't call base class methods written this way using super.
Bad: The exact semantics of which methods are "pre-bound" and which aren't create an additional non-typesafe contract between your class and its consumers.
Function Expression at Reference Site
Shown here with some dummy parameters for explanatory reasons:
class DemonstrateScopingProblems {
private status = "blah";
public something() {
console.log(this.status);
}
public run(x: any, y: any) {
// OK
console.log(this.status + ': ' + x + ',' + y);
}
}
let d = new DemonstrateScopingProblems();
// With parameters
someCallback((n, m) => d.run(n, m));
// Without parameters
window.setTimeout(() => d.something(), 100);
Good/bad: Opposite memory/performance trade-off compared to the first method
Good: In TypeScript, this has 100% type safety
Good: Works in ECMAScript 3
Good: You only have to type the instance name once
Bad: You'll have to type the parameters twice
Bad: Doesn't easily work with variadic parameters
I think that you should move the result block into the subscribe callback associated the getPlaces method call:
showPosition(position) {
this.xml_Latitude = position.coords.latitude;
this.xml_Lang = position.coords.longitude;
this._http.getPlaces(this.xml_Latitude, this.xml_Lang).subscribe(
data => {
this.maps = data;
var result = this.maps.results; // <----------
var city = result[0].address_components[4].long_name + "," + result[0].address_components[6].long_name;
alert(city);
},
err => { this.cat_error = true }
);
}
It's because this.maps is undefined before the callback is called. And you try tyo get the result attribute before (this.maps.results).
Edit
I also see a problem at the line navigator.geolocation.getCurrentPosition. You could refactor your code this way:
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => { // <----
this.showPosition(position);
});
} else {
alert("Geolocation is not supported by this browser.");
}