I'm building a custom page caching utility that uses a syntax like {Substitution:GetNonCachedData} to get data that's not supposed to be cached. The solution is very similar to the built-in <# OutputCache %> stuff but not as flexible (I don't need it to be) and, most importantly, allows the session state to be available when retrieving non-cached data.
Anyway, I have a method that replaces the tokens in the html with the result of the static method named in the {Substitution} tag.
For example my page:
<html>
<body>
<p>This is cached</p>
<p>This is not: {Substitution:GetCurrentTime}</p>
</body>
</html>
will fill in the {Substitution:GetCurrentTime} with results of a static method. Here's where the processing happens:
private static Regex SubstitutionRegex = new Regex(#"{substitution:(?<method>\w+)}", RegexOptions.IgnoreCase);
public static string WriteTemplates(string template)
{
foreach (Match match in SubstitutionRegex.Matches(template))
{
var group = match.Groups["method"];
var method = group.Value;
var substitution = (string) typeof (Substitution).GetMethod(method).Invoke(null, null);
template = SubstitutionRegex.Replace()
}
return template;
}
the variable template is the html with custom tokens in it that need to be replaced. The problem with this method is that everytime I update the template variable with the updated html the match.Index variable no longer points to the right character start because of the template now has more characters added to it.
I can come up with a solution that works by either counting characters etc or some other screwball way of doing it, but I first want to make sure there's not some easier way to achieve this with the Regex object. Anyone know how to this?
thanks!
You should call the overload of Regex.Replace that takes a MatchEvaluator delegate.
For example:
return SubstitutionRegex.Replace(template, delegate(Match match) {
var group = match.Groups["method"];
var method = group.Value;
return (string) typeof (Substitution).GetMethod(method).Invoke(null, null);
});
Instead of using Matches and looping on the results, set the regex to compiled and use a single Match in a while loop until it stops matching.
Related
I try to pass an object to th:onclick.
When I pass a string(afficherDetails() function), everything is ok
When I pass an object(afficherDetails2() function), in the called function the object seems ok but it is empty.
function afficherDetails(employee) {
console.log("afficher Details");
document.getElementById("detailledFirstNameDataLabelId").textContent = employee.firstName;
document.getElementById("detailledLastNameDataLabelId").textContent = employee.lastName;
document.getElementById("detailledAddressDataLabelId").textContent = employee.address;
document.getElementById("detailledTitleDataLabelId").textContent = employee.title;
document.getElementById("detailledManagerDataLabelId").textContent = employee.manager;
}
function afficherDetails2(name) {
console.log("afficher Details");
document.getElementById("detailledFirstNameDataLabelId").textContent = name;
}
<td><button th:data-parameter1="${employee}" th:onclick=" afficherDetails(this.getAttribute('data-parameter1')) ">details</button></label></td>
<!--td><button th:data-parameter1="${employee.firstName}" th:onclick=" afficherDetails2(this.getAttribute('data-parameter1')) ">details</button></label></td-->
</tr>
Is it a correct behavior ? Can't we pass a complex object and we can only pass simple object?
thanks for your answer
Short answer:
You can pass a complex object to a HTML attribute - but it will be reduced to a string by the object's toString() method.
Therefore, in your case, an attempt to do the following in JavaScript...
var something = employee.firstName;
...will do nothing because the function is passed a string not an object - and therefore employee.firstName will be undefined in JavaScript.
Longer answer:
Bear in mind a couple of points:
A HTML attribute expects to contain a string:
<button th:data-parameter1="SOME VALUE IN HERE" ... >
So, the attribute data-parameter1 will be populated by Thymeleaf using a string.
All Thymeleaf processing happens on the server. Thymeleaf removes all its processing directives from the template and replaces them with valid HTML. Your JavaScript does not have access to the original Java object - just to whatever representation of that object was added to the HTML by Thymeleaf.
Let's assume you use something such as:
th:data-parameter1="${employee.firstName}"
Assuming employee.firstName evaluates to a string (John) then that is what Thymeleaf will use to produce this:
data-parameter1="John"
But if you try this:
th:data-parameter1="${employee}"
Assuming employee is your custom Java bean, then Thymeleaf will call its toString() method to use as the string.
If you have not defined a toString() method in your Employee class, then the underlying Object.toString() method will be used - and you will see something like the following - a string representation of the unique object, based on the object's name and hash code:
data-parameter1="org.yourpackage.Employee#bcb8097"
You can provide your own implementation of toString() in your Employee class to provide more useful information. But it has to be a string which can be placed in a HTML attribute.
For example, if you pass an ArrayList to the button:
List<String> names = Arrays.asList("John", "Mary");
and:
th:data-parameter1="${names}"
then your HTML button will contain this:
data-parameter1="[John, Mary]"
because [John, Mary] is the result of how ArrayList has implemented its toString() method.
You can send a certain Java objects directly to JavaScript - see JavaScript serialization. But that is probably off-topic for this question.
One extra note: In the following:
th:onclick="afficherDetails(this.getAttribute('data-parameter1'));"
You are using th:onclick - but there are no Thymeleaf expressions in the attribute, so there is nothing for Thymeleaf to process. You can just use:
onclick="afficherDetails(this.getAttribute('data-parameter1'));"
So, there are a wealth of Flex articles online about how to handle a .NET WebMethod that returns a DataSet or DataTable. Here is an example:
Handling web service results that contain .NET DataSets or DataTables
So, I know how to use result.Tables.<tablename>.Rows and the like. But what I cannot seem to figure out or find online is how to go the other direction - a method to pass objects or tables back to the .NET Webservice from Flex, without stooping to passing XML as a string, or making huge web service methods that have one parameter for each property/column of the object being stored. Surely others, smarter than I, have tackled this issue.
I am using ASP.NET 2.0 Typed DataSets, and it would be really nice if I could just pass one object or array of objects from Flex to the web service, populate my Typed DataTable, and do an Update() through the corresponding typed TableAdapter. My dream would be a [WebMethod] something like one of these:
public void SaveObject(TypedDataTable objToSave) { ... }
public void SaveObject(TypedDataSet objToSave) { ... }
I've had the typed datatables saving to the database, I know how to do that part and even a few tricks, but we had XML being passed back-and-forth as a string - eww. I'm trying to get to a more object-based approach.
The best object based approach is AMF. I assume its probably a bit late in your your development cycle to change your integration layer, but otherwise I dont know of a way to get around marshalling your object(s) back into XML or separating them out into their primitive components.
For .NET implementations of AMF check out:
FlourineFX(FOSS)
WebORB for .NET
Its amazing how easy things become once AMF is used, for example using the Mate MVC framework and an AMF call passing a complex object to the server looks something like this:
<mate:RemoteObjectInvoker instance="yourWebservice" method="saveComplexObject" showBusyCursor="true" >
<mate:resultHandlers>
<mate:CallBack method="saveComplexObjectSuccess" arguments="{[resultObject]}" />
</mate:resultHandlers>
<mate:faultHandlers>
<mate:MethodInvoker generator="{DataManager}" method="presentFault" arguments="{fault}" />
</mate:faultHandlers>
</mate:RemoteObjectInvoker>
With result and fault handlers being optional.
The direction I ended up going was close to what I hoped was possible, but is "hack-ish" enough that I would consider SuperSaiyen's suggestion to use AMF/ORM a better solution for new/greenfield projects.
For sake of example/discussion, let's say I am working with a Person table in a database, and have a typed DataSet called PeopleDataSet that has PersonTableAdapter and PersonDataTable with it.
READ would look like this in .NET web service:
[WebMethod]
public PeopleDataSet.PersonDataTable GetAllPeople() {
var adapter = new PersonTableAdapter();
return adapter.GetData();
}
... which in Flex would give you a result Object that you can use like this:
// FLEX (AS3)
something.dataProvider = result.Tables.Person.Rows;
Check out the link I put in the question for more details on how Flex handles that.
CREATE/UPDATE - This is the part I had to figure out, and why I asked this question. The Flex first this time:
// FLEX (AS3)
var person:Object = {
PersonID: -1, // -1 for CREATE, actual ID for UPDATE
FirstName: "John",
LastName: "Smith",
BirthDate: "07/19/1983",
CreationDate: "1997-07-16T19:20+01:00" // need W3C DTF for Date WITH Time
};
_pplWebSvcInstance.SavePerson(person); // do the web method call
(For handling those W3C datetimes, see How to parse an ISO formatted date in Flex (AS3)?)
On the .NET web service side then, the trick was figuring out the correct Type on the web method's parameter. If you go with just Object, then step into a call with a debugger, you'll see .NET figures it is a XmlNode[]. Here is what I figured out to do:
[WebMethod]
public int SavePerson(PeopleDataSet p) {
// Now 'p' will be a PeopleDataSet with a Table called 'p' that has our data
// row(s) (just row, in this case) as string columns in random order.
// It WILL NOT WORK to use PeopleDataSet.PersonDataTable as the type for the
// parameter, that will always result in an empty table. That is why the
// LoadFlexDataTable utility method below is necessary.
var adapter = new PersonTableAdapter();
var tbl = new PeopleDataSet.PersonDataTable();
tbl.LoadFlexDataTable(p.Tables[0]); // see below
// the rest of this might be familiar territory for working with DataSets
PeopleDataSet.PersonRow row = tbl.FirstOrDefault();
if (row != null) {
if (row.PersonID > 0) { // doing UPDATE
row.AcceptChanges();
row.SetModified();
}
else { // doing CREATE
row.CreationDate = DateTime.UtcNow; // set defaults here
row.IsDeleted = false;
}
adapter.Update(row); // database call
return row.PersonID;
}
return -1;
}
Now, the kluge utility method you saw called above. I did it as extension method, that is optional:
// for getting the Un-Typed datatable Flex gives us into our Typed DataTable
public static void LoadFlexDataTable(this DataTable tbl, DataTable flexDataTable)
{
tbl.BeginLoadData();
tbl.Load(flexDataTable.CreateDataReader(), LoadOption.OverwriteChanges);
tbl.EndLoadData();
// Probably a bug, but all of the ampersand (&) in string columns will be
// unecessarily escaped (&) - kluge to fix it.
foreach (DataRow row in tbl.Rows)
{
row.SetAdded(); // default to "Added" state for each row
foreach (DataColumn col in tbl.Columns) // fix & to & on string columns
{
if (col.DataType == typeof(String) && !row.IsNull(col))
row[col] = (row[col] as string).Replace("&", "&");
}
}
}
I am trying to write a module/areas based mvc3 CMS using razor view engine. I have two layout views, _site.cshtml and _modules.cshtml. The _site.cshtml has an #RenderBody() section. My application always calls a view called "index.cshtml" which has its layout set to _site.cshtml page.
The Problem: The problem is that my modules/areas are rendered before doctype element - and not inside the RenderBody section of layout page.
It looks like what you're trying to do is the equivalent of Html.RenderAction (or Html.Action), so why are you writing all that code yourself instead of using the built-in functionality?
In short, its your Invoke() method! The razor engine works by writing to an in memory stream, and then parses it to the response. The reason you are getting that behaviour is because your Invoke() method is writing straight to the response stream; i.e. before the in memory stream is parsed.
I came across similar behaviour when using the Html.RenderAction(), which pointed to an action that returned a PartialView. The workaround was to use Html.Action(). The difference being is that Action() returns a string which gets appended to the in memory stream, and RenderAction() writes to the directly to the response.
If you post the code for your Invoke() method, I may be able to help you futher!
| -- Edit -- |
OK, this turned out to be more complex that initially anticipated. The problem is that I could not get ProcessRequest() to append to the current response; however, I may have a solution.
public string ProcessRoute(ViewContext viewContext, RouteData routeData)
{
var urlHelper = new UrlHelper(viewContext.RequestContext);
var currentUrl = urlHelper.Action(routeData.Values["action"].ToString(),
routeData.Values["controller"].ToString(), routeData.DataTokens);
var stringWriter = new StringWriter();
var simpleWorkerRequest = new SimpleWorkerRequest(currentUrl, "", stringWriter);
var context = new HttpContext(simpleWorkerRequest);
var contextBase = new HttpContextWrapper(context);
var requestContext = new RequestContext(contextBase, routeData);
var httpHandler = routeData.RouteHandler.GetHttpHandler(requestContext);
httpHandler.ProcessRequest(context);
context.Response.End();
stringWriter.Flush();
return stringWriter.GetStringBuilder().ToString();
}
The code above generates a new Request and returns the Request's HTML as a string. By doing this, the result is appended as part of the current response. You can now rewrite your Invoke() function to return a string, which can be displayed on your View.
public string Invoke(ViewContext viewContext)
{
if (_mvcHandler == null)
{
var routeData = new RouteData(context.RouteData.Route,
context.RouteData.RouteHandler);
routeData.Values.Add("id", _id);
routeData.Values.Add("moduleName", _moduleName);
routeData.Values.Add("controller", _controllerName);
routeData.Values.Add("action", _actionName);
routeData.Values.Add("pageContext", _pageContext);
if (!string.IsNullOrEmpty(_preferredNamespace))
{
routeData.DataTokens.Add("Namespaces", new[] { _preferredNamespace });
}
return ProcessRoute(viewContext, routeData);
}
return string.Empty;
}
You may also have to change;
mr.Invoke(ViewContext);
To;
Html.Raw(mr.Invoke(ViewContext));
In order stop the HTML encoding behaviour.
| -- Note -- |
Since I don't have your ModuleRequests class, I couldn't test this code specifically for your scenario. Instead, I replicated the problem as best as I could and solved it.
Please let me know if you have any questions.
Matt
Let me post a plausible second alternative to your methods while I try and solve the problem above.
There is a html extension in MVC called Html.Partial(), which allows you to use routedata to return the result of a controller action.
So, if you had an AccountController with an Register method that returned a PartialView result, then it would be appended to the current page.
public class AccountController() : Controller
{
public ActionResult Register()
{
return PartialView();
}
}
The call to this action could look something like;
#Html.Partial("Account", "Register");
| -- Note -- |
Don't use #Html.RenderPartial(), or you will have the same problem as above!
I am currently posting away from a computer, so I can't test this theory!
Hope this helps!
Matt
In Action Script 3, you can write a class that defines a dynamic object (MovieClip and Object are two examples), this objects can be modified in run-time. What I want to know if is there some way (in run-time, of course) to know if certain object is dynamic or not.
PS: Without making something like this:
function isDynamic(object) {
try {
object.newProperty = 'someValue'
} catch (e) {
return false
}
return true
}
CookieOfFortune has the right idea, but unfortunately the code itself has problems, isDynamic is an attribute, and the returned value will be a XMLList with a value of a String that reflects a true or false value, not a child node that directly returns a Boolean. It should look more like this:
function isDynamic(object) : Boolean
{
var type:XML = describeType(object);
return type.#isDynamic.toString() == "true";
}
Be careful!
Anytime you want to use the describeType() function, please please please use the variation:
import mx.utils.DescribeTypeCache;
var typeDesc:XML = DescribeTypeCache.describeType(object).typeDescription;
Performance of making repeated calls to the runtime reflective machinery will absolutely suck. That's why Adobe invented the DescribeTypeCache class.
You can use describeType from flash.utils to describe the object in XML form. Here's the reference to the API: flash.utils.describeType
function isDynamic(object) {
var type:XML = describeType(object);
if (type.#isDynamic == "true") return true;
return false;
}
This is a very old post, but I'll add an option for those future searchers.
AS3 has a built in way of doing this:
mx.utils.ObjectUtil.isDynamicObject(yourObject);
Read more about it here.
I have a list of error codes I need to reference, kinda like this:
Code / Error Message
A01 = whatever error
U01 = another error
U02 = yet another error type
I get the Code returned to me via a web service call and I need to display or get the readable error. So I need a function when passed a Code that returns the readable description. I was just going to do a select case but thought their might be a better way. What is the best way / most effieient way to do this?
Use a Dictionary, (in C#, but the concept and classes are the same):
// Initialize this once, and store it in the ASP.NET Cache.
Dictionary<String,String> errorCodes = new Dictionary<String,String>();
errorCodes.Add("A01", "Whatever Error");
errorCodes.Add("U01", "Another Error");
// And to get your error code:
string ErrCode = errorCodes[ErrorCodeFromWS];
You would use a dictionary. A dictionary uses a hashmap internally for performance, so it is good in that regard. Also, because you want this to go as quickly as possible by the sounds of it, I would statically initialize it in its own class instead of, for example, in an XML file or slimier. You would probably want something like:
public static class ErrorCodes
{
private static Dictonary<string, string> s_codes = new Dicontary<string, string>();
static ErrorCodes()
{
s_codes["code"] = "Description";
s_codes["code2"] = "Description2";
}
public static string GetDesc(string code)
{
return s_codes[code];
}
}
That way, if you wanted to move the back end to a file instead of being static, then you could.