Setting initial value of knockout viewmodel using razor - asp.net-mvc-3

I'm finding it strange that I can't find any information about how to populate the Knockout viewmodel dynamically. I guess my search terms are incorrect or something.
Anyways as I'm using Asp.Net MVC 3 and am only going to use knockout for one specific page this first time I was hoping I could somehow use the Razor model to insert into the knockout.
#Html.TextBoxFor(m => m.PropertyName, new { data_bind = "value: name" })
I figured that this didn't work because even if the razor populates the field with the PropertyName it happens before the binding so it doesn't force it's value to the knockout viewmodel.
var viewModel = {
name: ko.observable(#GetPoertyNameUsingRazorSomehow)
};
This doesn't work either, at least not in anyway I understand.
As we can easily get RazorModel data using MVC 3 normally I was sure we somehow could inject it into the Knockout viewModel. I haven't seen any tutorials that explain how. What am I missing?

I created a little library on top of Json.NET for just such an occasion:
https://github.com/paultyng/FluentJson.NET
You can create JSON in a Razor view like this (note the Knockout extension methods):
#JsonObject.Create()
.AddProperty("name", "value")
.AddProperty("childObject", c => {
.AddProperty("childProperty", "value2")
})
.AddObservable("knockoutProperty", 123)
This would produce JSON similar to this:
{"name":"value","childObject":{"childProperty":"value2"},"knockoutProperty":ko.observable(123)}
The Knockout methods are added via extension methods and other things can easily extend as well.
The package is on Nu-Get if you want it.

Assuming your property name should be a string, this should work if you enclose #GetPoertyNameUsingRazorSomehow in quotes. Otherwise, you're passing an undefined object into the observable.
name: ko.observable('#GetPoertyNameUsingRazorSomehow')

Related

Sitecore MVC: pass parameters to placeholders

As we are switching to sitecore from a java platform, I have some questions regarding parameters. This is easily done in jsp but I can't find a solution for sitecore. (the implementation is done by external partners)
In my cshtml, I include other elements via the placeholder-function: #Html.Sitecore().Placeholder("Placholdername")
The elements included as a placeholder also can include other elements as placeholders.
So the question is: can I pass some parameters along with those placeholders?
Like my parent element has a certain variable set, for example "i = 5", and I want to pass this variable to the elements included as placeholders and also pass it to the elements included as placeholders in the placeholders?
Something like:
A includes B as a placeholder and passes "i=5" and B includes C as a placeholder and passes "i=5" so in C the value of "i" is "5" because "i" was set to "5" in A.
On out current coremedia platform I can simply use something like:
<cm:include self="${self}" view="asdf">
<cm:param name="i" value="5"/>
</cm:include>
Edit:
What I want to achieve is the following: For example I have the following structure: the page frame cshtml with a variable i=5, which then includes a grid as a placeholder and passes the variable to the grid. The grid then does some math like i=i+5 (which should equal 10) and then includes a teaser as a placeholder and passes the new i=10 to the teaser and so on..
You should set the value of i in the model. Then all the different views or partial views should inherit the same model.
You can assign parameters to renderings, but not to placeholders. Placeholders should be seen as a hole. You can dynamically put stuff in it but you can't assign parameters to it. There's a discussion of that here: https://sitecore.stackexchange.com/questions/2098/add-rendering-parameters-to-placeholder/2101
I can think of at least two approaches to solve your problem:
Although your question is a bit lacking in detail, it kind of looks like you don't necessarily need placeholders because you already know what you're going to render inside those spaces. If that's the case, then you can statically bind your MVC views instead of using placeholders. This is not a common practice, but it is mentioned in Sitecore's latest training material and elsewhere as a way to optimize when you don't need the flexibility of placeholders. This is normally done using the #Html.Sitecore.Rendering helper.
You could use a global variable of sorts by leveraging the good old HttpContext.Items collection.
You can add parameters to the ViewData dictionary, in a controller action method:
public ActionResult MyPage()
{
ContextService.Get().GetCurrent().ViewData.Add("MyKey", "MyValue");
return View();
}
Then, any View Renderings can access the parameter from ViewData:
#{
var value = ViewData["MyKey"].Value;
}
Or if you're using Controller Renderings, add some code to fetch the ViewData from parent pages, and add it to the current ViewData instance:
public ActionResult ChildRendering()
{
// Get any ViewData previously added to this ViewContext
var contextViewData = ContextService.Get().GetCurrent().ViewData;
contextViewData.ToList().ForEach(x => ViewData.Add(x.Key, x.Value));
return View();
}
Your ViewData contents will now be available in view files.
This is discussed in a little more detail here: https://chrisperks.co/2017/03/06/a-workaround-for-missing-viewdata-in-sitecore-mvc/

Create ViewBag properties based on strings

Is there any way to create and use dynamic properties for ViewBag, based on strings?
Something like
ViewBag.CreateProperty("MyProperty");
ViewBag.Property("MyProperty") = "Myvalue";
Thank you
I just found out that ViewData can be used to create such properties for ViewBag
So to create property CityErrorMessage I have to use
ViewData.Add("CityErrorMessage", MyErrorMessage)
and then in the view I can use
#ViewBag.CityErrorMessage
EDIT:
I created the ViewBag's properties dynamically, because I received the name of field with validation error in a list
So the code actually is
foreach (ValidationError err in ValidationErrors)
{
ViewData.Add(
string.format("{0}ErrorMsg", err.PropertyName),
err.ValidationErrorMessage);
}
Update: I belatedly realised that this code comes from a Nancy project and Nancy implements it's own ViewBag so this code does not work with .Net MVC3 and does not answer the question. However, it might be argued that the question could be solved by switching to Nancy.
I found ViewBag has an Add method so you can do this:
foreach(var row in model)
{
ViewBag.Add(row.resourceName, row.content);
}

How do I output Massive ORM dynamic query to an MVC 3 View?

When I use Massive ORM to retrieve a record using the .Find() method, it returns a Massive.DynamicModel.Query object which doesn't get along very well with the ASP.MVC 3 View.
Controller:
public ViewResult Details(int id)
{
// Massive ORM Find syntax requires this next statement to use 'dynamic' not 'var'
dynamic table = new Things();
// Thing will return as type Massive.DynamicModel.Query
var Thing = table.Find(ThingId:id);
return View(Issue);
}
In the View, I've tried both #model dynamic and #model Massive.DynamicModel.Query, but neither will allow me to access the properties of my 'Thing' object using the normal #Model.Name syntax.
There is some discussion on here about how to handle ExpandoObjects with MVC3 views, but nothing in particular about the Massive.DynamicModel.Query implementation that has worked for me so far.
Any general ideas how to convert the Massive.DynamicModel.Query object to something typed?
Two words: View models. Strongly typed view models, that's what you should be passing to your views. Not dynamics, not expandos, not anonymous objects, not ViewData, not ViewBag => only strongly typed view models. So start by defining a view model which will represent the data this view will be working with. Then have your controller action do the necessary in order to convert whatever your repositories spit into a view model which will be passed to the view.
Failing to follow this basic rule, your ASP.NET MVC experience could quickly turn into a nightmare.
I think the easiest way is to use ViewBag because it's dynamic already.
You'd better watch the production because it's about Rob's opinionated way of development more than it is about MVC 3, and describes using Massive and other Rob tools.
But even if you don't make sure to check the code sample here for the production (free), to see how he integrates Massive to MVC 3:
https://github.com/tekpub/mvc3
You can see his production controller looks like. Quite interesting ways.
I'm experimenting with dynamics and Massive now. I'm using a dynamic viewModel:
public ActionResult Index() {
_logger.LogInfo("In home");
dynamic viewModel = new ExpandoObject();
var data = _tricksTable.Query("SELECT TOP(10) * FROM Tricks ORDER BY DateCreated DESC");
viewModel.TenTricksNewestFirst = data;
var data2 = _tricksTable.Query("SELECT TOP(10) * FROM Tricks ORDER BY Votes DESC");
viewModel.TenTricksMostPopularFirst = data2;
return View(viewModel);
}
In my view there is no reference to anything strongly typed on the first line eg NOT THIS:
#model IEnumerable<MvcApplication2.Models.Thing>
so in my view I do stuff like this:
#foreach (var item in Model.TenTricksNewestFirst) {
<div class="post block">
<div class="tab-image-block">
<a href="/tricks/#URL.MakeSpacesMinuses(#item.Name)" title="#item.Name">
<img src="/public/images/#item.Thumbnail" alt="#item.Name" class="woo-image thumbnail" /></a>
</div>
<h2 class="title">
#item.Name</h2>
<span class="date">#Dates.ShortDate(#item.DateCreated)</span>
<span class="likes">Likes: #item.Votes</span>
</div>
}
Experience so far is that I'm writing a lot less code.
Because of anonymous type are always annotated as "internal" so you can't access your dynamic type instance from View as they are in different scopes.
I find a better way make it work than using ViewBag. And the answer is Mono.Cecil. Grab it handy from NuGet.
With Mono.Cecil's help you can change MSIL code generated from your ASP.NET MVC project and change the type's accessible modifier to "public".
I write a little console program and host it on GitHub.
You can invoke the program from command line or add a post-build event in your ASP.NET MVC project's Build Events:
"$(SolutionDir)DynamicHelper\bin\Debug\DynamicHelper.exe" "$(TargetPath)"
NOTICE: "DynamicHelper" is the the code's project name and you can change it depending on your situation.

ASP.NET MVC 3 Model Binding - ViewBag.Title clash with input of id="Title"

There seems to be an issue with the ViewBag dynamic properties. Lets say I have:
#{
ViewBag.Title = #Model.CourseName;
}
And then in a form on the page I have:
#Html.TextBox("Title", null, new {style="width:400px;"})
Where Title is the name of a field in a database table.
When the page first opens, text box with an id of "Title" takes the value of the ViewBag.Title dynamic property.
I am a bit hazy on the exact details of Model Binding, but this does seem to be a bug, or if not, if it is something that occurs naturally as a result of the binding process, then it would be nice to be warned of this.
The work around I found, was to rename the ViewBag property to:
#{
ViewBag.Titulo = #Model.CourseName;
}
(Title changed to Titulo - Always good to know another language to avoid name clashes...)
And the issue went away.
However, the question is:
Is this behaviour to be expected? The bug was easy to find (took an hour to figure it out, including writing this question), but I suspect that other bugs might be more, uhmmm, recondite.
EDIT:
Rephrasing the question:
Does the Model Binder automatically bind properties it finds in the ViewBag? Even when an existing property exists in the strongly typed ViewModel I have passed to the page? Surely the ViewModel should take preference?
Html.TextBox checks ViewData/ViewBag values first, then Model. To make sure it takes Model value, you must use Html.TextBoxFor.

Symfony: How can I create user-templates from filled out forms

In my Symfony project I'm trying to provide a "save as template"-button for an embedded Form. The embedded form contains dynamic embedded forms itself.
Example:
The user should be able to save the template without saving the whole form. So i'm going to use AJAX to achieve this (as I already did, for the dynamic add-behavior).
The actual problem is that Symfony names the form in dependence on the parent form, e.g.
<input name="Project[Workflow][1][name]" />
But the template isn't related to "Project" at all. On the other hand, this naming format is required later, when saving the whole form.
Sending the whole form to the server might be a solution, but I think it's a bad practice / overkill / waste of bandwidth.
Is there a common way how to do this?
If not, do you have a basic approach in mind?
Regards,
Uli
symfony sfForms take 2 arrays on the bind method, you don't really need to take them from the request.
since you have several WorkflowForms there is a loop involved!
$formData = $request->getParameter('Project'); // you could do Project['Workflow'] except for symfony 1.4
foreach ($formData['Workflow'] as $embeddedData)
{
$formFiles = $request->getFiles('Project');
$embeddedFiles = $formFiles['Workflow'];
$form = new WorkflowForm();
$form->bind($embeddedData, $embeddedFiles);
if ($form->isValid())
{
// do your thing
// ...
$form->save();
}
}
then you process each form as you would usually do on // do your thing

Resources