Angular 6 injecting window token in library service - angular6

I have a library that has the code from this website post:
https://brianflove.com/2018/01/11/angular-window-provider/
In my library, I also have a service where I want to have access to the window object. However, adding in the following to the constructor is not working:
#Inject(WINDOW) private window: Window
When trying to build the library with the cli, the following error occurs:
Metadata collected contains an error that will be reported at runtime:
Could not resolve type Window.
I can change it to use the type any rather than Window, or add #dynamic as a comment and it does then build OK. Is there a better way of doing this?
I'm not sure if this is even the right way to do it, as it also relies on the consumer to have set WINDOW_PROVIDERS in its main app module providers array.

I am using the same service and I'm injecting it like this:
#Inject(WINDOW) private _window: Window
Notice the underscore ('_') character in front of the window variable. I suspect you cannot name a variable window, because this would clash with the built-in browser window object.
Other than that, make sure that you are importing the injection token in your component/service as such:
import { WINDOW } from '[path to your services]/window.service';
And in you app.module.ts (or whatever module you're using it in):
import { WINDOW_PROVIDERS } from '[path to your services]/window.service';
...
providers: [
WINDOW_PROVIDERS
]
...

Related

In Angular, imported json file as indexed type always return undefined

You will find instructions to reproduce on your own device at the bottom.
I have a basic Angular project I created using Angular CLI, running on TypeScript 3.1.3, with nothing much added aside a class and a json file.
I created a class ResourcesService with the following command with Angular CLI:
ng generate service Resources
I'm basically using it to load json files, as a mean of localising (instead of using Angular unfinished builtin internationalisation features).
The following is my class, as well as the json file:
ResourcesBundle.json
{
"label.changeLanguage": "Change language",
"label.education": "Education",
"label.experience": "Experiences",
"label.skills": "Skills",
"label.summary": "Summary",
"label.language.english": "English",
"label.language.french": "French"
}
resources.service.ts
import * as resources from '../assets/resources/ResourcesBundle.json';
#Injectable({
providedIn: 'root'
})
export class ResourcesService {
constructor() {}
public getString(label: string): string {
let resource: string = resources[label];
return resource;
}
}
Of course, in order to be able to import the json file that way, I've set "resolveJsonModule": true in tsconfig.json.
The service by itself is working properly. I can inject it and call the getString method, and it's running without any error.
However, no matter what value I pass to the getString method, the returned value is always undefined. I've even tried to hard code the value for label = 'label.summary', but it's still returning undefined. The only time it's working properly is when I write the string directly between the brackets:
let resource: string;
label = 'label.summary';
resource = resources[label]; // resource == undefined
resource = resources['label.summary']; // resource == 'Summary'
Within the TS on VSCode, the content of resources is as following:
label.changeLanguage
label.education
label.experience
label.language.english
label.language.french
label.skills
label.summary
When using console.log(resources), the console was displaying something like this on Firefox:
Object {
label.changeLanguage: "Change language"
label.education: "Education"
label.experience: "Experience"
label.language.english: "English"
label.language.french: "French"
label.skills: "Skills"
label.summary: "Summary"
}
So the json is properly loaded, but it can only be used with hard coded string.
The only other solution I found was to give up the json file and initialise an indexed type directly in the code:
private resources: { [key: string]: string } = {
'label.changeLanguage': 'Change language',
'label.education': 'Education',
'label.experience': 'Experiences',
'label.skills': 'Skills',
'label.summary': 'Summary',
'label.language.english': 'English',
'label.language.french': 'French'
};
However, I don't think that's a good approach, at all...
In the case of the imported json file, why does it always return undefined when I use a variable? Or otherwise, why does it work only with a hard coded string between the brackets?
Edit:
You will find below a stackblitz link to a demo project:
https://stackblitz.com/edit/angular-h2aspf?file=tsconfig.json
If you run it on the browser, it will work properly (the console will properly display Change language).
However, if you download it and run it locally, you will notice that the console will display undefined instead.
To run it locally:
You must have npm and Angular CLI
Download and unzip the stackblitz demo in a folder
Run npm i in the project folder
Run ng serve --open
Open the console on your browser, it should be displaying undefined, instead of the expected value (Change language on stackblitz)
Edit:
According to a comment on the Angular CLI issue, a workaround is to set "esModuleInterop": true in tsconfig.json, and to change the import statement from:
import * as resources from '../assets/resources/ResourcesBundle.json';
To this:
import resources from '../assets/resources/ResourcesBundle.json';
Original answer:
After checking multiple times on different devices, I think this is a bug directly related to Angular (current version: 7.0.2).
To take the example I gave in the question again:
https://stackblitz.com/edit/angular-h2aspf?file=tsconfig.json
On the browser, this demo is outputting Change language on the console.
On locale device:
Download and unzip the stackblitz demo in a folder
Run npm i in the project folder
If you run with ng serve, you will notice undefined in the web browser console
Stop Angular, then run again with ng serve --prod. The web browser console is now properly outputting Change language
I've opened the following issues for Angular and Angular CLI projects on GitHub for this problem:
Angular: Issue #26785: Imported json file as indexed type always giving undefined when Angular is not running in production mode
Angular CLI: Issue #12781: Imported json file as indexed type always giving undefined, but not when running ng serve --prod

Jodd proxy Unable to install breakpoint due to missing line number attributes

I have a jodd project that uses Proxetta and JTX for creating transactions over services classes. The issue is that when I try to debug a service class I receive :
Unable to install breakpoint due to missing line number attributes
I suspect that there has something to do with they way Proxetta generates my proxies classes as it seems that in Spring if you have no interface for a class the same happens.
I use Eclispe and here how Proxetta is initialized:
public void initProxetta() {
ProxyAspect txServiceProxy = new ProxyAspect(AnnotationTxAdvice.class,
new MethodAnnotationPointcut(Transaction.class) {
#Override
public boolean apply(MethodInfo mi) {
return isPublic(mi) &&
isTopLevelMethod(mi) &&
matchClassName(mi, "*ServiceImpl") &&
super.apply(mi);
}
});
proxetta = ProxyProxetta.withAspects(txServiceProxy);
proxetta.setClassLoader(this.getClass().getClassLoader());
}
Would you please try the following quickstart webapp1 example?
Its gradle project, so you can quickly import it in any IDE. In this example, we create proxy almost exactly like you above, but on actions (which should not make a difference). Now try to put a breakpoint into the IndexAction - this one gets proxified, for example. I am able to put break point there in IntelliJ IDEA.
Moreover, I dunno why Eclipse complains about the breakpoint in the service implementation class, since Proxetta as you used above creates a proxy subclass, and does not change the target class in any way. So when you put breakpoint in the service implementation code, it is in your class, not proxy class.
Finally, did you put BP on the method, or inside the code? If it is the first (on the method), then please try to put the BP inside the code of your service: eg on first line of the method body.

Use MessageDialog/MessageBox with Portable Class Library and MVVM Light

I´m developing an App that will be available for Windows Phone 8 and the Windows Store. To reduce redundancy I´m using a Portable Class Library (PCL) and on top of that I'm trying to apply the MVVM pattern with the help of the MVVM Light PCL Toolkit. The ViewModels are placed in the PCL and are bound directly in the XAML of the Apps pages.
When the data is received without an error, everything works fine. But I don´t know how to get the exceptions/error message back to the App when errors do happen.
Inside the Windows Store App errors will show as a MessageDialog while the Wp8 App will use the MessageBox class. Obviously the PCL isn´t aware of any of these classes. What I´m not getting is how to know if a ViewModel ran into an error, and how to get the message inside the App. Is this even possible when the ViewModels are bound inside the XAML?
The code in the ViewModel (inside the PCL) looks like this:
DataService.Authenticate((token, error) =>
{
if (error != null)
{
// This is, obviously, not going to work.
MessageBox.Show(error.Message);
return;
}
Token = token;
});
So I have to save the error somehow and let the App itself know the error has occurred, and then call the matching way of showing the error to the user.
Currently I´m thinking of something like defining an Error-property inside the BaseViewModel and fill it when errors in the ViewModel occur. Then, in the CodeBehind of the pages, make them aware of the current ViewModel and bind a PropertyChanged-event to this Error-property. But I was not able to implement it yet, so I don't know if this is even the right way to go.
Do I have to step down from the idea to bind the ViewModels inside the XAML, and do I instead have to initialize them inside the pages Codebehind?
Your instinct is correct, but there are more than a few ways of going about this.
First and foremost, you can use Mvvm's Messaging library, which will allow your ViewModel to send messages directly to your View. Your View can then handle it in any way it wishes, including but not limited to using a MessageDialog.
Secondly, you can also create a Function or Action (likely the former) in your ViewModelLocator for ShowMessageDialog. This Function will likely take a string and return a Task. Then, after you initialize your ViewModelLocator initially, you can inject your ShowMessageDialog code. Your ViewModels can then use whatever platform's MessageDialogs that they please.
Ex:
Note: This code uses the BCL Async libraries that are accessible in Nuget. They work in the PCL just fine.
ViewModelLocator:
public static Func<string, Task> ShowMessageDialog { get; set; }
App.xaml.cs:
ViewModelLocator.ShowMessageDialog = (message) =>
{
// For Windows Phone
return TaskFactory.StartNew(() => MessageBox.Show(message));
// For Windows 8
MessageDialog md = new MessageDialog(message);
return md.ShowAsync().AsTask();
};
ViewModel:
await ViewModelLocator.ShowMessageDialog("This is my message.");
Secondary Note: The md.ShowAsync().AsTask(); must be run on the UI Thread. This means that you will have to invoke it via the dispatcher in the case that you are running it in a task asynchronously. This is possible using a similar method of injecting the use of the app's CoreDispatcher via the RunAsync method.
This means that you can, on any platform (Windows 8 and Windows Phone shown above), inject whatever Message Dialog system you want and use it in your PCL.
I would say that it is much easier to do the first method I suggested, as that is what it is there for, but the Function method version is definitely helpful at times.

passing custom events between modules through parent application

I have created a custom event that I want to use to pass a string between two modules. The event looks like this:
package com.mypackage.events
{
import flash.events.Event;
public class ThumbDeleteEvent extends Event
{
public static const THUMBS_DELETED:String = "thumbsDeleted";
public var files:String;
public function ThumbDeleteEvent(type:String, files:String)
{
super(type);
this.files = files;
}
// Override the inherited clone() method.
override public function clone():Event {
return new ThumbDeleteEvent(type, files);
}
}
}
In one module I dispatch the event like so:
parentApplication.dispatchEvent(new ThumbDeleteEvent("parentApplication.thumbsDeleted", files));
and in another module I listen for the event like so:
public function init():void {
parentApplication.addEventListener("parentApplication.thumbsDeleted", onThumbsDelete);
}
if I use ThumbsDeleteEvent as the type passed in to the listener function like this:
public function onThumbsDelete(evt:ThumbDeleteEvent):void{
trace("thumb delete event for thumbs: "+evt.files);
}
I get the following error:
TypeError: Error #1034: Type Coercion failed: cannot convert com.mypackage.events::ThumbDeleteEvent#26748a31 to com.mypackage.events.ThumbDeleteEvent.
if I just use Event as the type passed in to the listener function like this:
public function onThumbsDelete(evt:ThumbDeleteEvent):void{
if(evt is ThumbDeleteEvent){
trace("thumb delete event for thumbs: "+(evt as ThumbDeleteEvent).files);
}else{
var type:XML = describeType(evt);
trace(type.toXMLString());
}
}
It works but does not think it is a ThumbDeleteEvent type class (it hits the else statement) the xml output of describe type says its type is:
type name="com.mypackage.events::ThumbDeleteEvent"
What is going on here? If I put a breakpoint in the debugger it says the event is a ThumbDeleteEvent and I can see the files parameter and its right???
The issue here is that one swf has their definition of that class, and then the other swf has its own version of that exact same class. When trying to cast between them flash does a bytecode-check to see if the definitions are the same, and if you ever changed something in that as file without updating both with the exact same info you will run into this issue. That is, compile both swf-files, then change a space in the as-file, and compile only one swf file.
Urgh it's coming back to me, all those issues with shared code between different modules. I always just slug my way through these errors until I get it to work and can never really remember what it is since it can be so many issues.
Make sure both compiled swf-files have up-to-date-versions of the file.
Make sure both swf-files have same linkage-nesting to the code-file.
If that doesn't work [can't really remember since this issue is kind of like solve-once and copy to every other project].
See in which order things are added to ApplicationDomain and make sure nothing else has their own out-of-date-version of it through something imported in flash library
Move shared code into seperate code library linked in with "dynamic binding"
Try with sharing the Interface instead
Change how assets are loaded into the ApplicationDomain
Hopefully someone has more knowledge of this issue and can tell exactly what steps to use, but this is at least a starting point... I might have more time to research this and write a post about it sometime in the future later today.
Edit:
from another SO-thread Custom AS3 Class not Updating
This is the age old problem of what ultimately boils down to is the Verify Error. It happens when you embed "Class A" in one or more applications, modules, swfs, etc. Ultimately every output swf that uses "Class A" must be recompiled when "Class A" is changed. Otherwise you end up with a situation where 1 module has the newer implementation but others don't. This problem is compounded by the fact that the application domain is defined as a "first in wins" when it encounters a Class of the same name / package - meaning if the old one is referenced first, the newer one loaded later gets ignored.
The more permanent solution is to use class promotion to an RSL that ultimately allows the RSL to control the actual "Class A" reference in which it also implements an "IClassAImpl" interface that all modules use. This allows the compiler to cross link the reference with a signature it knows about without actually embedding the actual class itself.

best way to fix inspection for soapclients in phpstorm

I'm using PHPStorm v3 and have some code which connects to a certain SOAP service. (via a simple PHP SoapClient) No problems whatsoever. But the PHPStorm inspector cant find the methods available of the WSDL and thus cant recognize the used methods:
$this->soap = new SoapClient('somewsdl url');
$issues = $this->soap->getIssuesFromJqlSearch($this->auth,
'ticketId = '.$ticket->getId().'
AND impId ~ "'.$currentImplementation->getIdentifier().'"', 1);
Everything works but the method 'getIssuesFromJqlSearch' which is provided by the external WSDL is highlighted with the mentioning of an undefined method... How can i 'tell' PHPStorm what should/could be used (or explain how to parse the WSDL?)
You can suppress the inspection for this statement from the Alt+Enter, right arrow menu:
This is not perfect, since it does not parse the WSDL and you have to do it manually, but works fine after the initial setup.
Create a class extending the native SoapClient and use annotations to add virtual methods:
/**
* #method mixed getIssuesFromJqlSearch
**/
class VendorSpecific extends \SoapClient {}
Or you could generate such client yourself, implementing all the methods as a proxy to self::__soapCall(). See my SoapClient generator for reference. The upside is that it can parse the WSDL, though not perfectly.