-
Notifications
You must be signed in to change notification settings - Fork 2
/
AddNewDynamicItem.cs
211 lines (194 loc) · 8.72 KB
/
AddNewDynamicItem.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// Dynamic View Model (lists)
// Copyright (c) 2020 César Roberto de Souza. Licensed under the MIT license
// cesarsouza@gmail.com - http://crsouza.com
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.Json;
using System.Web;
using DynamicVML.Extensions;
using DynamicVML.Internals;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
namespace DynamicVML
{
/// <summary>
/// Represents the data object that the client can send to the server to request a new partial view for
/// a new list item using ajax. When the controller creates this new partial view, an instance of this
/// class will be stored in the <see cref="ViewDataDictionary">ViewData</see> object of the views under
/// the key <see cref="Constants.ItemCreateParameters"/>.
/// </summary>
///
/// <remarks>
/// <para>
/// An instance of this class should be specified as the single argument of the controller action
/// that should handle the creation of a new item in your application. In addition, your controller
/// action must end by calling one of the
/// <see cref="ControllerExtensions.PartialView(Controller, IDynamicList, AddNewDynamicItem)"/>
/// overloads that will take care of rendering the new item for you. An example can be seen below:
/// </para>
///
/// <code language="csharp">
/// [HttpGet]
/// public IActionResult AddBook(AddNewDynamicItem parameters)
/// {
/// var newBookViewModel = new BookViewModel()
/// {
/// Title = "New book",
/// PublicationYear = "1994"
/// };
///
/// return this.PartialView(newBookViewModel, parameters);
/// }
/// </code>
/// >[!NOTE]
/// Under normal circumstances, this class should never need to be instantiated directly
/// by your code as it is part of the inner workings of the library.
/// </remarks>
///
[Serializable]
public class AddNewDynamicItem
{
/// <summary>
/// Gets or sets the HTML div ID for the list whose new item should be created for.
/// </summary>
///
public string? ContainerId { get; set; }
/// <summary>
/// Gets or sets the <see cref="ListParameters.ItemTemplate"/> to be used when creating the new item.
/// </summary>
///
public string? ItemTemplate { get; set; }
/// <summary>
/// Gets or sets the <see cref="ListParameters.ItemContainerTemplate"/> to be used when creating the new item.
/// </summary>
///
public string? ItemContainerTemplate { get; set; }
/// <summary>
/// Gets or sets the <see cref="ListParameters.ListTemplate"/> to be used when creating the new item.
/// </summary>
///
public string? ListTemplate { get; set; }
/// <summary>
/// Gets or sets the HTML prefix to be used when creating the new item.
/// </summary>
///
public string? Prefix { get; set; }
/// <summary>
/// Gets or sets the <see cref="ListRenderMode"/> to be used when creating the new item.
/// </summary>
///
public ListRenderMode Mode { get; set; }
/// <summary>
/// Gets or sets any additional view data which may have been passed by the user when calling the
/// <see cref="EditorExtensions.ListEditorFor">Html.ListEditorFor</see> extension method, represented
/// by an UTF-8 byte array that can be serialized to JSON and posted back to the server.
/// </summary>
///
public byte[]? AdditionalViewData { get; set; }
/// <summary>
/// Creates a new instance of <see cref="AddNewDynamicItem"/>.
/// </summary>
///
public AddNewDynamicItem(string containerId, string listTemplate, string itemContainerTemplate,
string itemTemplate, string prefix, ListRenderMode mode, object? additionalViewData)
{
this.ContainerId = containerId;
this.ItemTemplate = itemTemplate;
this.ItemContainerTemplate = itemContainerTemplate;
this.ListTemplate = listTemplate;
this.Prefix = prefix;
this.Mode = mode;
if (additionalViewData != null)
this.AdditionalViewData = JsonSerializer.SerializeToUtf8Bytes(additionalViewData);
}
/// <summary>
/// Converts this instance to a GET query string representation that can be sent to the server.
/// </summary>
///
/// <remarks>
/// >[!WARNING]
/// Additional user data is never included in the query string. See
/// <see cref="EditorExtensions.ListEditorFor"/> for more details.
/// </remarks>
///
/// <returns>
/// An HTTP GET query string containing the key-value pairs of the properties of this class, e.g.:
/// <code language="html">
/// "?containerId=SgDdaDhJ&prefix=MyForm.MyProperty&ListTemplate=MyTemplate"
/// </code>
/// </returns>
///
public string ToQueryString()
{
if (!DisableTraceWarningsForQueryStringsThatContainAdditionalViewData && AdditionalViewData != null)
{
Trace.TraceWarning("DynamicVM: Additional view data cannot be sent over GET, so the data present " +
$"in {nameof(AddNewDynamicItem)} will be ignored. If you do not want this behavior, please " +
$"add 'method: POST' as an argument to the 'Html.ListEditorFor()' method.");
// let's just warn once, otherwise the log will be filled with messages
DisableTraceWarningsForQueryStringsThatContainAdditionalViewData = true;
}
return $"{nameof(ContainerId)}={ContainerId}"
+ $"&{nameof(ListTemplate)}={HttpUtility.UrlEncode(ListTemplate)}"
+ $"&{nameof(ItemContainerTemplate)}={HttpUtility.UrlEncode(ItemContainerTemplate)}"
+ $"&{nameof(ItemTemplate)}={HttpUtility.UrlEncode(ItemTemplate)}"
+ $"&{nameof(Prefix)}={Prefix}"
+ $"&{nameof(Mode)}={(int)Mode}";
}
/// <summary>
/// Converts this instance to a JSON representation that can be sent to the server.
/// </summary>
///
/// <returns>A JSON containing the key-value pairs of the properties of this class.</returns>
///
public string ToJSON()
{
return JsonSerializer.Serialize(this);
}
/// <summary>
/// Gets the additional view data at <see cref="AdditionalViewData"/> as a
/// <see cref="Dictionary{TKey, TValue}">Dictionary{string, string}</see>
/// containing the key-value pairs in the <see cref="AdditionalViewData"/>.
/// </summary>
///
/// <returns>
/// A <see cref="Dictionary{TKey, TValue}"/> representing the contents
/// of the <see cref="AdditionalViewData"/> property of this class.
/// </returns>
///
public Dictionary<string, string> GetAdditionalViewData()
{
if (AdditionalViewData == null)
return new Dictionary<string, string>();
var readOnlySpan = new ReadOnlySpan<byte>(AdditionalViewData);
var data = JsonSerializer.Deserialize<Dictionary<string, string>>(readOnlySpan);
if (data == null)
throw new Exception("Failed to deserialize additional view data.");
return data;
}
/// <summary>
/// Creates a new instance of the <see cref="AddNewDynamicItem"/> class. This
/// is an empty constructor that is necessary to be present in order for
/// <see cref="IModelBinder"/> deserialize objects of this class correctly.
/// </summary>
///
public AddNewDynamicItem()
{
// This empty constructor is required for ModelBinder to work properly
}
/// <summary>
/// Self explanatory.
/// </summary>
///
/// <remarks>
/// By default, the library will generate a <see cref="Trace.TraceWarning(string)"/> if you
/// specify any <c>additionalViewData</c> to the <see cref="EditorExtensions.ListEditorFor"/>
/// method while also specifying the HTTP method as <see cref="NewItemMethod.Get"/>. Setting
/// this static property to <c>false</c> will disable those warnings globally.
/// </remarks>
///
public static bool DisableTraceWarningsForQueryStringsThatContainAdditionalViewData { get; set; } = false;
}
}