Comments (18)
Let dmvcframework depends from Spring4d would be a huge change and huge dependency. We've to find a different approach.
from delphimvcframework.
Probably we could avoid the Spring4D dependency if we can take a Duck Typing approach. I.e. Support Lists by looking for some iteration interface, and Objects by scanning the properties.
I'd need to play around some more to see what is available.
from delphimvcframework.
The unit MVCFramework.DuckTyping.pas
already implement lot of Duck Typing oriented code and has been around for years. Maybe with some tweaks and we could use it for ViewData...
from delphimvcframework.
I'll add it to my todo list, to investigate.
Although I've probably promised my boss more than I can deliver :(
from delphimvcframework.
I've got some code in my toolbox that is a container that can handle both TObject derived instances as well as reference counted IInterface derived instances. As an aside, I've been using it to implement a class helper for TWebContext (so I can can have application specific pseudo properties anywhere the WebContext is available) by binding it to the CustomIntfObject property.
The interface for this container is below - note that it automatically increments and decrements the reference count for IInterface instances to prevent them going out of scope and thus keeps the auto memory management working correctly.
/// <summary>Helper container for MVC Context auxilliary objects</summary>
/// <remarks>Supports adding and retrieving both (classic) TObject and (reference counted) IInterface objects
/// The container will increment the reference count when item is added, and decrement when it is removed
///</remarks>
IWebContextObjectsDictionary = interface
['{B70454F5-DD2E-4727-9B3B-D910CEE113AC}']
procedure Add(const Key: string; const Value: TObject); overload;
procedure Add(const Key: string; const Value: IInterface); overload;
function TryGetValue(const Key: string; out Value: TObject): Boolean; overload;
function TryGetValue(const Key: string; out Value: IInterface): Boolean; overload;
procedure Remove(const Key: string);
end;
I could probably refactor this to be more general so it would then be suitable as the container for the ViewData, although as implemented it assumes ownership of the contained objects and will call their destructor as part of its owner destructor (this could be changed though).
from delphimvcframework.
The other way is to add an additional ViewData2 container that is generalised for reference counted instances and change the rendering code to iterate over both containers.
from delphimvcframework.
Either way will require a change to the TMVCBaseViewEngine class as this takes the AViewModel (type TMVCViewDataObject) in the constructor.
Either we change the type that is passed in, or we add an additional parameter.
Given that this change should be seamless to the user of the Framework (i.e. they should be fairly agnostic about what type of object is passed in) I'd be looking to implement the ViewModel with a container that can handle either type.
from delphimvcframework.
OK, so my first naive implementation just stands up a parallel ViewModel that has a TDictionary<string, IInterface> as the storage, and the Mustache renderer takes this as an additional param which it iterates over when preparing the models.
procedure TMVCMustacheViewEngine.PrepareModels;
// snip
begin
if Assigned(ViewModel) then
begin
for DataObj in ViewModel do
begin
// snip
end;
end;
// new code to process the IInterface instances
if Assigned(ViewModel2) then
begin
for DataObj2 in ViewModel2 do
begin
lList := TDuckTypedList.Wrap(TObject(DataObj2.Value));
if lList <> nil then
lJSON := lSer.SerializeCollection(DataObj2.Value)
else
lJSON := lSer.SerializeObject(DataObj2.Value);
if not lFirst then
lSJSON := lSJSON + ',';
lSJSON := lSJSON + '"' + DataObj2.Key + '":' + lJSON;
lFirst := False;
end;
end;
// resume existing code
if Assigned(ViewDataSets) then
begin
// snip
end;
// snip
end;
This renders out a Spring4D list with no references to that framework apart from in my controller unit. So we are not coupling DMVC with Spring4D !
Also I check for memory leaks and there are none - I was a little hesitant in casting the reference counted instance to a TObject so wanted to double check.
I would prefer to propose some changes that preserve the existing interface by modifying the underlying storage so look forward to your feedback when you have time to review.
from delphimvcframework.
Hmm, I wonder if I'm over complicating this LOL.
I've been back in the code and have just cast the Spring framework IList instance to a TObject and then added it to the ViewData.
So that code in the first post of this ticket now becomes
procedure TMovieController.GetMoviesPage;
begin
// get IList<TMovie> (from spring.collections.pas) and cast it as a vanilla Delphi object
ViewData['Movies'] := TObject(GetMovieService.ListAll);
LoadView(['Header', 'Movie', 'Footer']);
RenderResponseStream;
end;
// the Interfaced object returned by the "ListAll" method above will be out of scope by here so its destructor will be called via the reference counting mechanism
My next task will be looking at the MVCFramework.DataSet.Utils unit so I can write some code to output the TDataset as a Spring4D IList<T> instance. Modifying the TDataSetUtils.DataSetToObjectList<T> method to accept a duck typed list would enable standard Delphi lists as well as other lists that exhibit list like behaviour (specifically the ability to Add an object) to be used.
from delphimvcframework.
I've found that the TDataSetUtils class has been declared as sealed so this is making it hard to subclass for use with an interfaced list.
I might just clone the unit, unless there is a better idea.
from delphimvcframework.
If there is a good reason we can remove the sealed clause.
from delphimvcframework.
If there is a good reason we can remove the sealed clause.
Sorry, that was me posting from a work account. I'll take a look and see what duck type list changes would make it work with different lists.
from delphimvcframework.
I'm stuck between trying to add "generic" interfaced list functionality - I've looked at IMVCList (used by TDuckTypedList) but this does not support the IMVCList<T> generic functionality.
Or just run with a copy of the unit and rewrite to support Spring4D (so outside of a contribution) to this excellent library.
Option 1 is preferable from a community perspective, option 2 from a get-the-job-done perspective.
from delphimvcframework.
I know the feeling. Writing a framework is not like just "make it work" job.
In this case, we could follow the aproach of sample and contributed units. Then we could find the way to bring that funzionality in the core without adding dependencies.
from delphimvcframework.
Option 2 yields the following - unfortunately due to the lack of a generic version of TDuckTyped list I've need to replicate the DataSetToObjectList on the utility class. It also means I have pulled in reference to the Spring collections rather than being able to use TDuckTypedList.IsWrappedList to decide if the list can be wrapped so the Add method can be accessed.
You'll see I've commented the line where the item to be added to the list is created.
As an aside the inheritance of the class helper hides the method with the same name (overload does not fix the visibility)
unit uDatasetHelpers;
interface
uses
System.SysUtils,
System.Classes,
Data.DB,
System.Rtti,
MVCFramework.DataSet.Utils,
Spring, Spring.Collections, Spring.Collections.Lists;
type
TDataSetHelper2 = class helper(TDataSetHelper) for TDataSet
function AsObjectList<T: class, constructor>(CloseAfterScroll: boolean = false; OwnsObjects: boolean = true): IList<T>;
end;
TDataSetUtils2 = class // (TDataSetUtils)// sealed
public
class procedure DataSetToObjectList<T: class, constructor>(ADataSet: TDataSet; AObjectList: IList<T>;
ACloseDataSetAfterScroll: boolean = true);
end;
implementation
uses
System.Generics.Collections;
{ TDataSetHelper2 }
function TDataSetHelper2.AsObjectList<T>(CloseAfterScroll, OwnsObjects: boolean): IList<T>;
begin
Result := TCollections.CreateObjectList<T>(OwnsObjects);
try
TDataSetUtils2.DataSetToObjectList<T>(Self, Result, CloseAfterScroll);
except
Result := nil;
raise;
end;
end;
{ TDataSetUtils2 }
class procedure TDataSetUtils2.DataSetToObjectList<T>(ADataSet: TDataSet; AObjectList: IList<T>;
ACloseDataSetAfterScroll: boolean);
// NB: no functional change here - just a change of signature for the method
var
Obj: T;
SavedPosition: TArray<Byte>;
begin
ADataSet.DisableControls;
try
SavedPosition := ADataSet.Bookmark;
while not ADataSet.Eof do
begin
Obj := T.Create; // this requires a generic type
TDataSetUtils.DataSetToObject(ADataSet, Obj);
AObjectList.Add(Obj);
ADataSet.Next;
end;
if ADataSet.BookmarkValid(SavedPosition) then
ADataSet.Bookmark := SavedPosition;
finally
ADataSet.EnableControls;
end;
if ACloseDataSetAfterScroll then
ADataSet.Close;
end;
end.
from delphimvcframework.
Did you tried to just hard-cast the interface reference to TObject
?
Example:
uses
MVCFramework.DuckTyping;
{ snip snip snip }
function TWebSiteController.PeopleList: String;
var
LDAL: IPeopleDAL;
lPeople: TPeople;
lPeople2: IInterface;
begin
LDAL := TServicesFactory.GetPeopleDAL;
lPeople := LDAL.GetPeople;
try
lPeople2 := WrapAsList(LDAL.GetPeople); //just for test, lPeople2 is now an interface
ViewData['people'] := lPeople;
ViewData['people2'] := TObject(lPeople2); //having already an interface this should be enough
Result := GetRenderedView(['header', 'people_list', 'footer']);
finally
lPeople.Free;
end;
end;
from delphimvcframework.
ef6edd5 should already solve this issue - the only thing is that you cannot directly assign any interface to TValue but need to write ViewData['...'] := TValue.From(someInterface);
I don't know though if internally everything is set up to properly work with interfaces inside those TValue that are stored inside the viewdata.
from delphimvcframework.
It looks like there have been some major changes prior to the referenced commit, in particular line 236 of the MVCFramework.View.Renderers.Mustache unit cannot be compiled
lSer.TValueToJSONObjectProperty(lJSONModel, DataObj.Key, DataObj.Value, TMVCSerializationType.stDefault, nil, nil);
Is this part of a released branch or still in the development branch ?
from delphimvcframework.
Related Issues (20)
- Request for crud sample using ADO HOT 2
- [htmx-sample] Rename all the class helper method with an "HX" suffix to avoid confusion
- Add "MVCFromContentField" attribute
- Embeded e-Mail Sending System feature request
- JSON Serializing floating point properties. HOT 8
- Nested Mustache Partials Rendering HOT 4
- Error: No mapping for the Unicode character exists in the target multi-byte code HOT 4
- The TUserPasswordChecker class does not exist HOT 1
- Error when compile to Linux HOT 5
- Model with multiple Primary Key. HOT 4
- Swagger Param Body Array HOT 6
- ActiveRecord: TableName property readonly. HOT 9
- Querystring parameter is placed in context.request.body - but only on GET, DELETE and OPTIONS HOT 1
- Wrong MVCProduces attribute on TMVCJSONRPCController.GetPublishedMethodList
- FireDac Exception when running the articles_crud_server and articles_crud_vcl_client HOT 2
- [Question] Alternative to FireDac usage for RPC calls to DB Query HOT 1
- Where is the ObjectsMappers unit?
- MVCFramework.Signal not in package dmvcframeworkRT
- Use LIB suffix 120 in SwagDoc
- Apache routing broken HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from delphimvcframework.