99using System . Diagnostics ;
1010using System . Linq ;
1111using System . Runtime . CompilerServices ;
12+ using DotVVM . Framework . Utils ;
1213
1314namespace DotVVM . Framework . Controls
1415{
@@ -145,16 +146,19 @@ protected override void RenderBeginTag(IHtmlWriter writer, IDotvvmRequestContext
145146 {
146147 TagName = WrapperTagName ;
147148
148- var ( bindingName , bindingValue ) = RenderOnServer ?
149- ( "dotvvm-SSR-foreach" , GetServerSideForeachBindingGroup ( ) ) :
150- GetForeachKnockoutBindingGroup ( context ) ;
151- if ( RenderWrapperTag )
149+ if ( GetValueBinding ( DataSourceProperty ) is { } )
152150 {
153- writer . AddKnockoutDataBind ( bindingName , bindingValue ) ;
154- }
155- else
156- {
157- writer . WriteKnockoutDataBindComment ( bindingName , bindingValue . ToString ( ) ) ;
151+ var ( bindingName , bindingValue ) = RenderOnServer ?
152+ ( "dotvvm-SSR-foreach" , GetServerSideForeachBindingGroup ( ) ) :
153+ GetForeachKnockoutBindingGroup ( context ) ;
154+ if ( RenderWrapperTag )
155+ {
156+ writer . AddKnockoutDataBind ( bindingName , bindingValue ) ;
157+ }
158+ else
159+ {
160+ writer . WriteKnockoutDataBindComment ( bindingName , bindingValue . ToString ( ) ) ;
161+ }
158162 }
159163
160164 if ( RenderWrapperTag )
@@ -165,7 +169,7 @@ protected override void RenderBeginTag(IHtmlWriter writer, IDotvvmRequestContext
165169
166170 private KnockoutBindingGroup GetServerSideForeachBindingGroup ( ) =>
167171 new KnockoutBindingGroup {
168- { "data" , GetForeachDataBindExpression ( ) . GetKnockoutBindingExpression ( this ) }
172+ { "data" , TryGetKnockoutForeachingExpression ( ) . NotNull ( ) }
169173 } ;
170174
171175 private ( string bindingName , KnockoutBindingGroup bindingValue ) GetForeachKnockoutBindingGroup ( IDotvvmRequestContext context )
@@ -174,7 +178,7 @@ private KnockoutBindingGroup GetServerSideForeachBindingGroup() =>
174178 var value = new KnockoutBindingGroup ( ) ;
175179
176180
177- var javascriptDataSourceExpression = GetForeachDataBindExpression ( ) . GetKnockoutBindingExpression ( this ) ;
181+ var javascriptDataSourceExpression = TryGetKnockoutForeachingExpression ( ) . NotNull ( ) ;
178182 value . Add (
179183 useTemplate ? "foreach" : "data" ,
180184 javascriptDataSourceExpression ) ;
@@ -204,7 +208,6 @@ private KnockoutBindingGroup GetServerSideForeachBindingGroup() =>
204208 /// </summary>
205209 protected override void RenderContents ( IHtmlWriter writer , IDotvvmRequestContext context )
206210 {
207- Debug . Assert ( ( clientSideTemplate == null ) == this . RenderOnServer ) ;
208211 if ( clientSideTemplate == null )
209212 {
210213 Debug . Assert ( clientSeparator == null ) ;
@@ -223,19 +226,18 @@ protected override void RenderEndTag(IHtmlWriter writer, IDotvvmRequestContext c
223226 {
224227 base . RenderEndTag ( writer , context ) ;
225228 }
226- else
229+ else if ( GetValueBinding ( DataSourceProperty ) is { } )
227230 {
228231 writer . WriteKnockoutDataBindEndComment ( ) ;
229232 }
230233
231234 emptyDataContainer ? . Render ( writer , context ) ;
232235 }
233236
234- private DotvvmControl GetEmptyItem ( IDotvvmRequestContext context )
237+ private DotvvmControl GetEmptyItem ( IDotvvmRequestContext context , IStaticValueBinding dataSourceBinding )
235238 {
236239 if ( emptyDataContainer == null )
237240 {
238- var dataSourceBinding = GetDataSourceBinding ( ) ;
239241 emptyDataContainer = new EmptyData ( ) ;
240242 emptyDataContainer . SetValue ( EmptyData . RenderWrapperTagProperty , GetValueRaw ( RenderWrapperTagProperty ) ) ;
241243 emptyDataContainer . SetValue ( EmptyData . WrapperTagNameProperty , GetValueRaw ( WrapperTagNameProperty ) ) ;
@@ -247,24 +249,25 @@ private DotvvmControl GetEmptyItem(IDotvvmRequestContext context)
247249 }
248250
249251 private ConditionalWeakTable < object , DataItemContainer > childrenCache = new ConditionalWeakTable < object , DataItemContainer > ( ) ;
250- private DotvvmControl GetItem ( IDotvvmRequestContext context , object ? item = null , int ? index = null , bool allowMemoizationRetrieve = false , bool allowMemoizationStore = false )
252+ private DotvvmControl GetItem ( IDotvvmRequestContext context , object ? item = null , int ? index = null , bool serverOnly = false , bool allowMemoizationRetrieve = false , bool allowMemoizationStore = false )
251253 {
252254 if ( allowMemoizationRetrieve && item != null && childrenCache . TryGetValue ( item , out var container2 ) && container2 . Parent == null )
253255 {
254256 Debug . Assert ( item == container2 . GetValueRaw ( DataContextProperty ) ) ;
255- SetUpServerItem ( context , item , ( int ) index ! , container2 ) ;
257+ SetUpServerItem ( context , item , ( int ) index ! , serverOnly , container2 ) ;
256258 return container2 ;
257259 }
258260
259261 var container = new DataItemContainer ( ) ;
260262 container . SetDataContextTypeFromDataSource ( GetBinding ( DataSourceProperty ) ! ) ;
261263 if ( item == null && index == null )
262264 {
265+ Debug . Assert ( ! serverOnly ) ;
263266 SetUpClientItem ( context , container ) ;
264267 }
265268 else
266269 {
267- SetUpServerItem ( context , item ! , ( int ) index ! , container ) ;
270+ SetUpServerItem ( context , item ! , ( int ) index ! , serverOnly , container ) ;
268271 }
269272
270273 ItemTemplate . BuildContent ( context , container ) ;
@@ -297,24 +300,28 @@ private void SetChildren(IDotvvmRequestContext context, bool renderClientTemplat
297300 clientSeparator = null ;
298301 clientSideTemplate = null ;
299302
300- if ( DataSource != null )
303+ var dataSource = GetIEnumerableFromDataSource ( ) ;
304+ var dataSourceBinding = GetDataSourceBinding ( ) ;
305+ var serverOnly = dataSourceBinding is not IValueBinding ;
306+
307+ if ( dataSource != null )
301308 {
302309 var index = 0 ;
303- foreach ( var item in GetIEnumerableFromDataSource ( ) ! )
310+ foreach ( var item in dataSource )
304311 {
305312 if ( SeparatorTemplate != null && index > 0 )
306313 {
307314 Children . Add ( GetSeparator ( context ) ) ;
308315 }
309- Children . Add ( GetItem ( context , item , index ,
316+ Children . Add ( GetItem ( context , item , index , serverOnly ,
310317 allowMemoizationRetrieve : context . IsPostBack && ! memoizeReferences , // on GET request we are not initializing the Repeater twice
311318 allowMemoizationStore : memoizeReferences
312319 ) ) ;
313320 index ++ ;
314321 }
315322 }
316323
317- if ( renderClientTemplate )
324+ if ( renderClientTemplate && ! serverOnly )
318325 {
319326 if ( SeparatorTemplate != null )
320327 {
@@ -326,7 +333,7 @@ private void SetChildren(IDotvvmRequestContext context, bool renderClientTemplat
326333
327334 if ( EmptyDataTemplate != null )
328335 {
329- Children . Add ( GetEmptyItem ( context ) ) ;
336+ Children . Add ( GetEmptyItem ( context , dataSourceBinding ) ) ;
330337 }
331338 }
332339
@@ -337,10 +344,11 @@ private void SetUpClientItem(IDotvvmRequestContext context, DataItemContainer co
337344 container . SetValue ( Internal . ClientIDFragmentProperty , this . GetIndexBinding ( context ) ) ;
338345 }
339346
340- private void SetUpServerItem ( IDotvvmRequestContext context , object item , int index , DataItemContainer container )
347+ private void SetUpServerItem ( IDotvvmRequestContext context , object item , int index , bool serverOnly , DataItemContainer container )
341348 {
342349 container . DataItemIndex = index ;
343350 container . DataContext = item ;
351+ container . RenderItemBinding = ! serverOnly ;
344352 container . SetValue ( Internal . PathFragmentProperty , GetPathFragmentExpression ( ) + "/[" + index + "]" ) ;
345353 container . ID = index . ToString ( ) ;
346354 }
0 commit comments