Im using a few modules repeatedly in a Flex app and varying the conditions by passing in params via the 'loaderInfo.url' var. This works fine for the first iteration of a given module but subsequent attempts will always see the same params as the first creation regardless of what is actually used.
Is there some way to reset this value when the module is created?
private var moduleInfo : IModuleInfo;
private function loadPageModule( pathString : String, pageParam : String ) : void
{
pathString = "modules/" + pathString + ".swf?param=" + pageParam;
moduleInfo = ModuleManager.getModule( pathString );
moduleInfo.addEventListener( ModuleEvent.READY, onPageModuleReady, false, 0, true);
moduleInfo.load( ApplicationDomain.currentDomain, null, null );
}
When I view the param in the 'CreationComplete' handler (eg 'trace( this.loaderInfo.url );') its the same every time (for a given module) regardless of what is actually passed in via the ?param=string. What am I doing wrong?
I'm little confused by your code. "moduleInfo" is a local variable in loadPageModule, meaning after the function executes it should be garbage collected. Typically adding a event listener would stop the object from being GC'ed but you specifically set the "weakListener" argument of addEventListener to true.
Can you set the weakListener argument of addEventListener to false and manually remove the listener in onPageModuleReady? Like below:
// change true to false on this line
moduleInfo.addEventListener( ModuleEvent.READY, onPageModuleReady, false, 0, false);
// tweak onPageModuleReady
private function onPageModuleReady(e:Event):void {
e.target.removeEventListener(ModuleEvent.READY, onPageModuleReady);
// that will allow the object to be GC'ed
...rest of your code
Test it again with that configuration and report back if things change.
Well I don't know why this is doing what its doing.. but.. an effective way to pass unique params to different instances of a module is to use the IModuleInfo.data param. The url method never worked properly (for me).
Related
I have an issue with handling popups. I have implemented ILifeSpanHandler and OnBeforeBrowse (amoungst others) from the IRequestHandler.
How do I know in the ILifeSpanHandler what URL is being called? I am unable to get it in either the OnAfterCreated or OnBeforePopup. Currently I see it first in OnBeforeBrowse.
I have no code as my question is a "How to". In OnBeforePopup I have checked targetUrl however it seems to be there for decoration as I have read that it is not implemented anyway. I have also looked at the browner/chromiumWebBrowser objects, browser and newBroswer seem to be nothing. One would expect in OnAfterCreated chromiumWebBrowser would return an object but it is nothing in my case.
I am testing with the following
Public Sub OnAfterCreated(chromiumWebBrowser As IWebBrowser, browser As IBrowser) Implements ILifeSpanHandler.OnAfterCreated
Try
Debug.Print(vbNewLine)
Debug.Print("OnAfterCreated")
Debug.Print(String.Concat("OnAfterCreated - MainFrame.Url "), browser.MainFrame.Url)
Debug.Print("OnAfterCreated")
Debug.Print(vbNewLine)
Catch ex As Exception
End Try
End Sub
And I have the following
Public Function OnBeforePopup(chromiumWebBrowser As IWebBrowser, browser As IBrowser, frame As IFrame, targetUrl As String, targetFrameName As String, targetDisposition As WindowOpenDisposition, userGesture As Boolean, popupFeatures As IPopupFeatures, windowInfo As IWindowInfo, browserSettings As IBrowserSettings, ByRef noJavascriptAccess As Boolean, ByRef newBrowser As IWebBrowser) As Boolean Implements ILifeSpanHandler.OnBeforePopup
Try
Debug.Print(vbNewLine)
Debug.Print("OnBeforePopup")
Debug.Print(String.Concat("OnBeforePopup - targetUrl "), targetUrl)
Debug.Print(String.Concat("OnBeforePopup - browser.MainFrame.Url "), browser.MainFrame.Url)
Debug.Print(String.Concat("OnBeforePopup - chromiumWebBrowser.Address "), chromiumWebBrowser.Address)
Debug.Print("OnBeforePopup")
Debug.Print(vbNewLine)
Catch ex As Exception
End Try
Return False
End Function
I have seen different approaches in handling popups using ILifeSpanHandler interface. One approach that I've seen also here in Stack Overflow and was accepted as the correct answer to that particular question is to return true in the OnBeforePopup implementation of ILifeSpanHandler then pass the targetURL argument to a handler that creates the popup.
This approach is very unideal because you are destroying the link between the page that actually opened the popup and the popup itself. If you access via JavaScript the opener property of the window object inside the popup you would notice that it is null. And the page that opened the popup would never know that the popup was actually opened because returning true cancels the creation.
The other approach is to let Cef create the popup and the programmer just decides whether to show the browser as a popup window or a child to control (like in tabbed browsing). This is error-free and almost ideal. But the problem with this approach is that you are not able to listen to events such as FrameLoadStart, FrameLoadEnd, AddressChanged, TitleChanged, etc.
One approach that is tagged experimental by the Cef developers is to return a new IWebBrowser instance via newWebBrowser out parameter. This has so many many side effects. The page that opened the popup would, of course, recognize the popup as his although it was not the original browser (IBrowser) that it created. The page may just ignore it like btcclicks.com and in that case, there'd be no problem. But there are websites like drops.xyz that is so particular with his stuff and will discard everything that is not originally his. So this is a problem.
So what is the correct approach?
The ChromeWebBrowser control
Now I'm going to share with you an undocumented approach in handling popups. Speaking of ChromeWebBrowser control, it is very much of help that we know how it creates the webbrowser which, in reality, it doesn't. The control just hosts the webbrowser window handle. It has a private field called managedCefBrowserAdapter (ManagedCefBrowserAdapter class) that handles the actual creation of the web browser. ChromiumWEbBrowser implements the IWebBrowserInternal that has a method OnAfterBrowserCreated with a single parameter of type IBrowser. The control then invokes browser.GetHost().GetWindowHandle() to get the actual window handle (HWND) of the webbrowser it is being hosted. It is quite good.
The problem of the ChromeWebBrowser is that it won't have a constructor that accepts an IBrowser as an argument. It only has constructor that accepts HtmlString, string and IRequestContext arguments. These control waits for the
invocation of OnHandleCreated (a base class override) where it calls the managedCefBrowserAdapter.CreateBrowser after which it waits till its implementation of IWebBrowserInternal's OnAfterBrowserCreated is invoked.
Again, what is the approach that works?
Now, this approach that actually works is a product of long series of trial and error. One caution though is that I don't know why and how it works but I know it works.
First, I did not use ChromeWebBrowser. But I copied its code omitting the part where it creates .net control. In this case, I am targeting the browser's window handle (HWND) to be host by any object that exposes a HWND. Obviously I created a class (NativeCefWebBrowser) that uses the modified code. The ChromeWebBrowser orignal constructors were still there untouched becuase they are used to the create the parent webrowser. But I added one constructor that accept the following arguments: ICefBrowserParent parent (an interface I've created and IBrowser browser that receives the browser argument in the ILifeSpanHandler's OnBeforePopup. I also added a public method AttachBrowser that has a single parameter IBrowser that recieves the IBrowser argument in the ILifeSpanHandler's OnAfterCreated. It the browser that will be kept by CefNativeWebBrowser class.
Why didn't I keep the browser instance received form ILifeSpanHandler.OnBeforePopup but used the instance received from ILifeSpanHandler.OnAfterCreated when they are the same browser instance? This is one of those parts that I don't know why. One thing I noticed is that when I called browser.GetHost().GetWindowHandle() during ILiffeSpanHandler.OnBeforePopup, the first window handle I received was the different compared to when I invoked the method during ILifeSpanHandler.OnAfterCreatd. Because of that, I store the browser instance from the latter that I passed to the NativeCefWebBrowser.AttachBrowser for its safekeeping.
In the NativeCefWebBrowser(ICefBrowserParent parent, IBrowser browser) contructor, I set the private following fields to true: browsercreated, browserinitialized (chromewebbrwoser orginal fields) and isAttachingBrowser (added private field). You don't call the ManagedCefBrowserAdapter's CreateBrowser in this contructor in instead call its OnAfterBrowserCreated passing the browser instance. You don't much in this constructor as you will wait the ILifeSpanHandler implementor to pass you the browser instance it will receive during its OnAfterCreated method. Take note that when calling the ManagedCefBrowserAdapter's OnAfterBrowserCreated method, ManagedCefBrowserAdapter will still invoke IWebBrowserInternal implementation of OnAfterBrowserCreated that when happens you have to exit immediately when isAttachingBrowser is true as the following code will no sense no more.
After calling the NativeCefWebBrowser(ICefBrowserParent, IBroser) construct, you can normally set event listeners as you will normally do.
And that's it.
The following are parts of the code that I wrote
The ICefBrowserParent interface
public interface ICefBrowserParent
{
IntPtr Handle { get; }
Size ClientSize { get; }
bool Disposing { get; }
bool IsDisposed { get; }
bool InvokeRequired { get; }
IAsyncResult BeginInvoke(Delegate d);
object Invoke(Delegate d);
event EventHandler Resize;
}
As you would notice, the methods, properties and events in this interface are already implemented by the System.Windowns.Forms.Control class. So if you implementing this from class inhering Control class, you would not need to implement this anymore. This interface is only for non-Control class.
class NativeCefWebBrowser
{
public NativeCefWebBrowser(ICefBrowserParent, IBroser)
{
requestContext = browser.GetHost().RequestContext;
this.parent = parent; // added field
HasParent = true; // IWebBrowserInternal. I don't know what's this for
mustSetBounds = true; // added field
browserCreated = true;
isAttachingBrowser = true; // added field
InitializeFieldsAndCefIfRequired();
managedCefBrowserAdapter.OnAfterBrowserCreated(browser);
}
}
ILifeSpanHandler.OnBeforePopup(..., out IWebBrowser newWebBrowser)
{
CefNativeWebBrowser b = new CefNativeWebBrowser
(
parent, // defined else where
browser
);
// Attach event handlers
b.TitleChanged...;
newWebBrowser = b;
}
ILifeSpanHandler.OnAfterCreated(...)
{
((CefNativeWebBrowser)webBrowser).AttachBrowser(browser);
}
In Java, it turns out that field accessors get cached, and using accessors has side-effects. For example:
class A {
private static final int FOO = 5;
}
Field f = A.class.getDeclaredField("FOO");
f.setAccessible(true);
f.getInt(null); // succeeds
Field mf = Field.class.getDeclaredField("modifiers" );
mf.setAccessible(true);
f = A.class.getDeclaredField("FOO");
f.setAccessible(true);
mf.setInt(f, f.getModifiers() & ~Modifier.FINAL);
f.setInt(null, 6); // fails
whereas
class A {
private static final int FOO = 5;
}
Field mf = Field.class.getDeclaredField("modifiers" );
mf.setAccessible(true);
f = A.class.getDeclaredField("FOO");
f.setAccessible(true);
mf.setInt(f, f.getModifiers() & ~Modifier.FINAL);
f.setInt(null, 6); // succeeds
Here's the relevant bit of the stack trace for the failure:
java.lang.IllegalAccessException: Can not set static final int field A.FOO to (int)6
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:100)
at sun.reflect.UnsafeQualifiedStaticIntegerFieldAccessorImpl.setInt(UnsafeQualifiedStaticIntegerFieldAccessorImpl.java:129)
at java.lang.reflect.Field.setInt(Field.java:949)
These two reflective accesses are of course happening in very different parts of my code base, and I don't really want to change the first to fix the second. Is there any way to change the second reflective access to ensure it succeeds in both cases?
I tried looking at the Field object, and it doesn't have any methods that seem like they would help. In the debugger, I noticed overrideFieldAccessor is set on the second Field returned in the first example and doesn't see the changes to the modifiers. I'm not sure what to do about it, though.
If it makes a difference, I'm using openjdk-8.
If you want the modifier hack (don't forget it is a total hack) to work, you need to change the modifiers private field before the first time you access the field.
So, before you do f.getInt(null);, you need to do:
mf.setInt(f, f.getModifiers() & ~Modifier.FINAL);
The reason is that only one internal FieldAccessor object is created for each field of a class (*), no matter how many different actual java.lang.reflect.Field objects you have. And the check for the final modifier is done once when it constructs the FieldAccessor implementation in the UnsafeFieldAccessorFactory.
When it is determined you can't access final static fields (because, the setAccessible override doesn't works but non-static final fields, but not for static final fields), it will keep failing for every subsequent reflection, even through a different Field object, because it keeps using the same FieldAccessor.
(*) barring synchronization issues; as the source code for Field mentions in a comment:
// NOTE that there is no synchronization used here. It is correct
(though not efficient) to generate more than one FieldAccessor for a
given Field.
In short: is there a way to know if a typescript parameter is required and/or has a default value?
Longer version:
Say I have the following file:
//Foo.ts
class Bar {
foo(required:string,defaultValue:number=0,optional?:boolean) {
...
}
}
I would like to know of each of the parameters:
the name
the type
is it required?
does it have a default value?
I have succesfully used method decorators with the TypeScript reflection API to get the types of the parameters, I've used this method to get their names, but so far I have not found a way to know if a variable is required and/or has a default value.
I know the typescript compiler itself can be used from within typescript. So I'm wondering if there is a way to use the parse tree of the compiler to see if a parameter is required and/or has a default value?
How would that work?
If you want to do this from scratch...
On a high level, one way of doing it is to:
Figure out how to get the SourceFile node using the compiler api of your file. That requires a bit of an explanation in itself.
From there, use the api's forEachChild function to loop over all the nodes in the file and find the node with a kind of SyntaxKind.ClassDeclaration and .name property with text Bar.
Then loop over all the children of the class by again using the api's forEachChild function and get the ones that has the kind SyntaxKind.MethodDeclaration and .name property with text foo.
To get the parameters, you will need to loop over the method node's parameters property.
Then for each parameter node, to get the name you can call .getText() on the .name property.
You can tell if the parameter is optional by doing:
const parameterDeclaration = parameterNode as ts.ParameterDeclaration;
const isOptional = parameterDeclaration.questionToken != null || parameterDeclaration.initializer != null || parameterDeclaration.dotDotDotToken != null;
Or you could use the TypeChecker's isOptionalParameter method.
To get its default expression, you will just have to check the initializer property:
propertyDeclaration.initializer;
To get the type use the TypeChecker's getTypeOfSymbolAtLocation method and pass in the symbol of the node... that gets a little bit complicated so I won't bother explaining it (think about how it's different with union types and such).
Don't do it from scratch...
I've created a wrapper around the TypeScript compiler api. Just use this code with ts-simple-ast (edit: Previously this talked about my old ts-type-info library, but ts-simple-ast is much better):
import { Project } from "ts-morph";
// read more about setup here:
// https://ts-morph.com/setup/adding-source-files
const project = new Project({ tsConfigFilePath: "tsconfig.json" });
const sourceFile = project.getSourceFileOrThrow("src/Foo.ts");
const method = sourceFile.getClassOrThrow("Bar").getInstanceMethodOrThrow("foo");
Once you have the method, it's very straightforward to get all the information you need from its parameters:
console.log(method.getName()); // foo
for (const param of method.getParameters()) {
console.log(param.getName());
console.log(param.getType().getText());
console.log(param.isOptional());
console.log(param.getInitializer() != null);
}
THIS QUESTION IS NOT ABOUT HOW TO SET DEFAULT VALUE OF A WIDGET
Hello Symfonians!
I had a fundamental doubt about forms, Im putting 2 scenarios below.
I have a customModelForm that extends a modelForm.
1> If I do not specify a default value for a form field
new: field is empty
edit: field shows the value in the object
2> If I specify a default value for a field,
new: field shows default value
edit: field shows default value
I am trying to avoid the EDIT mode behaviour in scenario 2.
The default value should only be displayed when the value in the object is not set.
I am calling parent::configure after setting the default value. Do we have any control on the 'bind' event?
Thanks
This shouldn't be happening, at least in Doctrine. The part of the code where this is happening is in updateDefaultsFromObject in sfFormDoctrine. The relevant lines are:
if ($this->isNew())
{
$defaults = $defaults + $this->getObject()->toArray(false);
}
else
{
$defaults = $this->getObject()->toArray(false) + $defaults;
}
updateDefaultsFromObject does net get called until the entire configure chain is done, so something else must be going on here.
Are you using Doctrine? Are you using the most current version of Symfony (there was a bug here a while ago)? Are you sure the default is getting set in the configure method of your form?
The isNew check richsage is recommending should be avoided. There is a larger issue here as the proper behavior is for default value to get overwritten by an existing object's values.
First of all, call parent::configure() first in your configure() method. That way you don't run the risk of your configuration being overwritten by the parent configuration.
You can set defaults based on the model's status by doing something like the following in your configure() method:
if ($this->getObject()->isNew())
{
// do something here but only if the object is new
}
else
{
// the object is being edited
}
Data binding in ActionScript is really cool. But what if I want to refactor a big switch or if statement inside the curly braces into a function, for example:
{person.gender == 'male' ? 'Mr.' : 'Ms.'}
into:
{salutation(person)}
The compiler doesn't let me do that. I know about properties and I could write getters and setters on the person object. But since I am using inlined JSON objects now that's not convenient(I think). What are other good ways to refactor this code?
To answer Matt's comment. The data type of person is just plain Object. It was decoded from JSON format coming from a service call.
You'll need to make the Person class (assuming you have one) bindable in order for this to work.
However, since you are saying you're using JSON objects, I'm assuming you just have anonymous objects that were parsed from a JSON string. In that case, I'm pretty sure that won't work. You'll need to create a strongly typed object that has bindable properties.
Just FYI: to avoid having to write custom JSON parsers for every object you want to create, you can create strong typed objects from vanilla objects using a bytearray trick:
public static function toInstance( object:Object, clazz:Class ):* {
var bytes:ByteArray = new ByteArray();
bytes.objectEncoding = ObjectEncoding.AMF0;
// Find the objects and byetArray.writeObject them, adding in the
// class configuration variable name -- essentially, we're constructing
// and AMF packet here that contains the class information so that
// we can simplly byteArray.readObject the sucker for the translation
// Write out the bytes of the original object
var objBytes:ByteArray = new ByteArray();
objBytes.objectEncoding = ObjectEncoding.AMF0;
objBytes.writeObject( object );
// Register all of the classes so they can be decoded via AMF
var typeInfo:XML = describeType( clazz );
var fullyQualifiedName:String = typeInfo.#name.toString().replace( /::/, "." );
registerClassAlias( fullyQualifiedName, clazz );
// Write the new object information starting with the class information
var len:int = fullyQualifiedName.length;
bytes.writeByte( 0x10 ); // 0x10 is AMF0 for "typed object (class instance)"
bytes.writeUTF( fullyQualifiedName );
// After the class name is set up, write the rest of the object
bytes.writeBytes( objBytes, 1 );
// Read in the object with the class property added and return that
bytes.position = 0;
// This generates some ReferenceErrors of the object being passed in
// has properties that aren't in the class instance, and generates TypeErrors
// when property values cannot be converted to correct values (such as false
// being the value, when it needs to be a Date instead). However, these
// errors are not thrown at runtime (and only appear in trace ouput when
// debugging), so a try/catch block isn't necessary. I'm not sure if this
// classifies as a bug or not... but I wanted to explain why if you debug
// you might seem some TypeError or ReferenceError items appear.
var result:* = bytes.readObject();
return result;
}