Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
Discussion GroupsGeneralPHPASPPerlColdFusionFlashHTML, CSS, ScriptsBrowsers

Webmaster Forum / ColdFusion / Advanced Techniques / August 2005



Tip: Looking for answers? Try searching our database.

Problems accessing VARIABLES scope in CFC

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
mate of the state - 26 Aug 2005 15:54 GMT
I've got a little CFC that is basically like a cart. When trying to call the
"add" method via a URL, I get an error saying my storage variable is undefined
in the CFCs variables scope, even though I set it in the init() method. The
relevant code has been attached.

<!--- THE COMPONENT --->
<CFcomponent>
   
    <CFfunction name="init" output="false" returntype="coupon-book">
        <CFscript>
            variables.coupons = arrayNew(1);
            return this;
        </CFscript>
    </CFfunction>
   
    <CFfunction name="getMyCoupons" output="false" returntype="array">
        <CFreturn variables.coupons>
    </CFfunction>
   
    <CFfunction name="add" output="false" returntype="void" access="remote">
        <CFargument name="id" type="numeric" required="yes">
        <CFscript>
            var arrayList = arrayToList(getMyCoupons());
            if (not listFind(arrayList,arguments.id)) {
                variables.coupons = arrayAppend(getMyCoupons(),arguments.id);
            }
        </CFscript>
        <CFlocation url="#cgi.http_referer#" addtoken="no">
    </CFfunction>
   
</CFcomponent>

<!--- THE RELEVANT VIEW CODE --->
<CFif not isDefined('session.couponBook')>
    <CFset session.couponBook =
createObject('component',request.config.paths.cfcroot & 'coupon-book').init()>
</CFif>
BSterner - 26 Aug 2005 22:35 GMT
Am I understanding you right in that you're instantiating the object in the
session scope, and then trying to post to it using the "?method=add" url query
param?  If, so, I don't think that will work, because you're not posting to the
session object, but rather a new instance of it.
mate of the state - 26 Aug 2005 23:12 GMT
"Am I understanding you right in that you're instantiating the object in the
session scope, and then trying to post to it using the "?method=add" url query
param? If, so, I don't think that will work, because you're not posting to the
session object, but rather a new instance of it."

Yes, of course! Thank you for clearing that up.

Your suggested workaround #1 is what I was wanting to avoid. I'd never tried
to implement CFC methods via the query string before, so I wanted to try that,
but also any sort of handler page would be so small I wanted to avoid it.

I agree that #2 is bad design. This project is already impure enough, with
CFCs referencing properties and config values in the request scope.

I'm gettin' there... ;)
BSterner - 26 Aug 2005 22:50 GMT
Put another way, you can't do a form post to an object stored in a persistant
scope.  Only to pages or cfc files.  Couple workarounds.

a) Post to another component, page or the current page, and have it call the
session.couponBook method 'add', passing it the form data.

b) Have the cfc you're posting to check for the existence of the object in the
session scope and pass it along.  This is bad design if you ask me.
BSterner - 26 Aug 2005 23:36 GMT
As an ancillary note, you can also specify the method using the following...

<input type="hidden" name="method" value="myCFCMethodToPostTo" />

I personally, use a cfc controller that invokes the session object in the
servicing method of the form post.
mate of the state - 30 Aug 2005 20:41 GMT
Okay, I've moved the access to a 'helper' page as suggested.

However, now I'm getting type errors I don't follow. This 'cart' of mine holds
a single value which is a single-dimension array. On the CFCs init() method, I
initialize that value:

variables.coupons = arrayNew(1);

Later, the add() and delete() methods use a helper method, getMyCoupons(), to
get the current value of #variables.coupons# at any time.

The CFC init()s fine, but the first attempt to call getMyCoupons() throws an
exception saying the value returned is not of type array.

Attached is the updated CFC code. I can post the 'helper page' code if
necessary, though I don't know if it's relevant.

<CFcomponent>
   
    <CFfunction name="init" output="false" returntype="coupon-book">
        <CFscript>
            variables.coupons = arrayNew(1);
            return this;           
        </CFscript>
    </CFfunction>
   
    <CFfunction name="getMyCoupons" output="false" returntype="array">
        <CFreturn variables.coupons>
    </CFfunction>
   
    <CFfunction name="add" output="false" returntype="void">
        <CFargument name="id" type="numeric" required="yes">
        <CFscript>
            var arrayList = arrayToList(getMyCoupons());
            if (not listFind(arrayList,arguments.id)) {
                variables.coupons = arrayAppend(variables.coupons,arguments.id);
            }
        </CFscript>
        <CFlocation url="#cgi.http_referer#" addtoken="no">
    </CFfunction>
   
    <CFfunction name="delete" output="false" returntype="void">
        <CFargument name="id" type="numeric" required="yes">
        <CFscript>
        var currentCoupons = getMyCoupons();
        for (i=1; i lt arrayLen(currentCoupons); i=i+1) {
            if (arguments.id is i) {
                variables.coupons = arrayDeleteAt(variables.coupons,i);
            }
        }
        </CFscript>
        <CFlocation url="#cgi.http_referer#" addtoken="no">
    </CFfunction>
   
</CFcomponent>
BSterner - 31 Aug 2005 04:48 GMT
Please post your "helper" code.  The following returned an empty array ok for me.

<cfset obj = createObject('component', 'coupon-book').init() />
<cfdump var="#obj.getMyCoupons()#" />
mate of the state - 31 Aug 2005 05:32 GMT
The main view template receives a request like:

?action=add&coupon=3

Based on the action param, a method is called in the coupon-book. The error
WAS being thrown from the module invoked at the end of the attached code. I
reworked some things, and now no error is being thrown, but nothing is being
added to the array. I know it's hitting the method call because it is
redirecting back to the referer. I also did a CFabort in the method call to
make sure.

<CFparam name="url.action" default="">
<CFparam name="url.coupon" default="0">

<CFif not isNumeric(url.coupon) or url.coupon lt 0>
    <CFset url.action = ''>
</CFif>

<CFswitch expression="#url.action#">
    <CFcase value="add">
        <CFset session.couponBook.add(url.coupon)>
    </CFcase>
    <CFcase value="delete">
        <CFset session.couponBook.delete(url.coupon)>
    </CFcase>
</CFswitch>

<CFmodule
    template="/system/taglibs/coupons/coupon-book.cfm"
    standalone="true"
BKBK - 31 Aug 2005 08:38 GMT
Just two things.

1) For better or worse, I have the "I started, so I'll finish"  mentality.
I wouldn't do

<CFlocation url="#cgi.http_referer#" addtoken="no">

just before the finish-line, </cffunction>. Instead, I would cflocate
on the cfm page that invokes the cfc, giving the function the opportunity to
finish gracefully.

2) I find this construction, a recursive instantiation of a component within
itself, unnecessarily complicated:

<CFcomponent>
<CFfunction name="init" output="false" returntype="coupon-book">
<CFscript>
variables.coupons = arrayNew(1);
return this;           
</CFscript>
</CFfunction>
...
...etc.

I would replace this excerpt with

<CFcomponent>
<cfset variables.coupons = arrayNew(1)>

Wherever you need to have an instance of the component, you only have to
create an object, outside the component itself, the way BSterner does.

Good luck.
mate of the state - 31 Aug 2005 15:55 GMT
BK,

Re: 1)
You may have a good point there. It doesn't seem to matter for the code, but
there is an element of cleanliness I may look into further.

Re: 2)
The way CF parses non-method code within a CFC is not the same as a
constructor function and more importantly, it is an insufficient replacement.
If I want to customize new instances of an object, I need a method I can pass
parameters to. When the constructor code is as simple as mine, sure, you don't
NEED the constructor function, but it's part of my desire for consistency that
I do it.
Kronin555 - 31 Aug 2005 16:04 GMT
mate of the state,

Here's something you can check. You're storing the component on the session,
and you're using cflocations with addToken="no". Are you using cookies to track
the session? Are you sure you're getting the same instance of the component
when you come back and the coupon array is empty? Maybe you're getting a new
session, which is instantiating a new instance of CouponComponent, which has a
blank array...
mate of the state - 31 Aug 2005 17:46 GMT
Kronin,

Yes, I had assumed cookies were being set, but it seems maybe they aren't. If
I add a call to the add() method right after the coupon-book object is created
in Application.cfm, the first view shows the added value. So, I think you may
be on to something. How can I ensure I stay within a single session scope? I'd
prefer to avoid the dirty URL scenario.
BSterner - 31 Aug 2005 17:47 GMT
Originally posted by: Kronin555
mate of the state,

Are you sure you're getting the same instance of the component when you come
back and the coupon array is empty? Maybe you're getting a new session, which
is instantiating a new instance of CouponComponent, which has a blank array...

Put some kind of output in the following code to check that the coupon object
is not repeatedlty being recreated...

<!--- THE RELEVANT VIEW CODE --->
<CFif not isDefined('session.couponBook')>
    <CFset session.couponBook =
createObject('component',request.config.paths.cfcroot & 'coupon-book').init()>
    <b>New coupon session object created.</b>
</CFif>

If you see the 'New coupon session object created.' each time, then you've got
a problem w/your session storage.  Are all these files in the same directory?  
I'm assuming you're using the <cfapplication> tag w/i Application.cfm.
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.