Kendo Tabstrip with dynamic content in a Partial View - asp.net

I am new to ASP.NET MVC. I am facing one or other issues to design the layout for below scenario. Could you someone help me with a solution and I will definitely appreciate your help.
The requirement is:
This is an existing application. While loading view there is a Master View and inside few partial views already defined.
In one of the Partial view, I need to have a same layout multiple times on demand. It is depends on the user how many required. may be 1 or 2 or more. We are using Telerik Kendo controls extensively in our UI and in existing View we strongly typed Model object with View.
I would like to go with Kendo Tabstrips control and add Tab dynamically when required by the user. Also, the layout is exactly same, So, would like to design (Html table with many controls like textbox, dropdown etc.) each tab layout as Partial View so that I can reuse the design. Please let me know whether this approach is best or any better approach is available.
I need to get the entire data when the user Submit the master view . Each main partial View contains and the parent of the Tabstrips Partial view also contains a but not defined for each tabstrip partial view as I need data as collection of objects in one of the property in Parent Partial View Model Object.
Can you please let me know how to design model object for each tabs(Partial View) as well as Parent Partial View. it could be good, if you could show a small example code.
The below are the issues faced during designing this
Unable to add inside another as getting below error
Inline markup blocks (#Content) cannot be nested. Only one level of inline markup is allowed.
#(Html.Kendo().PanelBar().Name("panelBar_" + panelName).Items(pb => pb.Add().Text("PCG").Expanded(Expanded).Selected(true)
.Content(#<text>
<form id="frm_#(panelName)" onsubmit="DisableEvent(event)">
<div style="width:100%; height:auto;">
<button class="k-button">Add new PCG</button>
#(Html.Kendo().TabStrip()
.Name("TabPCG").HtmlAttributes(new { style = "width:100%;" })
.Items(items =>
{
items.Add()
.Text("PCG 1 <button data-type='remove' class='k-button k-button-icon' onclick='deleteMe(this)'><span class='k-icon k-i-close'></span></button>")
.Encoded(false)
.Selected(true)
.HtmlAttributes(new { style = "width:12%", id = "tabPCG1" })
//.LoadContentFrom("_PCGTab", "Home", new { tabId ="tab1"});
.Content(#<text>#(Html.Partial("_PCGTab"))</text>);
})
)
</div>
</form>
</text>)))
2.Then Changed the design as shown below. defined partial view in Parent View
#helper RenderPCGTab()
{
<div style="width:100%; height:auto;">
<button class="k-button">Add new PCG</button>
#(Html.Kendo().TabStrip()
.Name("TabPCG").HtmlAttributes(new { style = "width:100%;" })
.Items(items =>
{
items.Add()
.Text("PCG 1 <button data-type='remove' class='k-button k-button-icon' onclick='deleteMe(this)'><span class='k-icon k-i-close'></span></button>")
.Encoded(false)
.Selected(true)
.HtmlAttributes(new { style = "width:12%", id = "tabPCG1" })
//.LoadContentFrom("_PCGTab", "Home", new { tabId ="tab1"});
.Content(#<text>#(Html.Partial("_PCGTab"))</text>);
})
)
</div>
}
and designed Kendo panel as shown below the Parent Partial View
#(Html.Kendo().PanelBar().Name("panelBar_" + panelName).Items(pb => pb.Add().Text("PCG").Expanded(Expanded).Selected(true)
.Content(#<text>
<form id="frm_#(panelName)" onsubmit="DisableEvent(event)">
#RenderPCGTab()
</form>
</text>)))

Since you use a strongly typed View, I would recommend using a Tuple as the model.
The Item1 would hold the required model details, while Item2 would hold the required number of tabs (it holds the names of the tabs).
#model Tuple<[Model],List<string>>
Now create a Kendo Tabstrip control, with dynamic items (based on model's Item2)
#(Html.Kendo().TabStrip()
.Name("KendoTabStrip") //You need to dynamically change the name by appending a unique parameter in case you need multiple Tabstrips
.Animation(animation =>
animation.Open(effect =>
effect.Fade(FadeDirection.In)))
.Items(tabstrip =>
{
var TabItemIndex = 0;
foreach (var TabItem in Model.Item2)
{
tabstrip.Add().Text(TabItem)
.Selected(false)
.HtmlAttributes(new { id = "TabStripButton" + TabItem + "_" + TabItemIndex, title = TabItem}) //Generate a dynamic ID for each Tab
.Content(" ");
TabItemIndex++;
}
})
)
Once you have created the structure of the Tabstrip, you need to populate each tab with its corresponding content
In the View (Parent Partial View) itself, create a Ready function for the tabstrip and serialize the object using JSON
$(("KendoTabStrip")).ready(function () {
_TBSModelName = #Html.Raw(JsonConvert.SerializeObject(this.Model.Item1))
TabStripUserControl();
});
Note: This is in case you need the Model Data in your child partial view.
Create a javascript file and place the function TabStripUserControl() in it. This function will create your content and place it into the tab.
function TabStripUserControl()
{
var _LocalTBSModel = _TBSModelName
var items = "#KendoTabStrip" + " .k-tabstrip-items";
$(items).click(function (z) {
}
);
}
Inside the function (click function), create a div and provide a dynamic ID for the same before placing it inside the tab using Javascript/JQuery.
var div = $("<div/>");
Use Ajax call to call your controller, which in turn will call your Child Partial View (which contains HTML controls) and render the partial view inside the above created div on Ajax call's success.
$.ajax({
url: 'Controller/ActionMethod',
data: JSON.stringify({ Value: "SomeValue" }),
type: 'POST',
contentType: 'application/json;',
async: false,
success: function (data) {
div = data;
}
});
Hope this helps.

Related

How to add script to ASP.NET Core partial view properly?

I am new to ASP.NET.
I needed to show a list of items. I made a partial view to represent each item. It works fine.
I needed to add a script in the partial view. So I created a new javascript file in wwwroot > js. Then referenced it from the partial view, like this:
<script src="~/js/testjs.js"></script>
And this is the javascript file:
var upbtn = document.getElementById("upbtn");
var dnbtn = document.getElementById("dnbtn");
upbtn.onclick = upbtn_click;
dnbtn.onclick = dnbtn_click;
function upbtn_click() {
upbtn.children[0].children[0].style.fill = "green";
dnbtn.children[0].children[0].style.fill = "gray"
}
function dnbtn_click() {
upbtn.children[0].children[0].style.fill = "gray";
dnbtn.children[0].children[0].style.fill = "green";
}
The problem is with the script. It only executes fot the first partial view, not for the others.
So, for the first partial view, when I click the buttons, I can see the changes. But for the rest of the partial views, they have no effect. What should I do to make the script work for all partial views?
The project is using ASP.NET Core.
The reason you are seeing your functions only get called for the first partial view is due to how that javascript code works and the fact that the same element id's and javascript is being repeated in each partial view.
For example, the first partial view gets rendered and your onclick events get hooked up for your two elements with id's "upbtn" and "dnbtn". When the second partial view gets rendered (and any others after that), that document.getElementById runs, and finds the first element with that id in the document, which will be that of the first partial. (also element id's should be unique throughout a page, so you should look at ways to get unique id's in repeated partial views after you get this working. I didn't add that to this answer so as to keep focused on this question/answer)
In order to get this working, you need to re-design how the elements in your partials are calling the js functions, and you can move the js out to the parent view or to a single shared js file for your site. Note I'm making some assumptions/estimates on html elements below since you did not share that code.
For your upbtn/dnbtn elements, you can change them to something like below. The "buttonsParentElement" div is so we can get to the other btn elements children upon any click.
<div id="buttonsParentElement">
<div id="upbtn" onclick="upbtn_click(this)">UP</div>
<div id="dnbtn" onclick="dnbtn_click(this)">DOWN</div>
</div>
Then update your js methods (again, not in the partial but either in the parent view, or a shared js file):
function upbtn_click(element) {
element.children[0].children[0].style.fill = "green";
var relatedDnBtn = element.closest("#buttonsParentElement").querySelector("#dnbtn");
relatedDnBtn.children[0].children[0].style.fill = "gray"
}
function dnbtn_click(element) {
var relatedUpBtn = element.closest("#buttonsParentElement").querySelector("#upbtn");
relatedUpBtn.children[0].children[0].style.fill = "gray";
element.children[0].children[0].style.fill = "green";
}

Where to populate multiple dropdown lists

I'm trying to determine the correct approach to populating multiple dropdown lists on a view page of an .net MVC application, which is using angularJS.
In my app, designed as a silo-SLA app. I'm using the angular ngRoute module, and the $routeProvider to describe the multiple HTML pages for a single CSHTML page.
.config(function ($routeProvider, $locationProvider) {
$routeProvider.when('/club', { templateUrl: '/App/Club/Views/MCLView.html', controller: 'mclViewModel' });
$routeProvider.when('/club/list', { templateUrl: '/App/Club/Views/MCLView.html', controller: 'mclViewModel' });
$routeProvider.when('/club/show/:clubId', { templateUrl: '/App/Club/Views/ClubView.html', controller: 'clubViewModel' });
$routeProvider.otherwise({ redirectTo: '/club' });
$locationProvider.html5Mode(true);
});
My CSHTML page is basically empty except for the <div ng-view></div> tag. The routed HTML pages get loaded into the ng-view div.
<div data-ng-app="club" id="club" data-ng-controller="rootViewModel">
<h2>{{ pageHeading }}</h2>
<div ng-view></div>
</div
Each html view page loaded into the ng-view has an associated viewmodel javascript controller.
I started out populating the first dropdown on the html page by using $http.get in the angular controller to get the list data (json) from a server call. Then do a JSON.parse on the result.data and populate the select list control that is defined in the html page.
$scope.refreshColorsDropdown = function () {
var sel = document.getElementById('colorDropdown');
var colors = JSON.parse($scope.mycolors);
angular.forEach(colors, function(color) {
var opt = document.createElement('option');
opt.innerHTML = color.name;
opt.value = color.hex;
sel.appendChild(opt);
});
}
I could continue this approach for each dropdown on my page, creating several server calls to populate all my dropdowns ... but this seems tedious and code heavy.
Would a reasonable approach be to consider building one server calls that returns multiple collections, one for each dropdown, and then I'd parse those to populate all the dropdowns?
I'd like to pre-populate the lists on the server side C# code, however my cshtml files are empty except for the ng-view div, so there's no coding going on there.
So to my question ... if my HTML views contain all the html content, and the cshtml are empty ... what strategy should I be using to populate multiple html list controls from the server side?
Using MVC, you can make a ViewModel that contains all collections needed and return this collection to your Angular Controller.
Your View Model would be something like:
public class MyViewModel
{
public List<ColorModel> Colors{get;set;}
public List<FabircModel> Fabrics {get;set;}
}
In your MVC Controller:
MyViewModel mvm = new MyViewModel();
mvm.Colors = get colors here;
mvm.Fabrics = get fabrics here;
return Json(mvm, JsonRequestBehavior.AllowGet);
In your success object in Angular:
myservice.getData()
.success(function(data){
$scope.colors = data.Colors;
$scope.fabrics = data.Fabrics;
})
Hopefully, that helps or at least gets you started.
If you ignore the fact you are using angular the answer would be to populate a view model with what you need and razor the cshtml, because you are using angular...nothing changes....because you said SERVER-SIDE that is still the answer.
If you move this to the client an angular service that can cache the data is probably the most direct way.

Vary type of input control based on drop down selection?

I'm building an edit screen where a use can edit rows of data. One of the fields is represented by a drop down, and another is an input field named 'value'. Now, depending on the value in the dropdown, I need to have different kinds of input controls for the value input control. Sometimes it should be a text box, others a datetime control (html5 and / or jqUI date picker), and finally a dropdown list containing a fixed set of values ('Yes' / 'No').
So basically sometimes I need to accept any string data, sometimes a date, and sometimes a boolean (but with a select box, not a check box). What's my best option for implementing this? Ideally the value entered would not be lost moving from one kind of input to another, while the user is on this edit page. On post back, I have a single database value to store (its a sql_variant).
Also, I'm using asp.net mvc3 so an ideal solution will work with the normal Html.ValidateFor and Html.ValidationMessageFor methods.
After lot's of time in JSFiddle, I made this solution. And I think it's pretty cool. It wasn't really that hard. and you can adapt it to whatever you need. just click here.
basically I make variables to represent the possible values. then I make a variable to hold the active element.
Whenever the type selector changes, it calls the change() function which uses if() statements to check what was selected, and then it sets the active element accordingly.
And finally, it calls the hide() function which hides the inactive elements.
here is the updated version
RED ALERT: I realized this didn't work in FF (maybe it was just my browser but whatever).
so I fixed it here
The typical way I accomplish something like this is to actually store 3 different fields in the db for each of the different types of values. Then I create something like the following html:
<!-- Input type selector -->
<div class="cell variable-selector">
<select><option ...</select>
</div>
<!-- varied input -->
<div class="cell variable show-text">
<div class="text"><input type="textbox"></div>
<div class="date-picker"><input type="textbox" class="datepicker"></div>
<div class="drop-down-bool"><select><option ...</select>
</div>
Then I have css that hides or shows the correct input element based on which class the cell has:
div.variable div { display:none }
div.show-text div.text { display: inline }
div.show-date-picker div.date-picker {display: inline }
div.show-drop-down-bool div.drop-down-bool {display: inline}
lastly you can setup some javascript so that when you change your variable-selector you change the class of your variable cell. Which jquery one might do this as so:
$(document).ready(function() {
var variableSelector = $("div.variable-selector > select");
variableSelector.change(function() {
var type = $(this).text();
var class = "cell variable show-" + type;
var variableCell = $(this).parent().parent().find("variable");
variableCell.attr("class", class);
})
});
As a quick warning I wrote the above code on the fly in the stack overflow editor window so there might be a couple of syntax errors or a minor bug somewhere but the basic idea should work. Hope it helps.
--Adam
In case you want to make full use of mvc3 validations, consider this approach.
Model
public class MultiValueViewModel
{
[Required]
public string TextValue { get; set; }
[Required]
public bool? BooleanValue { get; set; }
public MultiValueType ValueType { get; set; }
}
public enum MultiValueType
{
Text,
Boolean
}
View
#model MultiValueViewModel
#Html.DropDownListFor(m => m.ValueType, new SelectList(new[]
{
MultiValueType.Text,
MultiValueType.Boolean
}), new { #id = "multi_value_dropdown" })
<p>
<div data-type="#MultiValueType.Text" class="multi-value-pane">
#Html.EditorFor(m => m.TextValue)
</div>
<div style="display: none" data-type="#MultiValueType.Boolean" class="multi-value-pane">
#Html.DropDownListFor(m => m.BooleanValue, new SelectList
(new [] {
new SelectListItem { Text = "Yes", Value = "true"},
new SelectListItem { Text = "No", Value = "false"}
}, "Value", "Text"), optionLabel: "[Not Set]")
</div>
</p>
Javascript:
<script type="text/javascript">
$(function () {
$("#multi_value_dropdown").change(function () {
var value = $(this).val();
$(".multi-value-pane").each(function () {
$(this).css("display", value == $(this).attr("data-type") ? "block" : "none");
});
});
})
Inside your controller, receive MultiValueViewModel value (alone or inside parent model), and based on selected ValueType save to database. Please note that you will need jquery.validate version 1.9 if you need to skip validation on hidden fields (e.g. :hidden).

MVC3 webgrid filtering advice?

I'm trying to figure out the best way (minimal effort) to apply filtering to a webgrid that I have displayed in my Main/Index view (MVC3).
I added a multiselet that would allow filtering by a certain column and I would like to catch the click event (which I already have implemented and working) per select item and then somehow re-invoke my Index() method that contains all the code to rebuild the view based on if it was invoked from a filter (multiselect).
What is the best way to go about this? I know that this is a broad ask but any information would be greatly appreciated.
Thanks!
You could place the multiselect inside a form. Then you have 2 possibilities to submit this form:
Using a submit button
Using the onchange event of the multiselect (in this case you will have to use javascript)
The first point is straightforward:
#using (Html.BeginForm())
{
#Html.ListBoxFor(x => x.SelectedItems, Model.Items)
<button type="submit">Filter</button>
}
To implement the second you could use jQuery and subscribe to the change event of the multiselect. First, let's give this multiselect an id so that we can more easily select it:
#using (Html.BeginForm())
{
#Html.ListBoxFor(x => x.SelectedItems, Model.Items, new { id = "filter" })
}
and then in a separate javascript file:
$(function() {
$('#filter').change(function() {
// when the selection changes we manually trigger the submission
// of the containing form
$(this).closest('form').submit();
});
});
In both cases the controller action that we are submitting to will take an array of strings as argument which will represent the selected values in the multiselect which will be used to filter the resultset.

Rendering partial view dynamically in ASP.Net MVC3 Razor using Ajax call to Action

I'm trying to create a single page form to create a 'work item'. One of the properties is a drop down for 'work item type'.
Depending on the work item type, the user may need to provide additional information in a name-value-pair style attributes grid (property sheet).
I would like to dynamically render the property sheet as soon as a work item type is selected or changed. Once the user provides all information, he would click submit to create the 'work item'.
This is what I have so far:
#using (Ajax.BeginForm("AttributeData", new AjaxOptions() { UpdateTargetId="AttributeDataCell" }))
{
<div style="float:left">
#{
Html.RenderPartial("CreateWorkItemPartialView");
}
</div>
<div id="AttributeDataCell" style="float:right">
#Html.Action("AttributeData", new {id = 1})
</div>
}
The AttributeData action in the controller simply renders the partial view:
public ActionResult AttributeData(int id = 0)
{
var attributes = _workItemDataService.ListWorkItemTypeAttributes(id);
return PartialView("EditWorkItemAttributesPartialView", attributes);
}
Now I would like to hook this up to the drop-down-list's selection event so that the partial view re-renders in the above table cell at every selection change. I would like to pass in the selected value as id.
One way is to force the form to submit itself (and thus re-render).
If that is the right approach, how do we go about it? Esp., how do we make only the property sheet to re-render?
If there is a better way to achieve the above, please indicate.
Thanks
You could subscribe to the .change() event of the dropdown and trigger an AJAX request:
$(function() {
$('#Id_Of_Your_Drop_Down').change(function() {
// This event will be triggered when the dropdown list selection changes
// We start by fetching the form element. Note that if you have
// multiple forms on the page it would be better to provide it
// an unique id in the Ajax.BeginForm helper and then use id selector:
var form = $('form');
// finally we send the AJAX request:
$.ajax({
url: form.attr('action'),
type: form.attr('method'),
data: form.serialize(),
success: function(result) {
// The AJAX request succeeded and the result variable
// will contain the partial HTML returned by the action
// we inject it into the div:
$('#AttributeDataCell').html(result);
}
});
});
});

Resources