Blazor: Cannot initialize a ComponentBase more than once - telerik

I have a complicated issue when navigation through my Telerik-Blazor-Wizard.
Every Time i want to navigate Back i get this Exception:
Error: System.InvalidOperationException: The render handle is already set. Cannot initialize a ComponentBase more than once.
at Microsoft.AspNetCore.Components.ComponentBase.Microsoft.AspNetCore.Components.IComponent.Attach(RenderHandle renderHandle)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.AttachAndInitComponent(IComponent component, Int32 parentComponentId)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.InstantiateChildComponentOnFrame(RenderTreeFrame& frame, Int32 parentComponentId)
at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewComponentFrame(DiffContext& diffContext, Int32 frameIndex)
at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewSubtree(DiffContext& diffContext, Int32 frameIndex)
at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InsertNewFrame(DiffContext& diffContext, Int32 newFrameIndex)
at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex)
at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)
at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.ComputeDiff(Renderer renderer, RenderBatchBuilder batchBuilder, Int32 componentId, ArrayRange`1 oldTree, ArrayRange`1 newTree)
at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, Exception& renderFragmentException)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()
Note that I´m using a DynamicComponent to render my WizardPages..
#foreach( WizardSeite seite in WizardSeiten )
{
<WizardStep Label="#seite.Title" OnChange="#ReactOnChange">
<Content>
<DynamicComponent #ref="#currentPage" Type="#seite.GetType()" Parameters="#seite.Parameters"></DynamicComponent>
</Content>
</WizardStep>
}
and register the Pages in the ServiceCollection (IoC-Container)..
if( component.BaseType == typeof( WizardSeite ) )
{
// Registriere Wizardseite als Basetyp "WizardSeite"
services.AddTransient( component.BaseType ?? throw new NullReferenceException(), component );
}
If i Navigate back it tries to activate the component, I´m using a custom IComponentActivator that returns the registered Instance of my Wizard-Page.
instance = registeredPages.First( seite => seite.GetType() == componentType );
After that it seems that the component gets initialized twice thus throwing this error.
I know this issue is very specific any help to understand and/or resolve the problem is highly appreciated.

I found the issue!
Inside the IComponentActivator constructor i load the registered Pages once (into a list called registeredPages). The problem with this is that if the render tree gets refreshed it tries to initialize the component but gets the same instance of it. That leads to the exception above.
The solution: I register the components as Transiants thus every time i call GetService i get a new instance of the requested type. If i refresh registeredPages it always has a fresh component that can be initialized:
registeredPages = ServiceProvider.GetServices<WizardSeite>();
instance = registeredPages.First( seite => seite.GetType() == componentType );

Related

How to make my validator conditional based upon SSJS check (Xpages)?

I have a function to call which component on my XPage performs a submit to the server:
//Used to check which if a component triggered an update
function submittedBy( componentId ){
try {
var eventHandlerClientId = param.get( '$$xspsubmitid' );
var eventHandlerId = #RightBack( eventHandlerClientId, ':' );
var eventHandler = getComponent( eventHandlerId );
if( !eventHandler ){
return false;
}
var parentComponent = eventHandler.getParent();
if( !parentComponent ){
return false;
}
return ( parentComponent.getId() === componentId );
} catch( e ){ /*Debug.logException( e );*/ }
}
I have a validator which is called normally via expression language:
validator="#{applicationValidators.valPhone}"
I would like to make the validator conditioned by a check if the submit is done by the element/component with the btnSave id on it e.g.
<xp:this.validator><![CDATA[#{javascript:if (submittedBy('btnSave')){
return applicationValidators.valPhone();
}}]]></xp:this.validator>
but the valPhone funktion expects some inputs which are injected automatically if I call the method via EL:
public void valPhone(FacesContext facesContext, UIComponent component, Object value)
Is there a way to include the submittedBy function in the EL or must I supply the objects when calling the function in SSJS cause for now I get the error message:
Error while executing JavaScript action expression Script interpreter
error, line=2, col=38: Java method 'valPhone()' on java class
'org.acme.projectx.test.ApplicationValidators' not found
When I apply the objects e.g.
applicationValidators.valPhone(facesContext,getComponent("inpPhone"),getComponent("inpPhone").getValue());
it does run the validator method but does not seem to know how to handle the response (in my case I throw a ValidatorExeption with a message.
Error while executing JavaScript action expression Script interpreter
error, line=4, col=31: Error calling method
'valPhone(com.ibm.xsp.domino.context.DominoFacesContext,
com.ibm.xsp.component.xp.XspInputText, java.lang.String)' on java
class 'org.acme.projectx.test.ApplicationValidators' Phone is not
valid
Phone is not valid is the string message that I included in the FacesMessagge that I throw in the ValidatorExeption.
I'd take a totally different approach:
create a page controller class
call a method in the controller when the button is clicked
do your validation based on your value(s) in that method
if validation fails create a message in the validation error stack
I don't have a reference right now but this would be much easier to program and control in general.

Error at Watin.Core.Comparer StringComparer class

I am using Watin system in my program.
I get the following error:
ArgumentNullException at Watin.Core.Comparer
StringComparer(string comparisonValue, bool ignoreCase)
Error : Value Cannot be Null(comparisonValue)
But I have no idea that who and when stringcomparer is called,
also I don't know how to debug it.
Here is some of my code.
using (IE browser = new IE(url))
{
Trace.TraceInformation("success to create IE instance.");
int waitSecond = TimeSpan.FromSeconds(30).Seconds;
browser.WaitForComplete(waitSecond);
.........
.........
}
)
Add some ErrorTrace
at WatiN.Core.Comparers.StringComparer..ctor(String comparisonValue, Boolean ignoreCase)
at WitiN.core.DialogHandlers.DialogWatcher.HasDialogSameProcessNameAsBrowserWindow(Window window)
at WatiN.Core.DialogHandlers.DialogWatcher.HandleWindow(Window window)
at WatiN.Core.DialogHandlers.DialogWatcher.Start()
at System.Threading.ThreadHelper.ThreadStart_Context(ojbect state)
at System.Threading.ExecutionContext.Runinternal(exceutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutinoContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutinoContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
This code works as expected (no exceptions thrown):
using (IE browser = new IE("http://www.google.com"))
{
//Trace.TraceInformation("success to create IE instance.");
int waitSecond = TimeSpan.FromSeconds(30).Seconds;
browser.WaitForComplete(waitSecond);
}
Make sure you are using the latest sources for WatiN and you don't have any other code executing at that time (the exception you are getting if for a DialogWatcher, nothing to do with the code you posted).
Not sure why you've used the .WaitForComplete method like that but that method is called by default internally for each method you run that interacts with a web page. If you want to specify a generic wait time for loading web pages you should use settings for the WatiN object like this:
Settings.WaitForCompleteTimeOut = 30;
using (IE browser = new IE("http://www.google.com"))
{
//Trace.TraceInformation("success to create IE instance.");
browser.WaitForComplete();
}

control p5 slider.setRange() not found in subclass

Im trying to set the Range of a slider within a subclass, catching the respective controller using getController, which works fine, proven by the returned value i get within the print. But controller.setRange() doesnt get recognized as a function.
can the range only be initialized during the creation of the object or does getController return a different object than i expect it does?
thanks!
class Stepper
{
String stepperName = "default";
int ID = motorID;
int stepperValue;
Stepper(String givenName) {
stepperName = givenName;
cp5.addSlider(stepperName)
.setPosition(sliderPosX+(sliderWidth+100)*surgeonBotID, sliderPosY+90*servoList.length+30*(motorID-servoList.length))
.setSize(sliderWidth, int(sliderHeight*0.5))
//.setRange(0, 179)
.setSliderMode(Slider.FLEXIBLE);
println("Created new Stepper: "+stepperName+ " ID: "+ID);
motorID++;
}
void setRange(float min, float max){
println("object: "+cp5.getController(getStepperName()).getValue());
cp5.getController(getStepperName()).setRange(min, max);
}
...
}
Questions like these are best answered by consulting the API.
The getController() method returns a Controller. The Controller class does not have a setRange() function. That instance happens to be an instance of Slider, which is a subclass of Controller, but the compiler has no way of knowing that. That's what's causing your error.
You can tell the compiler that the instance is indeed a Slider by casting the returned value to a Slider, and then you can access the methods defined by the Slider class:
((Slider)cp5.getController(getStepperName())).setRange(min, max);
To make that easier to understand, here it is split up into two lines:
Slider s = (Slider)cp5.getController(getStepperName());
s.setRange(min, max);

Access to ViewEngines.Engines.FindView Method in not web context

I have question about access to FindView Method in not web context,
public static bool ViewExists(ControllerContext ctx, string name)
{
var result = ViewEngines.Engines.FindView(ctx, name, null);
return result.View != null;
}
I want to render view to create static files (JavaScript files rendered in Razor),
When I use this metod in web context (in controller action metod everythik is ok, but in test context I get exception
System.Web.HttpException : The application relative virtual path '~/Views/Test/New.aspx' cannot be made absolute, because the path to the application is not known.
How I would use (moq) ControllerContext in not web context to Render Razor View ?

Subscription to DTE events doesn't seem to work - Events don't get called

I've made an extension inside a package and I am calling the following code (occurs when a user presses a button in the toolbar):
DocumentEvents documentEvents = (DTE2)GetService(typeof(DTE));
_dte.Events.DebuggerEvents.OnEnterBreakMode += DebuggerEvents_OnEnterBreakMode;
_dte.Events.DebuggerEvents.OnEnterDesignMode += DebuggerEvents_OnEnterDesignMode;
_dte.Events.DebuggerEvents.OnContextChanged += DebuggerEvents_OnContextChanged;
_dte.Events.DocumentEvents.DocumentSaved += new _dispDocumentEvents_DocumentSavedEventHandler(DocumentEvents_DocumentSaved);
_dte.Events.DocumentEvents.DocumentOpened += new _dispDocumentEvents_DocumentOpenedEventHandler(DocumentEvents_DocumentOpened);
void DocumentEvents_DocumentOpened(Document Document)
{
}
void DocumentEvents_DocumentSaved(Document Document)
{
}
void DebuggerEvents_OnEnterBreakMode(dbgEventReason Reason, ref dbgExecutionAction ExecutionAction)
{
}
void DebuggerEvents_OnContextChanged(Process NewProcess, Program NewProgram, Thread NewThread, StackFrame NewStackFrame)
{
}
private void DebuggerEvents_OnEnterDesignMode(dbgEventReason reason)
{
}
The first and the major problem is that the subscription to the event doesn't work. I've tried:
Opening new documents
Detaching from debug (thus supposedly triggering OnEnterDesignMode
Saving a document
None of these seem to have any effect and the callback functions were never called.
The second issue is that the subscription to the event line works USUALLY (the subscription itself, the callback doesn't work as described above) but after a while running the subscription line, e.g:
_dte.Events.DebuggerEvents.OnEnterBreakMode -= DebuggerEvents_OnEnterBreakMode;
Causes an exception:
Exception occured!
System.Runtime.InteropServices.InvalidComObjectException: COM object that has been separated from its underlying RCW cannot be used.
at System.StubHelpers.StubHelpers.StubRegisterRCW(Object pThis, IntPtr pThread)
at System.Runtime.InteropServices.UCOMIConnectionPoint.Unadvise(Int32 dwCookie)
at EnvDTE._dispDebuggerEvents_EventProvider.remove_OnEnterDesignMode(_dispDebuggerEvents_OnEnterDesignModeEventHandler A_1)
Any ideas will be welcome
Thanks!
Vitaly
Posting an answer that I got from MSDN forums, by Ryan Molden, in case it helps anyone:
I believe the problem here is how the
CLR handles COM endpoints (event
sinks). If I recall correctly when
you hit the
_applicationObject.Events.DebuggerEvents
part of your 'chain' the CLR will
create a NEW DebuggerEvents object for
the property access and WON'T cache
it, therefor it comes back to you, you
sign up an event handler to it (which
creates a strong ref between the
TEMPORARY object and your object due
to the delegate, but NOT from your
object to the temporary object, which
would prevent the GC). Then you don't
store that object anywhere so it is
immediately GC eligible and will
eventually be GC'ed.
I changed the code to store DebuggerEvents as a field and it all started to work fine.
Here is what #VitalyB means using code:
// list where we will place events.
// make sure that this variable is on global scope so that GC does not delete the evvents
List<object> events = new List<object>();
public void AddEvents(EnvDTE dte)
{
// create an event when a document is open
var docEvent = dte.Events.DocumentEvents;
// add event to list so that GC does not remove it
events.Add(docEvent );
docEvent.DocumentOpened += (document)=>{
Console.Write("document was opened!");
};
// you may add more events:
var commandEvent = dte.Events.CommandEvents;
events.Add(commandEvent );
commandEvent.AfterExecute+= etc...
}

Resources