Action methods not shown in the object UI

May 14, 2013 at 3:51 AM
Hi Guys,
            I am unable to get my action methods to be shown on an object. I should use action methods as these methods are only used by this object.  
Your troubleshooting says

_Naked Objects does not recognise any overloaded methods as actions. (This is because the various overloaded versions would show up on the menu with the same label.)

Naked Objects does not recognise templated methods as actions.

Naked Objects only recognises as actions those methods with parameter types that are either recognised value types or domain objects. It does not recognise, for example, a method with parameter(s) that are enumerations.
_

Here are my methods.

[MemberOrder(10)]
    [Description("Set Next Practitioner")]
    public virtual void SetNextPractitioner()
    {
        SelectedPractitioner = (Practitioner)PractitionerService.GetNextPractitioner();
    }

    [MemberOrder(11)]
    [Description("Set Spectific Practitioner")]
    public virtual void SetSpectificPractitioner(Practitioner practitioner)
    {
        SelectedPractitioner = practitioner;
    }
And here is all of my code for this object.

using System;
using System.ComponentModel.DataAnnotations;
using System.Security.Cryptography;
using System.Text;
using NakedObjects;
using System.ComponentModel;
using Lansw.Uwas.Model.Enums;
using Lansw.Uwas.Interfaces;
using System.Collections.Generic;

namespace Lansw.Uwas.Model
{
//[IconName("cellphone.png")]
[IconName("default.png")]
public class WorkOffer : BaseDomainObject
{       
    #region Title       

    public override string ToString()
    {
        var t = new TitleBuilder();

        if (SelectedPractitioner == null)
        {
            t.Append("New Work Offer");
        }            
        else
        {
            t.Append("Work Offer For");
            t.Append(SelectedPractitioner);
        }
        return t.ToString();
    }

    #endregion Title

    #region Injected Services

    // This region should contain properties to hold references to any services required by the
    // object.  Use the 'injs' shortcut to add a new service.
    public WorkOfferRepository WorkOfferRepository { set; protected get; }
    public WorkOfferAuditRepository WorkOfferAuditRepository { set; protected get; }
    public IPractitionerService PractitionerService { set; protected get; }

    #endregion Injected Services

    #region Life Cycle Methods

    public virtual void Created() 
    {
        MethodOfOffer = MethodOfOffer.NextPractioner;
        OfferState = OfferState.Pending;
        ResetMethodOfOfferSpecificCircumstances();            
    }

    private void ResetMethodOfOfferSpecificCircumstances()
    {
        if (MethodOfOffer == MethodOfOffer.NextPractioner)
        {
            MethodOfOfferSpecificCircumstances = "NA";
        }            
    }

    public override void Persisting()
    {
        base.Persisting();
        ResetMethodOfOfferSpecificCircumstances();
    }

    public override void Updating()
    {
        base.Updating();

        ResetMethodOfOfferSpecificCircumstances();

    }

    public void Persisted()
    {
       AuditWorkOfferChange(this);
    }

    public void Updated()
    {
        AuditWorkOfferChange(this);
    }

    private void AuditWorkOfferChange(WorkOffer workOffer)
    {
        var workOfferAudit = Container.NewTransientInstance<WorkOfferAudit>();

        workOfferAudit.WorkOffer = workOffer;
        workOfferAudit.NewOfferState = workOffer.OfferState;            

        Container.Persist(ref workOfferAudit);     
    }

    #endregion Life Cycle Methods

    #region properties

    [Hidden]        
    public virtual int WorkOfferID { get; set; }        
    [MemberOrder(1), DisplayName("Legal Aid Office")]
    public virtual string LegalAidOffice { get; set; }
    [MemberOrder(2), DisplayName("Court Venue")]
    public virtual string CourtVenue  { get; set; }
    [MemberOrder(3), DisplayName("Type Of Offer")]
    public virtual TypeOfOffer TypeOfOffer { get; set; }
    [MemberOrder(4), DisplayName("Court Date")]     
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
    public virtual DateTime CourtDate  { get; set; }
    [MemberOrder(5), DisplayName("Person Making Offer")]
    public virtual string Offerer { get; set; }
    [MemberOrder(6), DisplayName("Method Of Offer")]
    public virtual MethodOfOffer MethodOfOffer { get; set; }

    [MemberOrder(7), DisplayName("Exceptional Circumstances Requiring An offer To A Specific Practitioner")]       
    public virtual string MethodOfOfferSpecificCircumstances { get; set; }

    [MemberOrder(8), DisplayName("Specific Practitioner Details")]
    //public virtual IPractitioner SelectedPractitioner { get; set; }        
    public virtual IPractitioner SelectedPractitioner { get; private set; }
    [MemberOrder(9), DisplayName("Work Offer State")]
    public virtual OfferState OfferState { get; set; }

    #region Row Guid and Modified Date

    #region rowguid

    [Hidden]
    public override Guid rowguid { get; set; }

    #endregion rowguid

    #region ModifiedDate

    //[MemberOrder(99)]
    [Disabled]
    [ConcurrencyCheck] // This property will be used for concurrency checking i.e. to check if another user has updated
                       // the object before this copy of the object can be saved
    public override DateTime ModifiedDate { get; set; }

    #endregion ModifiedDate

    #endregion Row Guid and Modified Date
    #endregion properties

    #region Defaults

    public OfferState DefaultOfferState()
    {
        return OfferState.Pending;
    }

    public MethodOfOffer DefaultMethodOfOffer()
    {
        return MethodOfOffer.NextPractioner;
    }

    public TypeOfOffer DefaultTypeOfOffer()
    {
        return TypeOfOffer.Duty;
    }

    #endregion Defaults

    //public List<OfferState> ChoicesOfferState()
    //{
    //    new List<int>(Enum.GetValues(OfferState));
    //    //List<OfferState> var = new Enum.GetValues(OfferState).;

    //}

    #region Validate Methods

    public string ValidateMethodOfOfferSpecificCircumstances(string newMethodOfOfferSpecificCircumstances)
    {
        if (MethodOfOffer == MethodOfOffer.SpecificPractioner)
        {
            if ((newMethodOfOfferSpecificCircumstances == "NA") || (newMethodOfOfferSpecificCircumstances.Length == 0))
            {
                return "A reason for the selected practitioner must be specified";
            }
        }
        return null;
    }

    #endregion Validate Methods

    #region Actions

    [MemberOrder(10)]
    [Description("Set Next Practitioner")]
    public virtual void SetNextPractitioner()
    {
        SelectedPractitioner = (Practitioner)PractitionerService.GetNextPractitioner();
    }

    [MemberOrder(11)]
    [Description("Set Spectific Practitioner")]
    public virtual void SetSpectificPractitioner(Practitioner practitioner)
    {
        SelectedPractitioner = practitioner;
    }

    // Use 'hide', 'dis', 'val', 'actdef', 'actcho' shortcuts to add supporting methods here.
    #endregion Actions       
}
}
Editor
May 14, 2013 at 5:38 AM
I suspect that they aren't being picked up because they begin with "Set".

Action methods are those public methods that do not have a recognized prefix, eg Validate, Disable, Hide, Choices, Default etc.

I suspect that Get and Set are also in this list, dating back to the days when NO was written in Java/J#, and supported Java-style getters and setters rather than C# properties.

The fix is easy: rename the methods.

If you want them to be called "Set..." in the UI, just use the [Named] annotation.

Cheers
Dan


Coordinator
May 14, 2013 at 6:19 AM
You can have actions that begin with 'Get' or 'Set'. I just tried this out - because it is not something I would normally do, perhaps because of very old Java habits - and Dan's response had cast a seed of doubt in my mind,. But I confirmed that I did get an action SetFoo() on both a service and a domain object.

When you say that your actions aren't showing, do you mean:
  • There's no Actions menu on the object?
  • There's an Actions menu but it is greyed-out (i.e. does not produce a pop-down menu)?
Please also confirm you are not using a custom view for this object - and/or that you're not inadvertently hiding the action(s) with .css.
Editor
May 14, 2013 at 6:40 AM



May 14, 2013 at 8:04 AM
Thanks guys I tried changing the method names as Dan said but they are still now showing.

#region Actions
    [MemberOrder(10)]
    [Description("Set Next Practitioner")]
    [Named("Set Next Practitioner")]
    public virtual void AssignNextPractitioner()
    {
        SelectedPractitioner = (Practitioner)PractitionerService.GetNextPractitioner();
    }

    [MemberOrder(11)]
    [Description("Set Specific Practitioner")]
    [Named("Set Specific Practitioner")]
    public virtual void AssignSpecificPractitioner(Practitioner practitioner)
    {
        SelectedPractitioner = practitioner;
    }
I do not see any Action Menu at all on the object, maybe you can send me a picture of what I should see. I can't find any in the sample app,

Regards,
             Alistair
May 14, 2013 at 8:05 AM
And I dont have any custom views yet
Coordinator
May 14, 2013 at 8:13 AM
You didn't answer my question about whether the Actions menu is in fact showing on the object (i.e. underneath the title of the object) or not.
May 14, 2013 at 8:19 AM
Edited May 14, 2013 at 8:20 AM
You didn't answer my question about whether the Actions menu is in fact showing on the object (i.e. underneath the title of the object) or not.
Did :)
I do not see any Action Menu at all on the object, maybe you can send me a picture of what I should see. I can't find any in the sample app,
Coordinator
May 14, 2013 at 8:55 AM
I would suggest you watch at least the first of these videos (http://www.nakedobjects.net/video/video_list.shtml) - Running the sample application. That shows the ui running, actions being invoked etc.
May 14, 2013 at 12:35 PM
Thanks guys, yes I am definitely not seeing the action menus on the object, as shown in the sample.
Coordinator
May 14, 2013 at 12:45 PM
Please post a screenshot of how your object is being rendered on screen.
Coordinator
May 14, 2013 at 12:52 PM
A further thought - which the screenshot would answer - is your object persisted? The action menu does not appear on an object that is transient - because there is no object yet (on the server) for the action to act on. Nor does the actions menu appear on a persisted object in edit mode.
May 14, 2013 at 1:18 PM
I was looking on the screen in edit mode but they didn't appear once it was persisted either.
However I wont be able to use action methods then as the user will want to set this property before the object has been saved.
So I will have to go back to setting this value with a property that will use contributed actions to find the object to set the property to.

Thanks again!
May 14, 2013 at 1:26 PM
If they are just object methods why should you have to have a persisted object and why can't you execute a method in edit mode?

regards,
           Alistair
Coordinator
May 14, 2013 at 1:43 PM
Naked Objects MVC (and the Restful Objects server) adopt the 'stateless architecture' now common in web applications. All Actions are executed on the server, so there has to be an object that the server can retrieve and act upon. The reason not to offer actions on a persisted object in edit mode had partly to do with technical limitations, and partly because of ambiguity about whether you were acting on the object's state as it exists on the server or as edited on the screen.

I am not saying you couldn't design a framework that worked differently - but that is the way the NOF works.