I was trying out the 5 min Anuglar2 Tutorial and when it said that you are able to use external templates I tried it.
My component looks like this
import {Component, Template, bootstrap} from 'angular2/angular2';
// Annotation section
#Component({
selector: 'my-app'
})
#Template({
url: "component.html"
})
// Component controller
class MyAppComponent {
constructor() {
this.name = 'Alice';
}
}
bootstrap(MyAppComponent);
I had a mistake in my external template and fixed it, but the HTML file was still cached so I couldn't the effects in the browser.
Figuring out how they cache it I looked at the code on Github
I found this
#angular/modules/angular2/src/core/compiler/template_loader.js
#Injectable()
export class TemplateLoader {
_xhr: XHR;
_htmlCache: StringMap;
_baseUrls: Map<Type, string>;
_urlCache: Map<Type, string>;
_urlResolver: UrlResolver;
constructor(xhr: XHR, urlResolver: UrlResolver) {
this._xhr = xhr;
this._urlResolver = urlResolver;
this._htmlCache = StringMapWrapper.create();
this._baseUrls = MapWrapper.create();
this._urlCache = MapWrapper.create();
}
// TODO(vicb): union type: return an Element or a Promise<Element>
load(template: Template) {
if (isPresent(template.inline)) {
return DOM.createTemplate(template.inline);
}
if (isPresent(template.url)) {
var url = this.getTemplateUrl(template);
var promise = StringMapWrapper.get(this._htmlCache, url);
if (isBlank(promise)) {
promise = this._xhr.get(url).then(function (html) {
var template = DOM.createTemplate(html);
return template;
});
StringMapWrapper.set(this._htmlCache, url, promise);
}
return promise;
}
So I check out the StringMapWrapper angular/modules/angular2/src/facade/collection.es6
and for set the code is just
static set(map, key, value) {
map[key] = value;
}
I saw that the StringMapWrapper comes from global
export var StringMap = global.Object;
But looking in angular/modules/angular2/src/facade/lang.es6 I cant figure out where the Map is cached.
I do not know much about the caching process and hope someone could explain how they do it in this case.
StringMapWrapper.create() creates an object literal {}. They use something like StringMapWrapper for Dart support where these primitives are created differently in the other language. In short all they're doing is this
var cache = {};
xhr(templateUrl).then(template => {
cache[templateUrl] = template;
return template;
})
#gdi2290 had nearly answered your question, and if you want to understand more about Cache Management in JavaScript/TypeScript, please see my post here http://www.ravinderpayal.com/blogs/12Jan2017-Ajax-Cache-Mangement-Angular2-Service.html .
There is a step by step explanation of a cache management class which acts as Layer to AJAX, and can be injected to components as Service. Here's a synopsis of code from class:-
private loadPostCache(link:string){
if(!this.loading[link]){
this.loading[link]=true;
this.links[link].forEach(a=>this.dataObserver[a].next(false));
this.http.get(link)
.map(this.setValue)
.catch(this.handleError).subscribe(
values => {
this.data[link] = values;
delete this.loading[link];
this.links[link].forEach(a=>this.dataObserver[a].next(false));
},
error => {
delete this.loading[link];
}
);
}
}
private setValue(res: Response) {
return res.json() || { };
}
private handleError (error: Response | any) {
// In a real world app, we might use a remote logging infrastructure
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
postCache(link:string): Observable<Object>{
return Observable.create(observer=> {
if(this.data.hasOwnProperty(link)){
observer.next(this.data[link]);
}
else{
let _observable=Observable.create(_observer=>{
this.counter=this.counter+1;
this.dataObserver[this.counter]=_observer;
this.links.hasOwnProperty(link)?this.links[link].push(this.counter):(this.links[link]=[this.counter]);
_observer.next(false);
});
this.loadPostCache(link);
_observable.subscribe(status=>{
if(status){
observer.next(this.data[link]);
}
}
);
}
});
}
Related
Motive : I am doing a validation on book name during save - if there is already a book in the DB with the same name, validation error message should be thrown.
I have a method in my service that returns the books object if any books are present with the user entered book name.
After I call this service method I subscribe to it, assign the result to a bool variable and check the bool variable in if statement. since using subscribe/ observable, the if statement executes first and then the subscriber is getting called and the correct value is not returned and validation is not firing.
Where am I going wrong ? Thanks in advance.
Below is my code :
export class AddBooksComponent implements OnInit{
bookName = "";
isError = false;
errorMessage="";
isPresent:boolean = false;
constructor(public bsModalRef: BsModalRef,private booksService: BooksService) { }
ngOnInit(): void { }
saveBooks() {
if(this.checkBookExistenance()) {
this.isError = true
this.errorMessage="The provided book name is already exists."
} else {
this.bsModalRef.hide()
}
}
checkPreferenceExistenance():boolean {
this.booksService.getBookByName(bookNameName:trim(this.bookName))
.pipe(first())
.subscribe((data) => {
// if the response has data then the book is present
if(data.length) {
isPresent= true;
}
else{
isPresent= false;
}
});
return isPresent;
}
}
this.booksService.getBookByName() is asynchonous, it takes some time to execute, in the meantime the code procedes.
checkBookExistenanceshould return an observable:
checkPreferenceExistenance(): Observable<boolean> {
return this.booksService.getBookByName(bookNameName: trim(this.bookName)).pipe(
first(),
map(data => {
if (data.length){
return true;
}else{
return false;
}
})
// or shortform without if/else: map(data => data.length)
);
}
and then:
this.checkPreferenceExistenance().subscribe(
exist => {
if (exist) {
this.isError = true
this.errorMessage = "The provided book name is already exists."
} else {
this.bsModalRef.hide()
}
}
);
I´ve downloaded the Forge Design Automation sample from the following link:
https://learnforge.autodesk.io/#/tutorials/modifymodels
But the downloable code example is not working fine. When any async method who involves the DesignAutomation API is called I get -> Value cannot be null. (Parameter 'ForgeConfiguration.ClientId'). So, I would like to know how it works and how I can set the ClientId in the ForgeConfiguration class or else if I making something else wrong. I attach a fragment of code where I get the error.
[HttpGet]
[Route("api/forge/designautomation/engines")]
public async Task<List<string>> GetAvailableEngines()
{
List<string> allEngines = new List<string>();
try
{
dynamic oauth = await OAuthController.GetInternalAsync();
// define Engines API
string paginationToken = null;
while (true)
{
Page<string> engines = await _designAutomation.GetEnginesAsync(paginationToken);
allEngines.AddRange(engines.Data);
if (engines.PaginationToken == null)
break;
paginationToken = engines.PaginationToken;
}
allEngines.Sort();
}
catch (Exception error) {
throw error;
}
return allEngines; // return list of engines
}
And the call of the method:
function prepareLists() {
list('engines', 'api/forge/designautomation/engines');
}
function list(control, endpoint) {
$('#' + control).find('option').remove().end();
jQuery.ajax({
url: endpoint,
success: function (list) {
if (list.length === 0)
$('#' + control).append($('<option>', { disabled: true, text: 'Nothing found' }));
else
list.forEach(function (item) { $('#' + control).append($('<option>', { value: item, text: item })); })
}
});
}
Did you forget to set the Forge App Keys in the Environment Variables of your project, check the page at https://learnforge.autodesk.io/#/environment/setup/netcore_da
I am working in Angular and I have a following situation:
my.service.ts has this class:
export class MyClass {
MyList: string[] = [];
MyString: string = '';
createString(): void {
this.MyList.forEach(s => {
this.MyString += s + ', ';
});
}
}
And my.component.ts calls it like this:
myData: MyClass[] = [];
this.myService.getMyData().subscribe(res => {
myData = res;
if (myData.length > 0) {
this.myData.forEach(x => x.createString());
}
});
VS Code recognizes the createString function as a metod of MyClass, but I still get an error:
ERROR TypeError: x.createString is not a function
Any explanations?
EDIT: The data comes from back end, and the back end model doesn't have this method. Maybe that is the issue?
The object coming from the server will be just a simple object not an instance of the class MyClass. You can create instances of MyClass and assign the values from the server object to the instance of the class:
this.myService.getMyData().subscribe(res => {
myData = res.map(o => Object.assign(new MyClass(), o));
if (myData.length > 0) {
this.myData.forEach(x => x.createString());
}
});
Accepted soulution didn't help me, so I propose mine. It does not require .map().
My http service:
getOffers() {
return this.http.get('https://ohipo.pl/assets/oferty.json');
}
Consuming service in component:
offers: Offer[] = [];
this.offersService.getOffers().subscribe((response: Offer[]) => {
for (let i in response) {
this.offers[i] = Object.assign(new Offer(), response[i]);
}
Im reading an url of the app http://localhost/?config=preprod
Im trying to create a Singleton service which reads UrlParameters.js and exposes get(key) method. Which stores config=preprod
Similar below (from my Angular 1.x singleton service)
get: function (key) {
if (!params) {
params = {};
var queryString = window.location.search.substring(1);
_(queryString.split('&')).each(function (param) {
var val = param.split('=');
params[val[0]] = val[1];
});
}
return params[key];
}
Now, I think I also will need access to Route params inside this service in Angular 2, since I cannot do the in Angular 2.
Also, I need to share this UrlParams singleton with another Singleton service called Flag. Which reads Flag.get('config')
Something like below (extracted from my Angular 1.x project)
Flag.js
set: function (flag) {
if (UrlParameter.get(flag)) {
localStorage.setItem(flag, UrlParameter.get(flag));
}
},
get: function (flag) {
return localStorage.getItem(flag);
}
As suggested by #JordanFrankfurt I used Location service, and fits my purpose. Also Thanks to #Günter Zöchbauer for the efforts.
Below is my UrlParams Service which is been also added in NgModule under providers
url-parameter.service.ts
import { Injectable } from '#angular/core';
import 'rxjs/add/operator/filter';
import {LocationStrategy} from '#angular/common';
#Injectable()
export class UrlParameterService {
public params = null;
constructor(private location: LocationStrategy) {}
get(key:string):String {
debugger;
if (!this.params) {
this.params = {};
var queryString = this.location.path();
queryString.split('&').forEach((param) => {
var val = (param.indexOf('?') ? param.slice(param.indexOf('?')+1).split('=') : param.split('='));
this.params[val[0]] = val[1];
});
}
return this.params[key] || false;
}
}
I would try to stay within the Angular 2 conventions, but if you simply want an instance of something you've instantiated outside of Angular 2 conventions, it's pretty simple.
var UrlParameters = function() {
this.instance = this;
this.params = null;
this.get = function(key) {
if (!this.params){
params = {};
var queryString = window.location.search.substring(1);
_(queryString.split('&')).each(function (param) {
var val = param.split('=');
params[val[0]] = val[1];
});
this.params = params;
}
return this.params[key];
};
this.set = function() {
}
}
var Flag = {
set: function (flag) {
var urlParams = UrlParameter.getInstance();
if (urlParams.get(flag)) {
localStorage.setItem(flag, UrlParameter.get(flag));
}
}
}
TypeScript version
class UrlParameter {
static instance:UrlParameter;
constructor() {
UrlParameter.instance = this;
}
get( key: string) : string {
// ...
}
}
class Flag {
set(key:string) : string {
if (UrlParameter.instance.get(key)){
// you have it
}
}
}
This might do what you want:
#Injectable()
class MyService {
constructor(router:Router) {
this.router.events
.filter(e => e instanceof NavigationEnd)
.forEach(e => {
var config = router.routerState.root.snapshot.param['config'];
console.log(config);
});
}
}
I am developing hybrid mobile application.
In one of the scenario we need to fetch mimeType from a file when we select or upload a file.
I am using apache FileTransfer.
window.resolveLocalFileSystemURL(fileURI , resolveOnSuccess, resolveOnFail)
you can get it from cordova File plugin.
$cordovaFile.checkFile(uri, '')
.then(function(entry) {
// success
var name = entry.name;
entry.file(function(data) {
// get mime type
var mime = data.type;
alert(mime);
})
}, function(error) {
// error
// show toast
});
I got it working like this in TypeScript and Angular 2:
this._File.resolveLocalFilesystemUrl(somefileUri).then((entry: Entry) => {
if (entry) {
var fileEntry = entry as FileEntry;
fileEntry.file(success => {
var mimeType = success.type;
}, error => {
// no mime type found;
});
}
});
file-transfer does not expose mimeType and other FileUploadOptions params.
Mimetype autodetection is only supported for uploads in Windows plugin code.
And here is a Jira ticket for this feature CB-5946 - it also has some suggestions on Android implementation.
In Angular 2 I use this:
export class Plugins {
albums = {
open () : Promise<any> {
return ImagePicker.getPictures({
quality: 100,
maximumImagesCount: 1,
}).then((imgUrls) => {
return imgUrls;
}, (err) => {
if(err.error == "cordova_not_available") {
alert("Cordova is not available, please make sure you have your app deployed on a simulator or device");
} else {
console.log("Failed to open albums: " + err.error);
}
});
},
}
...
#Component({
templateUrl: 'build/pages/home/home.html',
directives: [UploadButton]
})
export class HomePage implements OnInit {
openAlbums = (): void => {
var $self = this;
this._plugins.albums.open().then((imgUrls) => {
imgUrls.forEach((imageUrl: string): void => {
if (imageUrl) {
window.resolveLocalFileSystemURL(imageUrl, function (entry: FileEntry) {
entry.file(file=> {
console.log('mimeType', file.type);
}, ((error:FileError) => console.log(error)));
});
}
});
}); };
resolveLocalFileSystemURL gives back through the success callback an Entry which I had to cast to FileEntry to get access to the file method which gives back a File which extends Blob that has the mime type property.