Kendo MVC Grid - Dropdown with inline editing - kendo-grid

i have searched all over for a solution and tried many of the advises that was given (just saying if you think I'm too lazy)
I'm a bit of a Kendo noob, so not sure what I'm missing?
As the title say, I got a grid of items and want to edit them inline. all works fine, but it seems that my editor template just get ignored and 2 inputs get shown when in edit mode (since the child object that must be selected is a complex object with Id and Name properties)
ps: sorry bout formatting. seems my browser don't show this windows toolbar?
Kendo MVC Grid
Html.Kendo().Grid<MyViewModel>()
.Name("Grid")
.Columns(columns =>
{
columns.Bound(o => o.Id).Hidden(true);
columns.Bound(o => o.Product).EditorTemplateName("ProductsListTemplate");
...other columns
columns.Command(command =>
{
command.Edit();
command.Destroy();
}).Width(180);
})
.AutoBind(true)
.ToolBar(toolbar => toolbar.Create())
.Editable(editable => editable.Mode(GridEditMode.InLine).Enabled(true))
.Filterable(ftb => ftb.Mode(GridFilterMode.Row))
.Resizable(resize => resize.Columns(true))
.Selectable(sel => sel.Mode(GridSelectionMode.Single)
.Enabled(true))
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(20)
.Events(events => events.Error("error_handler"))
.Model(model =>
{
model.Id(c => c.Id);
model.Field(c => c.Product).Editable(true);
...other fields...
})
.Create(update => update.Action("Create", "MyController"))
.Read(read => read.Action("Read", "MyController"))
.Update(update => update.Action("Edit", "MyController"))
.Destroy(update => update.Action("Delete", "MyController"))
))
ProductsListTemplate.cshtml (in Shared/EditorTemplates, product options are present in the viewdata as IEnumerable )
#(Html.Kendo().DropDownList()
.Name("MyChildViewModel")
.DataValueField("Id")
.DataTextField("Name")
.BindTo((IEnumerable) ViewData["ProductOptions"])
)
MyViewModel
public class MyViewModel
{
public int Id { get; set; }
[Display(Name = "Product")]
[UIHint("ProductsListTemplate")]
public MyChildViewModelProduct { get; set; }
... other properties
public class MyChildViewModel
{
public string Id { get; set; }
public string Name { get; set; }
}

Related

Convertin res.json() to a Array<Object> [duplicate]

This question already has an answer here:
Angular: Typescript casting JSON response as object model not working
(1 answer)
Closed 5 years ago.
I have a java webservice that out puts a list of Json objects with this properties >
public class Oferta {
private int id;
private String categoria;
private String descricao_oferta;
private String anunciante;
private double valor;
private boolean destaque;
private List<String> imagens;
}
And i have a Angular4 project that i want to retrieve this json and store in a Array. How should i do this?
Angular4 oferta model:
export class Oferta {
public id: number;
public categoria: string;
public titulo: string;
public descricao_oferta: string;
public anunciante: string;
public valor: number;
public destaque: boolean;
public imagens: Array<Object>;
}
Method that i can retrieve the list of json objects(works fine, when i console.log(getData) i recieve the list of json objects. :
public getData(): Promise<Oferta[]>{
this.ofertas = this.http.get(this.apiURL)
.map((res: Response) => <Oferta[]>res.json()) << ERROR WHEN CASTING
return new Promise((resolve,reject)=>{
let deu_certo = true
if(deu_certo){
setTimeout(() => resolve(this.ofertas),3000);
}
else{
reject({codigo_erro: 404,mensagem_erro: 'Servidor nao encontrado'})
}
//console.log('passou aqui')
})
.then(( ofertas: Oferta[]) => {
console.log('segundo then')
return new Promise((resolve2,reject2) => {
setTimeout(() => {resolve2(ofertas )},3000)
})
})
.then((ofertas: Oferta[]) => {
console.log('terceiro then executado apos 3 sec , aguardando outra promisse ser resolvida')
return ofertas;
})
}
Now how can i convert this to a Array? Already tried this.oferta[] = res.json(); Wont let me.
///////////// This is how i call at home.component
ngOnInit() {
this.ofertas = this.ofertasService.getData()
this.ofertasService.getOfertas2()
.then(
( ofertas: Oferta[] ) => { this.ofertas = ofertas
console.log('a funçao resolve() foi resolvida depois de 3 segundos')
})
.catch((param: any) => {
console.log(param)
})
}
You simply have to typecast the data provided your class property names match the json properties.
getData():Promise<Oferta[]>{
return this.http.get(this.apiURL)
.map((res: Response) => <Oferta []>res.json()).toPromise()
}
You will have to import the toPromise function from rxjs
Update: i was able to do it using this:
this.http.get(this.actionUrl)
.map(res => res.json())
.subscribe(
data => {
this.ofertas = data;
},
err =>
console.log('cant get ofertas'),
() => console.log('FOi')
But only directly on my Home.component.ts, tried doing it with a method getData from my Oferta.service.ts, i cant use http in oferta.service, so not sure how im going to do this in a differente class apart from the component.

Pass parameter to view action from extended class

I extended "dektrium/yii2-user" controller's class as below, now I want to have $authItems in render view file of parent class, how should I pass this variable?
namespace app\controllers;
use app\models\AuthItem;
use dektrium\user\controllers\RegistrationController as BaseRegistrationController;
class RegistrationController extends BaseRegistrationController
{
public function actionRegister()
{
$authItems = AuthItem::find()->all();
return parent::actionRegister();
}
}
its is main class method
public function actionRegister()
{
if (!$this->module->enableRegistration) {
throw new NotFoundHttpException();
}
/** #var RegistrationForm $model */
$model = \Yii::createObject(RegistrationForm::className());
$event = $this->getFormEvent($model);
$this->trigger(self::EVENT_BEFORE_REGISTER, $event);
$this->performAjaxValidation($model);
if ($model->load(\Yii::$app->request->post()) && $model->register()) {
$this->trigger(self::EVENT_AFTER_REGISTER, $event);
return $this->render('/message', [
'title' => \Yii::t('user', 'Your account has been created'),
'module' => $this->module,
]);
}
return $this->render('register', [
'model' => $model,
'module' => $this->module,
]);
}
A solution could be
don't invoke the parent::actionRegister();
and add the code directly in your actionRegister
and last add the autItems to the render function array parameters
class RegistrationController extends BaseRegistrationController
{
public function actionRegister()
{
$authItems = AuthItem::find()->all();
// return parent::actionRegister();
if (!$this->module->enableRegistration) {
throw new NotFoundHttpException();
}
/** #var RegistrationForm $model */
$model = \Yii::createObject(RegistrationForm::className());
$event = $this->getFormEvent($model);
$this->trigger(self::EVENT_BEFORE_REGISTER, $event);
$this->performAjaxValidation($model);
if ($model->load(\Yii::$app->request->post()) && $model->register()) {
$this->trigger(self::EVENT_AFTER_REGISTER, $event);
return $this->render('/message', [
'title' => \Yii::t('user', 'Your account has been created'),
'module' => $this->module,
]);
}
return $this->render('register', [
'model' => $model,
'module' => $this->module,
'authItems' => $authItems;
]);
}
}
I would do something like this:
Add a member variable to your extended class, such as:
namespace app\controllers;
use app\models\AuthItem;
use dektrium\user\controllers\RegistrationController as BaseRegistrationController;
class RegistrationController extends BaseRegistrationController
{
public $authitems;
public function actionRegister()
{
$this->authitems = AuthItem::find()->all();
return parent::actionRegister();
}
}
Then in your parent class, do a test to see if that variable is set, such as:
public function actionRegister()
{
//.........
return $this->render('register', [
'model' => $model,
'module' => $this->module,
'authitems' => (isset($this->authitems)) ? $this->authitems : null;
]);
}

How can I make my EditorFor disabled?

I have an MVC Razor Create view and my model contains one or more fields that need to be auto-filled by the system, such as "Timestamp" and "Username". Because these fields are required, they need to be in the form.
Here is an example of what the default code looks like when you allow Visual Studio to scaffold your views from a controller.
<div class="form-group">
#Html.LabelFor(model => model.RoseUserName,
htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.RoseUserName,
new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.RoseUserName, "",
new { #class = "text-danger" })
</div>
</div>
This what the code looks like when rendered. It's functional, but the first and third fields are fully editable when it shouldn't be. So the question is how we can have EditorFor fields that feed the correct data into the model when the form is submitted but that the user can't change?
Further Explanation
While there are some reasons you might want to keep those fields hidden from view, there are also valid reasons you would want these fields to be seen by the user, such as in a company intranet with users who expect to see that information. While a datestamp could be autogenerated by SQL Server with a getdate() default value, capturing the user's identity under Windows Authentication would need to be done from from the MVC side. The identification of the user is part of the model.
An important database features is the ability to have computed properties. If you're mapping your code first classes to tables that contain computed properties, you don't want Entity Framework to try to update those columns. But you do want EF to return those values from the database after you've inserted or updated data. You can use the DatabaseGenerated annotation to flag those properties in your class along with the Computed enum. Other enums are None and Identity.
[DatabaseGenerated(DatabaseGenerationOption.Computed)]
public DateTime DateCreated { get; set; }
Now just have your database generate the time stamp when the record is created. You can also set DataAnnotation to read only like so
[DatabaseGenerated(DatabaseGenerationOption.Computed)]
[Editable(false)]
public DateTime DateCreated { get; set; }
Hope this helps
Update
// Private
private DateTime _createdOn = DateTime.Now;
// Public property
[Display(Name = "Created On")]
[DataType(DataType.DateTime)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime CreatedOn
{
get
{
return (_createdOn == DateTime.MinValue) ? DateTime.Now : _createdOn;
}
set { _createdOn = value; }
}
Or in the View.
#Html.HiddenFor(model => model.CommentTime, new { #Value=System.DateTime.Now })
Or in the controller.
public ActionResult Index()
{
return View(new MyViewModel
{
Birth = DateTime.Now
});
}
If done in the controller and/or model you can use the html attribute of readonly like so.
You could use a custom editor template:
public class MyViewModel
{
[UIHint("MyHiddenDate")]
public DateTime Date { get; set; }
}
and then define ~/Views/Shared/EditorTemplates/MyHiddenDate.cshtml:
#model DateTime
#Html.Hidden("", Model.ToString("dd/MM/yyyy"))
and finally in your view use the EditorFor helper:
#model MyViewModel
#Html.EditorFor(x => x.Date)
This will render the custom editor template for the Date property of the view model and consequently render the hidden field with a value using the desired format.
UPDATE
The EditorFor html helper does not have overloads that take HTML attributes. In this case, you need to use something more specific like TextBoxFor:
<div class="editor-field">
#Html.TextBoxFor(model => model.DateCreated , new
{ disabled = "disabled", #readonly = "readonly" })
</div>
You can still use EditorFor, but you will need to have a TextBoxFor in a custom EditorTemplate:
public class MyModel
{
[UIHint("DateCreated ")]
public string DateCreated { ;get; set; }
}
Then, in your Views/Shared/EditorTemplates folder, create a file DateCreated .cshtml. In that file, put this:
#model string
#Html.TextBoxFor(m => m, new { #readonly = "readonly" })
When I posted this answer, it was based on what I was able to find so far. I have learned that this is a case of "Beware what you wish for." The other answers provided point out correctly that following the approach shown below is not a good practice and has at least one false premise, as explained in the conversation thread.
This is useful only as an explanation of what NOT to do. The answer I accepted shows the approach I adopted.
The answer is amazingly simple and elegant. Tt just takes an extra phrase: #readonly="readonly".
In better context, the original example was:
<div class="form-group">
#Html.LabelFor(model => model.RoseUserName,
htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.RoseUserName,
new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.RoseUserName, "",
new { #class = "text-danger" })
</div>
All you do is add#readonly="readonly"to the EditorFor htmlAttributes, like this:
#Html.EditorFor(model => model.RoseUserName,
new { htmlAttributes = new { #class = "form-control", #readonly="readonly" } })
Problem solved.
<div class="form-group">
#Html.LabelFor(model => model.RoseUserName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.RoseUserName, new { htmlAttributes = new { #class = "form-control", disabled = "disabled", #readonly = "readonly"} })
#Html.ValidationMessageFor(model => model.RoseUserName, "", new { #class = "text-danger" })
</div>
</div>
Add disabled = "disabled", #readonly = "readonly" along with #class = "form-control", so you have:
#Html.EditorFor(model => model.RoseUserName, new { htmlAttributes = new { #class = "form-control", disabled = "disabled", #readonly = "readonly"} })

How to configure Kendo Dropdownlist to work with Grid popup editor template

I've been struggling with the Kendo dropdownlist for 2 days now and just can't seem to get it configured correctly.
How do I get the Kendo Dropdownlist to show the current item of the #Model? This is my code:
#Html.TextBoxFor(model => model.ShortDescription, new { #class="wide200;" })
#(Html.Kendo().DropDownList()
.Name("importance")
.HtmlAttributes(new { style = "width: 250px" })
.DataTextField("Name")
.DataValueField("ID")
.DataSource(source => {
source.Read(read =>
{
read.Action("GetImportanceList", "Home");
})
.ServerFiltering(true);
})
.SelectedIndex(0)
)
And in my controller:
public ActionResult GetImportanceList()
{
GenericRepository<Importance> _repository = new GenericRepository<Importance>(_context);
IEnumerable<ImportanceViewModel> list = _repository.Get().ConvertToViewModelList();
return Json(list, JsonRequestBehavior.AllowGet);
}
Problem is with SelectedIndex(0) which is set to the first item. How can I set it to whatever is in the model? It's very simple to do for the textbox (first line in the code): model => model.ShortDescription. But how does this work for the dropdownlist?
I don't just want to set it upon the showing of the editor, but also want the grid to know what the new selection is after I click the Update button.
Note that this is in a custom template for the grid popup editor.
Try this,
You have to pass DropDownListId in model and ListItems.
#(Html.Kendo().DropDownListFor(m=>m.DropDownListId)
.Name("importance")
.HtmlAttributes(new { style = "width: 250px" })
.DataTextField("Name")
.DataValueField("ID")
.DataSource(source => {
source.Read(read =>
{
read.Action("GetImportanceList", "Home");
})
.ServerFiltering(true);
})
.SelectedIndex(0)
)
I asked this question to Telerik. Apparently the Name mustn't be assigned.

Kendo DropDown filter and show in GridView

I have filter with dropdown cities. I wanna that when I choose city from dropdown lis and click button search to show data in gridview with this city.
First question
1) How to get value from dropdown and pass to button and call controller?
2) I tried without dropdown, when populate value directly in Read method but nothing, my gridview is empty.
This is my code
Partial View "Filter", View with gridview and method in controller that populate gridview.
#{
ViewBag.Title = "Filter";
}
<div class="filter-all">
<div class="filter-dropdown">
<div class="filter-part">
<div class="custom-label-div">
City:</div>
<div class="defaultSize">
#(Html.Kendo().DropDownList()
.Name("City")
.HtmlAttributes(new { style = "width:250px" })
.DataTextField("CityName")
.DataValueField("CityID")
.OptionLabel("...")
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetCities", "Filter");
});
})
)
</div>
</div>
</div>
<div class="filter-buttons">
<div class="button-filter-div">
<input type="button" value="Search City" onclick="location.href='#Url.Action("Index", "MFS3")'" class="k-button" style="width: 80px"/>
<input type="button" value="Cancel" onclick="location.href='#Url.Action("Index", "Preview")'" class="k-button" style="width: 80px"/>
</div>
</div>
</div>
#model IEnumerable<MFS_App.Models.MFS3ViewModel>
<div class="right-content shadow">
<div class="preview-header">
Preview Reports</div>
<div class="preview-content">
#Html.Partial("_Filter")
</div>
</div>
<div class="parent-preview-content shadow">
<div class="child-preview-content">
#Html.Partial("_ReportsGridView")
<div class="mfs-title">
<div class="filter-preview-div">
#(Html.Kendo().Grid(Model)
.Name("GridMFS3")
.Columns(columns =>
{
columns.Bound(p => p.FirstName).HtmlAttributes(new { style="width:50px"});
columns.Bound(p => p.LastName).HtmlAttributes(new { style ="width:70px"});
columns.Bound(p => p.Address).HtmlAttributes(new { style = "width:80px"});
columns.Bound(p => p.Mail).HtmlAttributes(new { style = "width:100px" });
columns.Bound(p => p.County).HtmlAttributes(new { style = "width:70px" });
columns.Bound(p => p.City).HtmlAttributes(new { style = "width:50px" }); columns.Command(c => c.Edit());
})
.DataSource(source =>
{
source.Server()
.Model(model => model.Id(m => m.MFS3_ID))
.Read(read => read.Action("GetMFS", "MFS3", new { cityID = 251} ))
.Update(update => update.Action("Update", "MFS3"));
})
.Editable(editable => editable.Mode(GridEditMode.PopUp))
.Selectable(selectable => selectable.Mode(GridSelectionMode.Multiple))
.Pageable()
.Resizable(resize => resize.Columns(true))
.HtmlAttributes(new { style = "width: 1850px" })
)
private IEnumerable<MFS3ViewModel> GetMFS3(int cityID)
{
return HelperClass.dbUp.TBL_MFS_MFS3_Input.Select(p => new MFS3ViewModel
{
CITYID = p.CITIYID,
MFS3_ID = p.MFS3_ID,
FirstName = p. FirstName,
LastName = p. LastName,
p.Address = p. p.Address,
.Mail = p. .Mail,
County = p. County,
City = p. City,
}).Where(p => p.CITYID == cityID);
}
I resolved this via jQuery and added parameter in my Index method
$('#btnSearch').click(function () {
var cityID = $("#City").data("kendoDropDownList").value();
document.location = "/MFS3/Index?cityId=" + cityID;
});
public ActionResult Index(int cityId)
{
return View(GetMFS3(cityId));
}