Telerik UI for Asp.Net MVC How to retrieve selected row of detail grid - telerik-grid

I need to get the selected row in a detail grid. Here is the master grid:
#(
Html.Kendo().Grid<CN.Models.Competency.ViewModels.AssessmentModel>()
.Name("grid")
.Columns(columns =>
{
columns.Bound(c => c.AssessmentId).Hidden();
columns.Bound(c => c.AssessmentName).Width(250);
columns.Bound(c => c.CompetencyType).Width(80);
columns.Bound(c => c.CompanyName).Width(200);
columns.Bound(c => c.NumOfUnits).Width(50);
columns.Bound(c => c.CompetencyTypeId).Hidden();
columns.Bound(c => c.CompanyId).Hidden();
columns.Template(#<text></text>).Title("Actions").Width(150).ClientTemplate(
"<div class='btn btn-alt3' style='cursor: pointer;' onclick='downloadAssessment()'><i class='lnir lnir-download'></i>Download</div>" +
"# if('" + #IsiCanAdmin + "'== 'True' || ('" + #IsCompanyAdmin + "'== 'True' && CompetencyTypeId == 2 && CompanyId == " + User.companyId + ") ) {#" +
"<div class='btn btn-alt3' style='cursor: pointer;' onclick='editRow()'><i class='la la-edit'></i>Edit</div>" +
"<div id='deleteAssessment' class='btn btn-alt3' style='cursor: pointer;' onclick='deleteAssessment(this)'><i class='la la-trash'></i>Delete</div>" +
"# } #"
);
})
.Height(750)
.Scrollable()
.Sortable()
.Selectable()
.ClientDetailTemplateId("template")
.DataSource(dataSource => dataSource
.Ajax()
.Read(read => read.Action("GetAssessmentSearch", "Config", new { area = "Competency" }))
.Model(m => m.Id(o => o.AssessmentId))
)
)
Here is the detail grid:
<script id="template" type="text/kendo-tmpl">
#(
Html.Kendo().Grid<CN.Models.Competency.ViewModels.AssessmentUnitModel>()
.Name("grid_#=AssessmentId#")
.Columns(columns =>
{
columns.Bound(o => o.UnitId).Hidden();
columns.Bound(o => o.UnitName).Width(350).Sortable(true);
columns.Bound(o => o.CompetencyType).Width(75);
columns.Bound(o => o.CompanyName).Width(200).Sortable(true);
columns.Bound(o => o.GapClosureThreshold).Width(50);
columns.Bound(o => o.NumEvidences).Width(50);
columns.Template(#<text></text>).Title("Actions").Width(150).ClientTemplate("#=editUnit(data)#");
})
.Scrollable()
.ClientDetailTemplateId("evidenceTemplate")
.DataSource(dataSource=>dataSource
.Ajax()
.Read(read=>read.Action("GetAssessmentUnits", "Config", new { area = "Competency", AssessmentId = "#=AssessmentId#" }))
.Model(m=>m.Id(o=>o.UnitId))
)
.ToClientTemplate()
)
Here is what I tried:
function editUnit(item) {
if ("#IsiCanAdmin" === "True") {
return "<div class='btn btn-alt3' style='cursor: pointer;' onclick='UnitEditRow(this)'><i class='la la-edit'></i>Edit</div>";
}
}
function UnitEditRow(item) {
var data1;
var grid = $("#grid").data('kendoGrid');
var allChildren = $(grid.element[0]).find('input.checkChild:selected')
$.each(allChildren, function () {
var detailRow = $(this).closest('tr');
var table = $(this).closest('div.k-grid');
var detailGrid = $(table).data('kendoGrid');
//var allSelected = detailGrid.select();
data1 = detailGrid.dataItem(detailRow);
});
var unitId = data1.UnitId;
openUnit(unitId)
}

Added a "DataBound" event to the detail grid to handle the hide/show of the button:
#(
Html.Kendo().Grid<CN.Models.Competency.ViewModels.AssessmentUnitModel>()
.Name("grid_#=AssessmentId#")
.Columns(columns =>
{
columns.Bound(o => o.UnitId).Hidden();
columns.Bound(o => o.UnitName).Width(350).Sortable(true);
columns.Bound(o => o.CompetencyType).Width(75);
columns.Bound(o => o.CompanyName).Width(200).Sortable(true);
columns.Bound(o => o.GapClosureThreshold).Width(50);
columns.Bound(o => o.NumEvidences).Width(50);
columns.Template(#<text></text>).Title("Actions").Width(150).ClientTemplate("<div class='btn btn-alt3 editClass' style='cursor: pointer;' onclick='editRow(this)'><i class='la la-edit'></i>Edit</div>");
})
.Selectable()
.Scrollable()
.ClientDetailTemplateId("evidenceTemplate")
.DataSource(dataSource=>dataSource
.Ajax()
.Read(read=>read.Action("GetAssessmentUnits", "Config", new { area = "Competency", AssessmentId = "#=AssessmentId#" }))
.Model(m=>m.Id(o=>o.UnitId))
)
.Events(e => e.DataBound("onDetailDataBound"))
.ToClientTemplate()
)
function onDetailDataBound() {
if ("#IsiCanAdmin" != "True") {
var editButtons = $(".editClass");
for (var i = 0; i < editButtons.length; i++) {
$(editButtons[i]).css("display", "none");
}
}
}
The use the button to find the closest row and table to get the dataItem:
function editRow(button) {
var buttonDiv = $(button)[0];
var row = $(buttonDiv).closest('tr');
var table = $(buttonDiv).closest(".k-grid");
var grid = $(table).data("kendoGrid");
var dataItem = grid.dataItem(row);
var unitId = dataItem.UnitId;
}

Related

Tabulator: component.getComponent is not a function

I'm using Tabular in a Next/React environment and I occasionally get the following when clicking a button that's rendered via a custom formatter:
component.getComponent is not a function
The button/custom formatter:
const EditButton = () => {
const editor = document.createElement('button');
editor.style.backgroundColor = '#000';
editor.style.color = '#FFF';
editor.innerHTML = 'Edit';
editor.style.width = '100%';
return editor;
}
The button event:
const handleEditButtonClick = (e, c) => {
const el = e.target as HTMLButtonElement;
const data = c.getRow().getData();
console.log(el, data);
}
Tabulator is initialized via a useEffect:
let el = React.createRef();
let tabulator: typeof Tabulator | null = null;
useEffect(() => {
if (el.current) {
tabulator = new Tabulator(el.current, {
data: [...productsData()],
columns: columns,
});
}
}, [el]);
And rendered:
return (
<div ref={el} style={{ width: '100%', height: '100%' }} />
);
The column:
{ formatter: EditButton, cellClick: handleEditButtonClick }
Any ideas?

Set active item automatically

I have a menu in my app:
<?php
NavBar::begin([
'brandLabel' => Html::img('#web/images/logo-top.png', ['id' => 'logo']),
'brandUrl' => Yii::$app->homeUrl,
'options' => [
'class' => 'navbar-inverse navbar-static-top',
],
]);
if (count($menuItems)) {
echo Nav::widget([
'options' => ['class' => 'navbar-nav'],
'items' => $menuItems,
]);
}
?>
<?php NavBar::end(); ?>
$menuItems is generated within a Controller:
private function constructMenu($categories) {
$menu = [];
if (is_array($categories) && (count($categories) > 0)) {
foreach($categories as $key => $category) {
$menu[$key] = [
'label' => $category['name'],
'url' => Url::to([
'category/view',
'slug' => $category['slug']
]),
];
if (is_array($category['children']) && (count($category['children']) > 0)) {
$menu[$key]['items'] = $this->constructMenu($category['children']);
}
}
}
return $menu;
}
Also I have urlManager config:
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
'category/<slug:[\w_-]+>' => 'category/view',
'item/<slug:[\w_-]+>' => 'item/view',
'cart/remove/<item_id:\d+>' => 'cart/remove',
'cart/add/<item_id:\d+>' => 'cart/add',
],
],
So, the only problem is that the menu items are always active = false. How should I modify constructMenu method to set proper value for active key? Or maybe I should do it within the template?
Here is content of $menuItems from PhpStrom debug panel right before it will be passed to Nav::widget:
Here is where I am now:
NavBar::begin([
'brandLabel' => Html::img('#web/images/logo-top.png',
['id' => 'logo', 'style' => 'height: 40px; filter: invert(100%);']),
'brandUrl' => Yii::$app->homeUrl,
'options' => [
'class' => 'navbar-inverse navbar-static-top',
],
]);
$controllerAndSlug = $this->context->id . '/' . $this->context->actionParams['slug'];
$menuItems = array_map(
function($item) use ($controllerAndSlug) {
$item['active'] = strpos($item['url'], $controllerAndSlug) !== false;
return $item;
},
$menuItems
);
if (count($menuItems)) {
echo Nav::widget([
'options' => ['class' => 'navbar-nav'],
'items' => $menuItems,
]);
}
NavBar::end();
It works fine, but sets active flag only on top level menu items. Now I wonder about how to pass a param to callback-function.
Although if you provide the full URL with /controller/action format it should work if it still does not, then you can use the active option of the item by checking the current controller, action, and slug
protected function constructMenu($categories) {
$menu = [];
$controller = Yii::$app->controller->id;
$action = Yii::$app->controller->action->id;
$slug = Yii::$app->request->get('slug');
if( is_array($categories) && (count($categories) > 0) ){
foreach( $categories as $key => $category ){
$isActive = ($controller == 'category' && $action == 'view' && $slug == $category['slug']);
$menu[$key] = [
'label' => $category['name'],
'url' => Url::to([
'category/view',
'slug' => $category['slug']
]),
'active' => $isActive
];
if( is_array($category['children']) && (count($category['children']) > 0) ){
$menu[$key]['items'] = $this->constructMenu($category['children']);
}
}
}
return $menu;
}
Try this:
private function constructMenu($categories) {
$menu = [];
if (is_array($categories) && (count($categories) > 0)) {
foreach($categories as $key => $category) {
$menu[$key] = [
'label' => $category['name'],
'url' => Url::to([
'category/view',
'slug' => $category['slug']
]),
'active' => Yii::$app->controller->id == 'category'
&& Yii::$app->controller->action->id == 'view'
&& Yii::$app->request->get('slug') == $category['slug']
];
if (is_array($category['children']) && (count($category['children']) > 0)) {
$menu[$key]['items'] = $this->constructMenu($category['children']);
}
}
}
return $menu;
}

Proper way to lay out in Xamarin.Forms?

I'm trying to figure out how to properly lay out a few items
on a page in my Xamarin.Forms project.
I need to create a login page and the graphical layout should look something as presented below:
I am guessing that I should use Grid but I have a really hard time figuring out how to use it.
How would I go about to create the presented layout?
Note:
This question isn't about the layout I desire. The layout I desire
would however give me a real-world sample to learn how to properly do
layouts in Xamarin.Forms.
Here is the solution
public partial class ComplexRelativeLayoutPage : ContentPage
{
public ComplexRelativeLayoutPage()
{
InitializeComponent();
RelativeLayout layout = new RelativeLayout();
Label topLabel = new Label
{
Text = "I am a label",
};
layout.Children.Add(topLabel,
Constraint.RelativeToParent((parent) =>
{
return parent.Width / 2 - topLabel.Measure(double.PositiveInfinity, double.PositiveInfinity).Request.Width / 2;
}),
Constraint.Constant(10)
);
Image blueImage = new Image
{
Source= ImageSource.FromResource("ButtonRendererDemo.Resources.test.jpg")
};
layout.Children.Add(blueImage,
Constraint.RelativeToParent((parent) =>
{
return parent.Width / 2 - 300 / 2;
}),
Constraint.RelativeToView(topLabel, (parent, label) =>
{
return label.Bounds.Bottom + 20;
}),
Constraint.Constant(300),
Constraint.Constant(250)
);
Entry e1 = new Entry
{
Placeholder="Input Box 1",
};
layout.Children.Add(e1,
Constraint.RelativeToParent((parent) =>
{
return parent.X + 10;
}),
Constraint.RelativeToView(blueImage, (parent, img) =>
{
return img.Bounds.Bottom + 20;
}),
Constraint.RelativeToParent((parent) =>
{
return parent.Width - 20;
})
);
Entry e2 = new Entry
{
Placeholder = "Input Box 2",
};
layout.Children.Add(e2,
Constraint.RelativeToParent((parent) =>
{
return parent.X + 10;
}),
Constraint.RelativeToView(e1, (parent, e) =>
{
return e.Bounds.Bottom;
}),
Constraint.RelativeToParent((parent) =>
{
return parent.Width - 20;
})
);
Button bLeft = new Button
{
Text = "Button",
BackgroundColor = Color.Pink
};
layout.Children.Add(bLeft,
Constraint.RelativeToParent((parent) =>
{
return parent.X + 20;
}),
Constraint.RelativeToView(e2, (parent, e) =>
{
return e.Bounds.Bottom;
})
);
Button bRight1 = new Button
{
Text = "Button",
BackgroundColor = Color.Pink
};
layout.Children.Add(bRight1,
Constraint.RelativeToParent((parent) =>
{
return parent.Width - bRight1.Measure(double.PositiveInfinity, double.PositiveInfinity).Request.Width - 20;
}),
Constraint.RelativeToView(bLeft, (parent, b) =>
{
return b.Y;
})
);
Button bRight2 = new Button
{
Text = "Button",
BackgroundColor=Color.Pink
};
layout.Children.Add(bRight2,
Constraint.RelativeToView(bRight1, (parent, b) =>
{
return b.X;
}),
Constraint.RelativeToView(bRight1, (parent, b) =>
{
return b.Bounds.Bottom + 10;
})
);
Button bBottom1 = new Button
{
Text = "Button",
BackgroundColor = Color.Lime
};
layout.Children.Add(bBottom1,
Constraint.RelativeToParent((parent) =>
{
return parent.Width / 2 - bBottom1.Measure(double.PositiveInfinity, double.PositiveInfinity).Request.Width / 2;
}),
Constraint.RelativeToView(bRight2, (parent, b) =>
{
return b.Bounds.Bottom + 20;
})
);
Button bBottom2 = new Button
{
Text = "Button",
BackgroundColor = Color.Lime
};
layout.Children.Add(bBottom2,
Constraint.RelativeToView(bBottom1, (parent, b) =>
{
return b.X;
}),
Constraint.RelativeToView(bBottom1, (parent, b) =>
{
return b.Bounds.Bottom + 10;
})
);
ScrollView v = new ScrollView
{
Content=layout
};
Content = v;
}
}
I'd try a StackLayout first
<StackLayout>
<Label />
<Image />
<Entry />
<Entry />
<StackLayout Orientation="Horizontal">
<Button />
<StackLayout>
<Button />
<Button />
</StackLayout>
</StackLayout>
<Button />
<Button />
</StackLayout>

Implementation of Yii2 Menu widget with nested set behavior

Can anyone help me in the implementation of nested set behavior https://github.com/creocoder/yii2-nested-sets with Yii2 Menu Widget?
Basically I want the hierarchical sidebar navigation menu like this http://www.surtex-instruments.co.uk/surgical/general-surgical-instruments
Thanks.
In your Model class define this functions:
public static function getTree($selected, $search)
{
$categories = Categories::find()->orderBy('lft')->asArray()->all();
$tree = [];
$index = 0;
foreach ($categories as $category) {
if ($category['parent_category_id'] === NULL) {
$tree[$index]['text'] = $category['category_' . Yii::$app->language];
if ($search) {
$tree[$index]['href'] = Url::to(['products', 'category' => $category['category_id'], 'search' => $search, 'string' => $category['category_' . Yii::$app->language]]);
} else {
$tree[$index]['href'] = Url::to(['products', 'category' => $category['category_id'], 'string' => $category['category_' . Yii::$app->language] ]);
}
$tree[$index]['id'] = $category['category_id'];
if ($selected) {
if ($selected['category_id'] == $category['category_id']) {
$tree[$index]['state']['selected'] = true;
}
if ($selected['lft'] >= $category['lft'] && $selected['rgt'] <= $category['rgt']) {
$tree[$index]['state']['expanded'] = true;
}
}
if ($category['lft'] + 1 != $category['rgt']) {
Categories::getNodes($tree[$index], $categories, $selected, $search);
}
$index++;
}
}
return $tree;
}
private static function getNodes(&$tree, $categories, $selected, $search)
{
$index = 0;
foreach ($categories as $category) {
if ($tree['id'] == $category['parent_category_id']) {
$tree['nodes'][$index]['text'] = $category['category_' . Yii::$app->language];
if ($search) {
$tree['nodes'][$index]['href'] = Url::to(['products', 'category' => $category['category_id'], 'search' => $search, 'string' => $category['category_' . Yii::$app->language]]);
} else {
$tree['nodes'][$index]['href'] = Url::to(['products', 'category' => $category['category_id'], 'string' => $category['category_' . Yii::$app->language]]);
}
$tree['nodes'][$index]['id'] = $category['category_id'];
if ($selected) {
if ($selected['category_id'] == $category['category_id']) {
$tree['nodes'][$index]['state']['selected'] = true;
}
if ($selected['lft'] >= $category['lft'] && $selected['rgt'] <= $category['rgt']) {
$tree['nodes'][$index]['state']['expanded'] = true;
}
}
if ($category['lft'] + 1 != $category['rgt']) {
Categories::getNodes($tree['nodes'][$index], $categories, $selected, $search);
}
$index++;
}
}
}
and use this extension execut/yii2-widget-bootstraptreeview
In the controller file get the menu like this:
public function actionProducts($category = false)
{
...
$data = Categories::getTree($category,'');
In your view file
<?php
...
use execut\widget\TreeView;
...
$onSelect = new JsExpression(<<<JS
function (undefined, item) {
window.location.href = item.href;
}
JS
);
?>
<?= TreeView::widget([
'data' => $data,
'template' => TreeView::TEMPLATE_SIMPLE,
'clientOptions' => [
'onNodeSelected' => $onSelect,
'onhoverColor' => "#fff",
'enableLinks' => true,
'borderColor' => '#fff',
'collapseIcon' => 'fa fa-angle-down',
'expandIcon' => 'fa fa-angle-right',
'levels' => 1,
'selectedBackColor' => '#fff',
'selectedColor' => '#2eaadc',
],
]); ?>

Telerik MVC grid footer templace

I have a telerik grid and would like to customize the footer template in such a way that the count is right under the sum like this:
Total: $XXXXXXX
Count: XXXXX
#model List
#(Html.Telerik().Grid(Model)
.Name("grd")
.NoRecordsTemplate(" ")
.Resizable(resizing => resizing.Columns(false))
.Scrollable(c => c.Enabled(true).Height(300))
.Footer(false)
.Columns(columns =>
{
columns.Bound(o => o.Number)
.Aggregate(ag => ag.Count());
columns.Bound(o => o.TotalCost)
.Aggregate(ag => ag.Sum())
.ClientFooterTemplate(
"<div>Total: <#= $.telerik.formatString('{0:c}', Sum) #></div>"
);
})
.DataBinding(db => db.Ajax().Select("getList", "Home"))
.Pageable()
.Scrollable()
.Footer(true)
.HtmlAttributes(new { #class = ".t-grid .t-status" })
)
Thanks for your help.
You could try the following. Please be aware it is untested:
.Resizable(resizing => resizing.Columns(false))
.Scrollable(c => c.Enabled(true).Height(300))
.DataBinding(db => db.Ajax().Select("getList", "Home"))
.Pageable()
.Columns(columns =>
{
columns.Bound(o => o.Number)
.Aggregate(ag => ag.Count())
.FooterTemplate(result => (result.Count == null ? "0" : result.Count.Format("{0:N0}")));
columns.Bound(o => o.TotalCost)
.Aggregate(ag => ag.Sum())
.FooterTemplate(result => (result.Sum == null ? "0.00" : result.Sum.Format("{0:N2}")));
})
If you wanted to see the Sum and Count on the footer beneath the same column:
.NoRecordsTemplate("No records available!")
.Resizable(resizing => resizing.Columns(false))
.Scrollable(c => c.Enabled(true).Height("auto"))
.DataBinding(db => db.Ajax().Select("getList", "Home"))
.Pageable()
.Columns(columns =>
{
columns.Bound(o => o.TotalCost)
.HtmlAttributes(new { style = "text-align:right" })
.Format("{0:N2}")
.Aggregate(ag => ag.Sum().Count())
.FooterHtmlAttributes(new { style = "text-align:right" })
.FooterTemplate(result => string.Format("Total: ${0:N2}, Count: {1:N0}", result.Sum , result.Count))
.Width(60);
})

Resources