Lotus nots script , assign to a field , parametric , using function - lotus-notes

I need to create a function and to pass a variable which represent a field on a document.
At the and in the same function I try to update the field , but is not work.
Actually , I need to remove a member from a list.
Table AllStringTable has the correct members but line
"varFieldToRemoveMembers = AllStringTable" does not update field.
What is missing ? Thanks to advice me.
Function funcRemoveMembersFromAField (varFieldToRemoveMembers As Variant , varFieldToRemoveMembersSTR As String , varMemberToRemove As Variant)
Dim TableIndex As Integer
Forall memebers In varFieldToRemoveMembers
If memebers <> "" Then
If varMemberToRemove = memebers Then
' do nothing
Else
Redim Preserve AllStringTable(TableIndex) As String
AllStringTable(TableIndex) = memebers
TableIndex = TableIndex + 1
End If
End If
End Forall
Set ReadersItem=note.GetFirstItem(varFieldToRemoveMembersSTR)
If Not ReadersItem Is Nothing Then
varFieldToRemoveMembers = AllStringTable
Call note.Save(True,False)
End If
End Function

You are not actually modifing the field (the ReadersItem). You are only changing your temporary variables.
Try something like this:
Set ReadersItem=note.GetFirstItem(varFieldToRemoveMembersSTR)
If Not ReadersItem Is Nothing Then
Call note.replaceItemValue(varFieldToRemoveMembersSTR, AllStringTable)
Call note.Save(True,False)
End If

Related

Class constructor confusion - wrong number of arguments or invalid property assignment

I'm having a class module with some data:
Private sharedFolders() As String
Public Property Let SetSharedFolders(val As String)
Dim i As Integer
sharedFolders = Array("folder one", "folder two")
i = UBound(sharedFolders)
i = UBound(sharedFolders)
ReDim Preserve sharedFolders(i)
sharedFolders(i) = CStr(val)
End Property
Property Get GetSharedFolders()
GetSharedFolders = sharedFolders()
End Property
And I want to add something to this property from other module like this:
Sub PrepareData()
Dim e
Dim s
Dim a(2) As String
Set e = New Entry
a(0) = "add one"
a(1) = "add two"
For Each s In a
e.SetSharedFolders (s) 'Here comes exception
Next
For Each s In e.GetSharedFolders
Debug.Print s
Next
End Sub
But I receive an "wrong number of arguments or invalid property assignment vba" exception... Can anyone assist?
Addendum
Thanks to #AJD and #Freeflow to pointing out a mistake and idea to make it easier. Decided to make as like below.
Class Module:
Private sharedFolders As New Collection
Public Property Let SetSharedFolders(val As String)
If sharedFolders.Count = 0 Then ' if empty fill with some preset data and add new item
sharedFolders.Add "folder 1"
sharedFolders.Add "folder 2"
sharedFolders.Add CStr(val)
Else
sharedFolders.Add CStr(val)
End If
End Property
Property Get GetSharedFolders() As Collection
Set GetSharedFolders = sharedFolders
End Property
and regular module:
Sub AddData()
Dim e As New Entry ' creating an instance of a class
Dim s As Variant ' variable to loop through collection
Dim a(1) As String 'some array with data to insert
a(0) = "add one"
a(1) = "add two"
For Each s In a
e.SetSharedFolders = s
Next
For Each s In e.GetSharedFolders
Debug.Print s
Next
End Sub
Initially I thought the problem lies in this code:
i = UBound(sharedFolders)
i = UBound(sharedFolders)
ReDim Preserve sharedFolders(i)
sharedFolders(i) = CStr(val)
i is set twice to the same value, and then the sharedFolders is reDimmed to the same value it was before! Also, there is some trickery happening with the use of ix within a 0-based array.
But the problem is most likely how you have declared your variables.
For Each s In a
e.SetSharedFolders (s) 'Here comes exception
Next
s is a Variant, and a is a Variant. At this point VBA is trying to guess how to handle a For Each loop with two Variants. And then the improper call is made. The correct syntax is:
e.SetSharedFolders s '<-- no parenthesis
There are plenty of posts on StackOverflow explaining how to call routines and what the impact of the evaluating parenthesis are!
However, at this point we are only assuming it is passing in a single element of the array - it could be passing the full array itself (albeit unlikely).
And the third factor -
Public Property Let SetSharedFolders(val As String)
The parameter val is being passed ByRef and should be passed ByVal. This also has unintended side effects as I found out (Type mismatch trying to set data in an object in a collection).
Public Property Let SetSharedFolders(ByVal val As String)
All in all you have the perfect storm of ambiguity driving to an unknown result.
The answer here is to strongly type your variables. This removes about two layers of ambiguity and areas where errors can happen. In addition, this will slightly improve code execution.
Another aspect is to understand when you should pass something ByVal and when to use the default (preferably explicitly) ByRef.
And a final gratuitous hint: Use a Collection instead of an Array. Your code you have implies a Collection will be more efficient and easier to manage.
Addendum
(thanks to #FreeFlow):
If the OP changes the definition of sharedfolders to Variant rather than String() then the array statement will work as expected.
The line e.SetSharedFolders (s) will work fine if it is changed to e.SetSharedFolders = s because the method SetSharedFolders is a Let Property not a Sub. There are other errors but these two changes will make the code run.

Exist function in dictionary doesn't work for an object as Key created in a class module

In my code, in need to store two value in my key to be able to do the analysis i require. And since I didn't want to store everything in an array, i decided to create an object with 2 parameters. But when i run the Exist function of the dictionary with that object "TwoInputs" as the type of Key, I always get that they Key doesn't exist. Can anyone help please?
I added "Option Compare Text" just in case but the exist still return False.
When I run the code line by line and force it to go the If "true" condition, a new Key is still created, don't know why.
This is the class module i created:
Private acc As Double
Private act As Variant
'Account property
Public Property Get Account() As Double
Account = acc
End Property
Public Property Let Account(Value As Double)
acc = Value
End Property
'Activity property
Public Property Get Activity() As Variant
Activity = act
End Property
Public Property Let Activity(Value As Variant)
act = Value
End Property
In a normal module i wrote a function to create a TwoInputs object based on two entries:
Public Function cTwoInputs(Account As Double, Activity As Variant) As TwoInputs
Set cTwoInputs = New TwoInputs
cTwoInputs.Account = Account
cTwoInputs.Activity = Activity
End Function
Then I create a sub where I want to add the 2 informations in a Key if they exist:
While dataSheet.Range("dataAgent").Offset(j, 0).Value <> "Project ID:" And dataSheet.Range("dataAgent").Offset(j, 0).Row <= lRow
If dataSheet.Range("dataAgent").Offset( j, 0).Value = "Activity ID:" Then
actName = dataSheet.Range("dataAgent").Offset(j, 1).Value
End If
If (dataSheet.Range("dataAgent").Offset(j, 0).Value = "XXXXX" Or dataSheet.Range("dataAgent").Offset(j, 0).Value = "") Then
KeyExist.Account = dataSheet.Range("dataAccount").Offset(j , 0).Value
KeyExist.Activity = actName
If dicBudget.Exists(KeyExist) Then
dicBudget(KeyExist) = dicBudget(KeyExist) + dataSheet.Range("dataBudget").Offset(j , 0).Value
Else
dicBudget.Add cTwoInputs(dataSheet.Range("dataAccount").Offset(j, 0).Value, actName), dataSheet.Range("dataBudget").Offset( j, 0).Value
End If
End If
j = j + 1
Wend
.Exists() method compares objects by their instances not by values of their fields.
So it's better to use primitive types like String, Integer, Double etc. as keys, and not to use custom object as a key. If you really need to use object-key for some purpose than you must be sure that you call .Exists() method on the same object you put to dictionary (e.g. by storing that key in a global variable).

VBA - Dictionary search - type mismatch

So I'm learning how to use Dictionaries but I have encountered a type problem. In the code below I get an error.
Type mismatch
Each time I try to call If Not target.SearchItem(sKey) Is Nothing Then. I want it to return a object then if it isn't Nothing I can convert it back to long.
For Each target In dList.getArray
For Each key In aList.getArray(1).getKeys
sKey = CStr(key)
'Error occurs here - as Type mismatch
If Not target.SearchItem(sKey) Is Nothing Then
cData.setData(sKey) = CLng(target.SearchItem(sKey))
target.removeData = sKey
Else
'if value doesn't exists it will just be 0
cData.setData(sKey) = 0
End If
Next key
Next target
data is my dictionary that is in a separate class:
Property Get SearchItem(name As String) As Variant
If data.exists(name) Then
'becomes a Variant/Integer - data(name) is a Variant/long
Set SearchItem = CVar(data(name))
Else
'Should return Nothing if item doesnt exist
Set SearchItem = Nothing
End If
End Property
Update: To explain the problem I bit more. Even though I return it as a variant it will still be partly Integer and therefor If Not target.SearchItem(sKey) Is Nothing Then will return mismatch as it needs an object and VBA doesn't read is as a variant or something. Is there anything like Nothing, null or equally that works for a long? That would solve the problem.
This code below returns a long as I want it to, but I can't use -99 as it would corrupt the data analyse. It needs to be something that isn't a value
Property Get SearchItem(name As String)
If data.exists(name) Then
SearchItem = data(name)
Else
'SearchItem = Nothing
SearchItem = -99
End If
End Property
If Not target.SearchItem(sKey) Is Nothing Then
This implies that the stored item is an Object
cData.setData(sKey) = CLng(target.SearchItem(sKey))
The only way that this line will work on an Object is that the Object has a default value that can be converted to long. If the Object's default value is returning a value that can be converted to long.
Does Clng(Object) work on the actual Object that is being stored in the Dictionary?
If you are storing mixed data type then SearchItem check if the return data is an Object.
Property Get SearchItem(name As String) As Variant
If isObject(data(name)) Then
Set SearchItem = data(name)
Else
SearchItem = data(name)
End If
End Property
I would not convert the datatype in this method. I would instead create one or more separate methods or convert the data type at the point of use.
Function getTypedItem(Key as Variant, DataType as VbVarType) as Variant
IsNumeric(Obj)
Since you are using actual Keys of the Dictionary we know that it will return something and there is not a need for .Exists in this context. What you will need to do is test if it is returning an Object before you test whether the Object Is Nothing.
If IsObject(Target.SearchItem(sKey)) Then
If IsNumeric(Target.SearchItem(sKey)) Then
cData.setData(sKey) = CLng(Target.SearchItem(sKey))
End If
ElseIf IsNumeric(Target.SearchItem(sKey)) Then
cData.setData(sKey) = CLng(Target.SearchItem(sKey))
End If
I don't think you can use Set to assign an integer, so the property will fail when the dictionary item is an Integer. Perhaps use:
Property Get SearchItem(name As String) As Variant
If data.exists(name) Then
'becomes a Variant/Integer - data(name) is a Variant/long
SearchItem = data(name)
...
Unfortunately, VBA does not have nullable primitive types such as a nullable Long. A usual workaround is to use a Variant, which can be assigned the value of Empty and which can also hold a Long natively.
I would rewrite your property as follows:
Property Get SearchItem(name As String) As Variant
If Data.exists(name) Then
SearchItem = Data(name)
Else
SearchItem = Empty
End If
End Property
And the test for empty would be:
If Not IsEmpty(target.SearchItem(sKey)) Then ...

VBA Function - Argument Not Optional

Public Function RETURN_Equipment(Optional category As String) As Collection
Dim config As classConfiguration
Set config = New classConfiguration
Dim item As classItem
Set item = New classItem
Dim myCollection As Collection
Set myCollection = New Collection
For Each config In Configurations
For Each item In config.colItems
If IsMissing(category) Then
myCollection.add item
ElseIf InStr(category, "mainframe") <> 0 And item.category = "mainframe" Then
myCollection.add item
MsgBox "Fired!"
ElseIf category = "accessory" And item.category = "accessory" Then
Else
End If
Next
Next
RETURN_Equipment = myCollection
End Function
I keep getting
Compile error:
Argument not optional
I get the error on the last line
RETURN_Equipment = myCollection
I understand the error message, its telling me I did not fill out a parameter. But I only have one parameter, and I've declared it optional. It looks like the code thinks I'm trying to call the function from the function?
What gives?
Anytime you assign an object you need to use the set keyword.
set RETURN_Equipment = myCollection
I was getting this error because I was using the wrong function name when trying to return a result from a function. I was doing this:
Function MyFuncA(arg as String)
MyFuncB = arg 'The problem is I'm using MyFuncB instead of MyFuncA
End Function
This happened because I copied a function from somewhere else and changed the name, but not the return statement. This is not the OP's problem, but I was getting the same error message.
Because you've specified the Optional Parameter as a string it will default to an empty string if you've not specified a value.
This means it can't be missing
If you'd specified it as
Public Function RETURN_Equipment(Optional category) As Collection
It would be a variant and that could be missing, although you'd also be able to mess things up by passing non string variants as the category parameter
The best course of action is probably to replace
If IsMissing(category) Then
with
If category = "" Then
And as Brad has pointed out you'll need to use Set
Set RETURN_Equipment = myCollection
For full details check this
http://msdn.microsoft.com/en-us/library/office/gg251721%28v=office.15%29.aspx

How to create array of lists in LotusScript

I have code like this:
Dim MyACL As Variant
Dim Person As List
Redim MyACL(0)
Person("Detail1") = "Something1"
.
.
.
Person(Detailx") = "Somethingx"
ForAll name in names
ReDim Preserve MyAcl(Ubound(MyACL)+1)
Person("Name") = name
MyACL = ArrayAppend(MyACL,Person)
End ForAll
It throws error "Type Mismatch". Do you know, how to create an array of lists? Thank you.
This is a typical example of when you want to use a class instead, and create an array of that class. That class, in turn can contain a list (as well as other things). Can be very powerful!
Updated:
The benefit of using a class is that you can add business logic in the class, and it is very easy to extend it with more functionality later. Below is an example, based on the question above, but with additional functionality.
Class PersonObject
Public PersonAttribute List As String
Public NABdoc As NotesDocument
Public PersonName As String
Public Sub New(personname As String)
Dim nab as New NotesDatabase("Server/Domain","names.nsf")
Dim view as NotesView
'*** Get person document from Domino directory
Set view = nab.GetView("PeopleByFirstName")
Set me.NABdoc = view.GetDocumentByKey(personname)
'*** Set person name in object
me.PersonName = personname
'*** Set some values from person doc
me.PersonAttribute("Email") = GetValue("InternetAddress")
me.PersonAttribute("Phone") = GetValue("OfficePhone")
End Sub
Public Function GetValue(fieldname as String) as String
GetValue = me.NABdoc.GetItemValue(fieldname)(0)
End Function
Public Sub AddAttribute(attributename as String, value as string)
me.PersonAttribute(attributename) = value
End Sub
End Class
You can now very easily build you a list, using this class (and assuming that names is a list of unique names):
Dim person List As PersonObject
Dim personname As String
ForAll n in names
'*** Create person object based on name
person(n) = New PersonObject(n)
'*** Store additional info about this person
person.AddAttribute("Age","35")
End ForAll
Hopefully this gives you an idea of what you can do with classes.
You can also take a look at the following two blog entries about the basics of object oriented Lotusscript:
http://blog.texasswede.com/object-oriented-lotusscript-for-beginners-part-1/
http://blog.texasswede.com/object-oriented-lotusscript-for-beginners-part-2/
If you explicitely declare a variable as Array (as you do in your Redim Statement), then it can not be "reassigned" using arrayappend.
And it is NOT necessary to do it that way. just replace the line MyACL = ArrayAppend(MyACL,Person) with MyACL(Ubound(MyACL)) = Person
Take care: With that example code you will never fill MyACL(0) as the first Element filled is MyACL(1)
To begin filling the array with element 0 the code needs to be changed like this:
Dim max As Integer
max = 0
ForAll thisName In names
ReDim Preserve MyAcl(max)
Person("Name") = thisName
MyACL(max) = Person
max = max + 1
End ForAll
BUT: I don't know, if this is a good idea, as you can not access the "Detail1- Property" of Person directly.
Something like
detail = MyACL(1)("Detail1")
is not possible. You always have to have a temporary variable like this:
person = MyACL(1)
detail = person("Detail1")

Resources