Sitecore MVC Sitecore.Mvc.Presentation.RenderingModel - sitecore

I am pretty new at sitecore and i faced a problem when rendering controller.
As you can see from below picture i created a "Mainlayout" and putted the placeholders.
<div id="page-wrapper">
<div class="container-fluid">
<div class="row">
#Html.Sitecore().Placeholder("content")
#Html.Sitecore().Placeholder("content1")
</div>
<!-- /.row -->
</div>
<!-- /.container-fluid -->
</div>
After that i created a class Driver which contains Name and Text properties
public class Driver
{
public HtmlString Name { get; set; }
public HtmlString Text { get; set; }
}
The third picture has a method for getting the driver.
public class DriverRepository
{
public Driver GetDriver()
{
var driver = new Driver();
var rendering = RenderingContext.Current.Rendering;
var datasource = rendering.Item;
driver.Name = new HtmlString(FieldRenderer.Render(datasource, "Name"));
driver.Text = new HtmlString(FieldRenderer.Render(datasource, "Text"));
return driver;
}
}
After that i created a controller Driver with actionresult Featured
public class DriverController : Controller {
// GET: Driver public ActionResult Featured() {
var repository = new DriverRepository();
var driver = repository.GetDriver();
return View(driver);
}
}
And generated the view for the controller
using Sitecore.Mvc
model TestMvcTaulantTutorial.Models.Driver
Name : Model.Name <br />
Text : Model.Text
After that in sitecore I created a controller rendering for the Driver controller
I specified to home page this rendering to be read to content1
But when I deploy it returns me this error
The model item passed into the dictionary is of type
'TestMvcTaulantTutorial.Models.Driver', but this dictionary requires a
model item of type 'Sitecore.Mvc.Presentation.RenderingModel'.

1. My first assumption, as far as I understood from your description, seems that your rendering is not re-deployed and you are having a previous version of the same rendering at your deployment target, because dictionary requires a model item of type 'Sitecore.Mvc.Presentation.RenderingModel' while you have clearly specified correct model at the top:
model TestMvcTaulantTutorial.Models.Driver
... so that's probably another rendering being taken.
2. Another assumption, could you please replace that:
return View(driver);
.. with thу code below, explicitly specifying your view path, just to understand wether that is a root of a problem:
return View("your_correct_view_path", driver);
3. Also, what I've noticed, on a view you should have:
Name : #Model.Name <br/>
Text : #Model.Text
instead of:
Name : Model.Name <br/>
Text : Model.Text

The Driver class should inherit from RenderingModel class.
public class Driver: Sitecore.Mvc.Presentation.RenderingModel { .. }

I think Rvan is right. That error caused you to create the view rendering and not the controller rendering. Remember in the naming convention you don't need the controller keyword after the controller's name.

Related

Can't access variable after radio select. Livewire

New to Laravel / Livewire.
I have a component that outputs a radio group, It renders fine.
When I select one of the radio controls I get an error, "Attempt to read property "id" on array"
Here I pass an array of options from my blade file
#livewire('test-options', ['options' => $option])
Component
class TestOptions extends Component {
public $selected;
public $options;
public function mount($options)
{
$this->options = $options;
}
public function render()
{
return view('livewire.test-options', [
'options' => $this->options
]);
}
component blade
<div>
#foreach($options['items'] as $option_items)
<div class="custom-control custom-radio">
<input
wire:model="selected"
class="custom-control-input"
type="radio"
id="rdio_{{$option_items['0']->id}}_{{$options['option']->id}}">
<label class="custom-control-label" for="rdio_{{$option_items['0']->id}}_{{$options['option']->id}}">{{$option_items['0']->title}}</label>
</div>
#endforeach
The issue is, when you select a radio, Livewire is refreshing the component. However, the $options array is not being passed again. mount() will only run when the component is first rendered.
You are already passing the options into the component, the $optons variable does not need to be mounted as it is already loaded and set here:
#livewire('test-options', ['options' => $option])
Change your code to:
class TestOptions extends Component {
public $selected;
public $options;
public function render()
{
return view('livewire.test-options', [
'options' => $this->options
]);
}
If you're also trying to get a value from the radio input, you'll need to add a value:
<input
wire:model="selected"
class="custom-control-input"
type="radio"
value="yourvaluehere"
id="rdio_{{$option_items['0']->id}}_{{$options['option']->id}}">
From the docs:
In Livewire components, you use mount() instead of a class constructor __construct() like you may be used to. NB: mount() is only ever called when the component is first mounted and will not be called again even when the component is refreshed or rerendered.
Read more here: Receiving Parameters

Ember cli pagination - unable to receive model?

Working with Ember 3.19 and trying to use ember-cli-pagination. I have all my posts from JSONplaceholder in the data-store under model type 'post'. I was able to view all the posts from the data-store without pagination but have been unsuccessful in implementing ember-cli-pagination. Console shows currentPage and totalPages as undefined. The Articles element shows in the ember inspector but blank in chrome. The PageNumbers element appears but it is rendered as <<< ... NaN >>>
Controller - articles.js
import Controller from "#ember/controller";
import { tracked } from "#glimmer/tracking";
import { alias, oneWay } from "#ember/object/computed";
import pagedArray from "ember-cli-pagination/computed/paged-array";
import { inject as service } from '#ember/service'
export default class ArticlesController extends Controller {
// setup our query params
queryParams: ["page", "perPage"];
// set default values, can cause problems if left out
// if value matches default, it won't display in the URL
#tracked page = 1;
#tracked perPage = 10;
// can be called anything, I've called it pagedContent
// remember to iterate over pagedContent in your template
#pagedArray('model', {
page: alias("parent.page"),
perPage: alias("parent.perPage"),
})
pagedContent;
// binding the property on the paged array
// to a property on the controller
#oneWay("pagedContent.totalPages") totalPages;
}
Handlebar - articles.hbs
<h2>Posts</h2>
<div>
<ul>
{{#each #pagedContent as |post|}}
<li>User: {{post.user}} Title: {{post.title}} - {{post.body}}</li>
{{/each}}
</ul>
</div>
<PageNumbers #content={{#pagedContent}} />
Model - post.js
import Model, { attr } from '#ember-data/model';
export default class ArticleModel extends Model {
#attr title;
#attr body;
#attr userId;
}
The issue is in the articles.hbs file:
Since the pagedContent is defined in the corresponding controller and not any kind of argument, the property has to be used with this and not with #. Hence change this template code should work.
<h2>Posts</h2>
<div>
<ul>
{{#each this.pagedContent as |post|}}
<li>User: {{post.user}} Title: {{post.title}} - {{post.body}}</li>
{{/each}}
</ul>
</div>
<PageNumbers #content={{this.pagedContent}} />
Also, there is a typo in the controller file. Since this is a class component, the qps has to defined like:
queryParams = ["page", "perPage"];

How to clear the typeahead input after a result is selected?

I'm using the ng-bootstrap typeahead component to search a customer database. When the user selects a customer from the typeahead results list, I navigate to a customer details page. I've got this working, but I want to clear the input field after navigation has taken place. I've tried setting the model to null or an empty string in the selectItem event logic, but this isn't working:
customer-search-typeahead.component.html
<template #resultTemplate let-r="result" let-t="term">
<div>
<div>
{{r.resource.name[0].given}} {{r.resource.name[0].family}}
</div>
<div>
{{r.resource.birthDate | date: 'dd/MM/yyyy'}}
</div>
</div>
</template>
<input type="text" class="form-control" [resultTemplate]="resultTemplate" (selectItem)="onSelect($event)"
[(ngModel)]="model" placeholder="Start typing a customer name..." [ngbTypeahead]="search"/>
customer-search-typeahead.component.ts
#Component({
selector: 'customer-search-typeahead',
template: require('./customer-search-typeahead.component.html'),
styles: [`.form-control { width: 300px; }`]
})
export class CustomerSearchTypeaheadComponent {
model: any;
searching: boolean;
constructor(private customerService: CustomerService, private router: Router) {}
onSelect($event) {
this.router.navigate(['/customers', $event.item.resource.id]);
this.model = null;
};
search = (text$: Observable<string>) =>
//omitted for brevity
}
The typeahead input looks like this after a selection has been made:
Solution
customer-search-typeahead.component.html
<input type="text" class="form-control" #input [ngbTypeahead]="search" (selectItem)="onSelect($event); input.value='' ">
customer-search-typeahead.component.ts
onSelect($event, input) {
$event.preventDefault();
this.router.navigate(['/customers', $event.item.resource.id]);
};
The issue you witnessing arises from the fact that the NgModel directive is updating model binding asynchronously and the actual model is updated after the onSelect method gets executed. So your model update gets overridden by the NgModel functionality.
Fortunately we (ng-bootstrap authors) got all the flex points in place to cover your use-case :-) There are a couple of things that you could do.
Firstly the $event object passed to the onSelect method has the preventDefault() method and you can call it to veto item selection (and as a result writing back to the model and input field update).
$event.preventDefault() will make sure that the model is not updated and the input field is not updated with the selected item. But text entered by a user will still be part of the input so if you want to clear up this as well you can directly update the input's value property.
Here is code demonstrating all those techniques together:
onSelect($event, input) {
$event.preventDefault();
this.selected.push($event.item);
input.value = '';
}
where input argument is a reference to the input DOM element:
<input type="text" class="form-control" #input
[ngbTypeahead]="search" (selectItem)="onSelect($event, input)">
Finally here is a plunker showing all this in practice: http://plnkr.co/edit/kD5AmZyYEhJO0QQISgbM?p=preview
The above one is template ref value solution.
This is for ngModel solution.
Html code:
<input type="text" class="form-control" [resultTemplate]="resultTemplate" (selectItem)="onSelect($event)"
[(ngModel)]="model" placeholder="Start typing a customer name..." [ngbTypeahead]="search"/>
Component code:
onSelect($event) {
$event.preventDefault();
this.model = null;
this.router.navigate(['/customers', $event.item.resource.id]);
};
$event.preventDefault();
for ngModel value change empty

MVC4 - Creating a List of Model the View and passing to controller

Move MVC blues. I'm trying to populate an empty List in the View and pass it to a controller. Here the model below
//Model
public class Person{
string name {get;set;}
int age {get;set;}
}
Here's the controller that I'd like to send the List to
//Controllers
[HttpPost]
public ActionResult GetPeople(List<Person> people) //Trying to get here
{
foreach(var person in people)
{
//Do Work
}
ActionResult.Redirect(/Somewhere);
}
Here's the initial controller where my view is tied to that creates and passes an empty List
public ActionResult Index()
{
var people = new List<People>();
return View(people);
}
Here's the view. The point of confusion. I somehow need to use a form to add to the List then submit back to GetPeople controller
//Index View
#model List<People> //This starts out empty;
<form method="POST" action="/Home/GetPeople">
<div name="person">
<input name="name" />
<input name="age" />
</div>
<button> Add Another Person</button> //I'm thinking this would create another person div which would be added to the list to be passed to the controller someohow
<button> Submit </button> //Sends List<Person> to GetPeople controller
</form>
Any ideas? Thanks
You need to maintain names of the input fields on your view. To be posted as a member of a list, your fields should look like
<div>
<input name="people[0].name" />
<input name="people[0].age" />
</div>
For the "Add Another Person" button you might need some javascript that will find out current index and insert fields with this index incremented. One thing to watch out is the "Remove" button you might want to have for new entries - make sure it does not creat gaps in indicies.

Binding a DropDownList in Kendo Scheduler Custom Template (ASP.NET MVC Wrapper version)

I'm using the ASP.NET MVC wrapper for Kendo UI and I'm trying to bind several dropdownlists within a custom template (x-kendo-template). I can't figure out how to do this using the ASP.NET MVC Wrapper (this is similar to this question: How do I bind a DropDownList to a DataSource within an editor template using the scheduler?).
There are some examples on using the Kendo Web version, but no complete examples out there that show using a custom pop-up editor with the scheduler that contains a dropdownlist pulling data from a URL (json data).
Is there an end-to-end example? I can load the scheduler with data. The issue is with the custom template and dropdownlist binding.
EDIT:
After searching extensively, I stumbled upon a "getting started" page from Telerik on using the Kendo UI Scheduler in ASP.NET MVC. They (Telerik) really need to do a better job cross-linking between Demos to Documentation to APIs and Examples (Here is the example)
I've also created a blog post that wraps everything for a Scheduler (from the database table to the Views), you can see that here.Kendo UI Scheduler with ASP.NET MVC and Peta Poco
The example shed some light which the demo and documentation do not do, like for instance, the ViewModel they use in their example online:
C# ViewModel
public class Projection : ISchedulerEvent
{
public string Title { get; set; }
public DateTime Start { get; set; }
public DateTime End { get; set; }
public string Description { get; set; }
public bool IsAllDay { get; set; }
public string Recurrence { get; set; }
public string RecurrenceRule { get; set; }
public string RecurrenceException { get; set; }
// Custom Field
public int EventId { get; set; }
public int CustomerId { get; set; }
}
Your ViewModel that you use for the Scheduler must inherit from the ISchedulerEvent class or it will not work correctly.
Razor View
The Razor View is pretty straight-forward, although most of the demos you run across will show data getting passed via the server-side (from the controller). Here, I'm doing this via Ajax methods (Create, Read, Update, Destroy).
#(Html.Kendo().Scheduler<KendoUISchedulerDemo.Models.Projection>()
.Name("scheduler")
.Date(DateTime.Today)
.Height(600)
.DataSource(d => d
.Model(m =>
{
m.Id(f => f.EventId);
m.Field(f => f.Title);
m.Field(f => f.CustomerId);
m.Field(f => f.Description);
m.RecurrenceId(f => f.Recurrence);
})
.Read("Read", "Shared", new { Area = "Events" })
.Create("Create", "Shared", new { Area = "Events" })
.Destroy("Destroy", "Shared", new { Area = "Events" })
.Update("Update", "Shared", new { Area = "Events" })
)
.Events( events =>
{
events.Add("ABC.events.SchedulerAdd");
})
.Editable(edit =>
{
edit.TemplateId("schedulerTemplate");
})
)
The main point with using the datasource with ajax calls like above is that it allows us to put the methods in a separate controller so that we can keep the controller that is displaying the view clean.
Razor View - Kendo Template (for the pop-up editor of events)
This is the script block for the x-kendo-template that overwrites the default pop-up window when creating and editing events in the Kendo Scheduler. This script is pretty much the wild west and you can do whatever you want in it, and it is bound by default with the Kendo MVVM model. Take that with a grain of salt though because there is not a documented way to "extend" that ViewModel to properly put your datasources from custom drop down lists in the ASP.NET MVC wrapper (version) of the Scheduler. (This uses Twitter Bootstrap as well)
<script type="text/x-kendo-template" id="schedulerTemplate">
<div class="form-group">
<div class="col-md-5">
#Html.Label("title", "Title", new { #class = "col-md-2 control-label" })
<div class="col-md-10">
<input class="k-textbox" data-bind="value: title" />
</div>
</div>
</div>
<div class="form-group mTop10">
#Html.Label("CustomerId", "Customer", new { #class = "col-md-2 control-label" })
<div class="col-md-10">
<input id="CustomerId" class="w450" />
</div>
</div>
<div class="form-group mTop10">
<div class="left col-md-5">
#Html.Label("start", "Start", new { #class = "col-md-2 control-label left" })
<div class="col-md-10">
<input name="start" type="text" required data-type="date" data-role="datetimepicker" data-bind="value: start,invisible: isAllDay" />
<input name="start" type="text" required data-type="date" data-role="datepicker" data-bind="value: start,visible: isAllDay" />
</div>
</div>
<div class="left col-md-5">
#Html.Label("end", "End", new { #class = "col-md-2 control-label left" })
<div class="col-md-10">
<input name="end" type="text" required data-type="date" data-role="datetimepicker" data-bind="value: end ,invisible:isAllDay" />
<input name="end" type="text" required data-type="date" data-role="datepicker" data-bind="value: end ,visible:isAllDay" />
</div>
</div>
</div>
<div class="clear"></div>
<div class="form-group mTop10">
#Html.Label("isAllDay", "All Day", new { #class = "col-md-2 control-label" })
<div class="col-md-10">
<input type="checkbox" name="isAllDay" data-type="boolean" data-bind="checked:isAllDay">
</div>
</div>
</script>
JsonResults (in Controller)
Here are the CRUD Json Results. The Create, Update and Destroy JsonResults have been trimmed for the example.
public virtual JsonResult Read([DataSourceRequest] DataSourceRequest request)
{
var data = new List<Projection>();
data.Add(new Projection()
{
EventId = 1,
Start = DateTime.Now.AddDays(-2),
End = DateTime.Now.AddDays(-2).AddHours(2),
IsAllDay = false,
CustomerId = 1,
Description = "Booked for plumbing",
Title = "Manchester Residence"
});
return Json(data.ToDataSourceResult(request), JsonRequestBehavior.AllowGet);
}
public virtual JsonResult Create([DataSourceRequest] DataSourceRequest request, Projection evt)
{
if (ModelState.IsValid)
{
// Other code here
}
return Json(new[] { evt }.ToDataSourceResult(request, ModelState));
}
public virtual JsonResult Update([DataSourceRequest] DataSourceRequest request, Projection evt)
{
if (ModelState.IsValid)
{
// Other code here
}
return Json(new[] { evt }.ToDataSourceResult(request, ModelState));
}
public virtual JsonResult Destroy([DataSourceRequest] DataSourceRequest request, Projection evt)
{
if (ModelState.IsValid)
{
// Other code here
}
return Json(new[] { evt }.ToDataSourceResult(request, ModelState));
}
JavaScript
Here is the JavaScript contained within a stand alone JS file that corresponds to my "Add" event for the Scheduler. I didn't show the "Edit" event as it is pretty much the same idea and you should be able to figure it out.
ABC.Events.SchedulerAdd = function (e) {
function GetCustomers(value) {
var url = "/Events/Shared/GetCustomers"
var success = function (result) {
if (result != null) {
$("#CustomerId").kendoDropDownList({
dataTextField: "FullName",
dataValueField: "CustomerId",
dataSource: new kendo.data.DataSource({ data: result })
});
}
};
$.ajax({ url: url, success: success });
}
GetCustomers();
};
One of the keys in this JavaScript function is that we are turning our field into a Kendo DropDownList and wiring our datasource up at the same time that we are receiving as a JsonResult (not shown, but it's a simple Json object). Another key is how we wire up the datasource as we are creating a new kendo.data.DataSource. If you try to simply wire up the JsonResult, it won't work.
Conclusion
This is a work around to fill dropdownlists in a Scheduler Template (pop-up) when using the ASP.NET MVC Wrapper version of the Kendo UI. I am open for a better way, in which I imagine it will be adding the Json list data to the Internal MVVM that the Kendo Scheduler uses, but without the documentation for ASP.NET MVC or examples of how to pull it off, this is a way it can work.
EDIT #2 - Telerik ASP.NET MVC Example
I finally heard back from Telerik Support on this issue and was directed to this link: http://www.telerik.com/support/code-library/custom-editor-9fd60fca3c02 There is a sample MVC project there that shows how to use a Custom Editor, Drop Down Lists with datasources, all within ASP.NET MVC. Why on Earth there is no link from the documentation to such projects that can obviously help is a mystery to me.
Did you manage to figure this out? I'm working on something similar and have managed to get some of this to work and I have a demo that might help. Mine is not 100% right now but I am getting there. I have a custom template linked to a resource. My issue is sometimes the model doesn't validate so I don't get a post back to the Jason method in the controller. Have you seen this example?