I would like some advice on this matter. I have a view page that will display a number of users. One view is to display users in a grid (gallery like) of their images. Second view is to display the same users but by their name in a list layout. I will have a toggle button on the page to switch between the two. What is the best way to go about it? Having two separate view pages or have a partial view of some sort?
Update Code after suggestion below
<div data-bind="template: {name:'grid-template'}"></div>
<div data-bind="template: {name:'list-template'}"></div>
<script style="float:left" type="text/html" id ="grid-template">
<section " style="width:100%; float:left">
<section id="users" data-bind="foreach: Users">
<div id="nameImage">
<figure id="content">
<img width="158" height="158" alt="Gravatar" data-bind="attr:{src: GravatarUrl}"/>
<figcaption>
<a title="Email" id="emailIcon" class="icon-envelope icon-white" data-bind="attr:{'href':'mailto:' + Email()}"></a>
<a title="Profile" id="profileIcon" class="icon-user icon-white"></a>
</figcaption>
</figure>
<p data-bind="text:Name"></p>
</div>
</section>
</section>
</script>
<script style="float:left" type="text/html" id="list-template">
<div data-bind="foreach: Users">
<div style="width:60%; float:left; margin:10px; height:58px">
<img style="float:left; margin-right:5px" width="58" height="58" alt="Gravatar" data-bind="attr:{src: GravatarUrl}"/>
<p style="height:58px; float:left; vertical-align:central" data-bind="text:Name"></p>
<a style="float:right" title="Profile" class="icon-user icon-black"></a>
<a style="float:right" title="Email" class="icon-envelope icon-black" data-bind="attr:{'href':'mailto:' + Email()}"></a>
</div>
</div>
</script>
Knockout Script File
$.views.User.UserViewModel = function (data) {
var self = this;
self.Name = ko.observable(data.Name);
self.Email = ko.observable(data.Email);
self.ContentRole = ko.observable(data.ContentRole);
self.MD5Email = ko.observable(data.MD5Email);
self.GravatarUrl = ko.computed(function () {
return 'http://www.gravatar.com/avatar/' + self.MD5Email() + '?s=300&d=identicon&r=G';
});
self.renderMode = ko.observable('grid');
self.displayTemplate = ko.computed(function () {
return self.renderMode() + '-layout-template';
});
};
Personally, I like having clean isolated small little Partial Views especially if it is going to be regular HTTP POST.
However, based on the assumptions I am making below, I think I can suggest a better implementation design.
My Assumption
You have
Index.cshtml Parent view to display a list Users.
JSON object array containing your list of Users
Based on what I see, you are using KnockoutJS.
Read the KnockoutJS Template Binding especially the "Note 5: Dynamically choosing which template is used" part.
It kind of makes it easier to do what you are doing if you are using KnockoutJS or something similar.
You simply have toggle between the two rendering templates.
<script type="text/html" id="gallery-layout-template"> ... </script>
<script type="text/html" id="listing-layout-template"> ... </script>
<div id="divOutputContainer"
data-bind="template: { name: displayTemplate, foreach: users }"></div>
<script type="text/javascript">
$(document).ready(function() {
// I am just writing out a dummy User array here.
// Render out your User Array JSON encoded using JSON.NET.
var myUsers = [
{ "id" : 1, "name": "User 1" },
{ "id" : 2, "name": "User 2" }
];
// here is your KnockoutJS View Model "class"
function MyKoViewModel(users) {
var self = this;
self.users = ko.observableArray(users);
// Toggle this renderMode observable's value
// between 'listing' and 'gallery' via your Toggle button click event handler
self.renderMode = ko.observable( 'gallery' );
self.displayTemplate = function(user) {
// this will return 'gallery-layout-template' or 'listing-layout-template'
return self.renderMode() + '-layout-template';
}
}
ko.applyBindings( new MyKoViewModel( myUsers ) );
});
</script>
So with that technique, you don't need to make an AJAX call every time to refresh the view with a different rendering template.
You have all your data that you want to display as a client-side JavaScript KnockoutJS view model.
Then, just switch the client-side rendering template using KnockoutJS.
Must more efficient :-)
NOTE
I have a feeling, you might have to use the ko.computed() for the MyKoViewModel's displayTemplate() function like this.
self.displayTemplate = ko.computed(function() {
return self.renderMode() + '-layout-template';
}
Related
<div class="container">
#section HeroSection{
#foreach (var category in Model.AllCategory.Where(m => m.IsFeatured).FirstOrDefault().CategoryPictures.Select(m => m.Picture).Distinct())
{
<div class="hero__item set-bg" data-setbg="/FileStore/images/#category.Url">
<div class="hero__text">
<span>FRUIT FRESH</span>
<h2>Vegetable <br />100% Organic</h2>
<p>Free Pickup and Delivery Available</p>
SHOP NOW
</div>
</div>
}
}
</div>
</div>
</div>
}
</div>
I tried to #Render the //#section HeroSection// as false in _layout page .so that it will only appear in home/index ..not in other pages!
I can think of 2 approaches.
the first one is not so practical which is making 2 different layouts, one without the div and one with it. And you can change the layout of the homepage in the razor file like this
#{Layout = "YourNewLayOut"}
The other approach which I think it's better is to use JavaScript.
you can use window.location.href to get the current url and check if it's the url of the home page you target the div and make hidden otherwise visible.
your function should look like this :
function someFunctionName() {
let div = document.querySelector("#yourDiv");
if (window.location.href === "your home page url ") {
div.style.display = "none";
} else {
div.style.display = "block or flex or what ever you're using";
}
}
and then Invoke this function on loading like this
window.onload = function() {
someFunctionName();
};
this is my .cshtml page code,i use jquery repeater is in this code repeater is working fine but i want to add some modification in this repeater but i am stuck here. you can see my code.
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data", #class = "repeater" }))
{
<div data-repeater-list="">
<div data-repeater-item="">
<div class="col-lg-12 col-md-12 col-sm-12">
<input type="file" name="Docfiles" />
</div>
</div>
</div>
<input data-repeater-create type="button" value="Add" />
<button>Save</button>
}
#section Scripts{
<!-- Import repeater js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.repeater/1.2.1/jquery.repeater.js"></script>
<script>
$(document).ready(function () {
$('.repeater').repeater({
// (Optional)
// start with an empty list of repeaters. Set your first (and only)
// "data-repeater-item" with style="display:none;" and pass the
// following configuration flag
initEmpty: true,
// (Optional)
// "show" is called just after an item is added. The item is hidden
// at this point. If a show callback is not given the item will
// have $(this).show() called on it.
show: function () {
$(this).slideDown();
},
// (Optional)
// "hide" is called when a user clicks on a data-repeater-delete
// element. The item is still visible. "hide" is passed a function
// as its first argument which will properly remove the item.
// "hide" allows for a confirmation step, to send a delete request
// to the server, etc. If a hide callback is not given the item
// will be deleted.
hide: function (deleteElement) {
if (confirm('Are you sure you want to delete this element?')) {
$(this).slideUp(deleteElement);
}
},
// (Optional)
// Removes the delete button from the first list item,
// defaults to false.
isFirstItemUndeletable: true
})
});
</script>
}
i want to change html in this formate like this as shown in below screenshot.
Firstly,the name format is set in jquery.repeater.js,if you want to change it,you need to change jquery.repeater.js,here is a demo worked:
1.Add the jquery.repeater.js to your project.
I copy the js to my project like this:
2.Find setIndexes in the jquery and change var newName = groupName + '[' + index + '][' + name + ']' +like this(You can also change it to other format you want):
3.change your script src from cdnjs to your own project:
<script src="~/lib/jquery-repeater/jquery.repeater.js"></script>
4.Result:
I have a mvc 5 project using a database.
In my project I have a Blog-page. This page is loaded using a controller.
In the page I dynamically create a list of items (blogitems) using a simple '#foreach(var item in collection).
Each item has an id (off course), a title and a description.
#foreach (var item in Model.VmBlogItems)
{
<div class="allblogs">
<div class="blogtitle">
<h4>#item.Title</h4>
</div>
<div id="allblogcontent">
#item.Description
</div>
<a id="moreless" href="">Lees meer</a>
</div>
}
My question now is how I can create a button so that when clicked on it the description is hidden or shown.
I also wrote some jquery:
<script type="text/javascript">
//when ready
$(function () {
$('#allblogcontent').hide();
$('#moreless').click(function () {
$('#allblogcontent').toggle();
});
});
</script>
However when I try this, all my items show or hide the Description.
Does anybody know how I make sure that only the Description of the selected item is shown or hidden? I just spend 6 hours reading, trying and sadly enough failing to make it work. I could use some help.
First you modify the markup so each description tag has a unique id attached to it, and the corresponding button has a reference to that id
#{
int i=0;
}
#foreach (var item in Model.VmBlogItems)
{
i++;
<div class="allblogs">
<div class="blogtitle">
<h4>#item.Title</h4>
</div>
<div id="allblogcontent#i" class='someclass'>
#item.Description
</div>
<a id="moreless" href="" data-desc-id='allblogcontent#i'>Lees meer</a>
</div>
}
Your code can figure out which is which now.
<script type="text/javascript">
//when ready
$(function () {
$('.someclass').hide();
$('#moreless').click(function () {
$('#' + $(this).attr('data-desc-id')).toggle();
});
});
</script>
A footnote
This approach will make static references between the tags, so you can move the markup around if you like. The other two solutions shown here, so far, will break if you change the order of the tags.
Just change the below section from
$('#moreless').click(function () {
$('#allblogcontent').toggle();
});
to
$('#moreless').click(function () {
$(this).siblings('#allblogcontent').toggle();
});
I am currently using knockoutjs with one of my MVC applications.
The Layout template looks like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>#ViewBag.Title</title>
#Styles.Render("~/Content/css")
#Scripts.Render("~/bundles/modernizr")
</head>
<body>
<div class="container-fluid head-content">
<div class="row">
<div class="col-xs-6">
<img class="img-responsive" src="~/Images/logo.jpg" />
</div>
<div class="col-xs-3">
<a class="block" href="#" style="display: none" data-bind="visible: showBack, click: goBack">
<div class="block-text">
<h4>Back</h4>
</div>
</a>
</div>
<div class="col-xs-3">
<a class="block" href="#" style="display: none" data-bind="visible: showHome, click: navigateToHome">
<div class="block-text">
<h4>Home</h4>
</div>
</a>
</div>
</div>
</div>
<div class="container-fluid body-content">
#RenderBody()
</div>
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/bootstrap")
#RenderSection("scripts", required: false)
</body>
</html>
and my Index partial looks like this:
#Html.Partial("_Login")
#Html.Partial("_Home")
#Html.Partial("_CutLengths")
#Html.Partial("_MoveStock")
#section scripts {
#Scripts.Render("~/bundles/knockout")
#Scripts.Render("~/bundles/app")
}
My problem is that depending on which page I am on, I would like to use the back button to go to another page. For example, if I am on cutLengths I would want the back button to take me home.
My app.viewmodel.js has a method which looks like this:
// Other operations
self.addViewModel = function (options) {
var viewItem = {},
navigator;
// Add view to AppViewModel.Views enum (for example, app.Views.Home).
self.Views[options.name] = viewItem;
// Add binding member to AppViewModel (for example, app.home);
self[options.bindingMemberName] = ko.computed(function () {
if (self.view() !== viewItem) {
return null;
}
return new options.factory(self, dataModel);
});
if (typeof (options.navigatorFactory) !== "undefined") {
navigator = options.navigatorFactory(self, dataModel);
} else {
navigator = function () {
self.view(viewItem);
};
}
// Add navigation member to AppViewModel (for example, app.NavigateToHome());
self["navigateTo" + options.name] = navigator;
};
What I would like to do is pass a string from the ViewModel I am currently viewing which when the back button is pressed will know to direct me to the right ViewModel.
Is it possible to do this?
I hope I have explained it well, if I haven't please ask and I will try harder :D
You can use either ViewData or ViewBag for passing data from the controller to view. So one option is to just add a few dynamic properties to ViewBag for current view model and prior view model.
ViewData is a dictionary of objects that are stored and retrieved using strings as keys.
ViewBag uses the dynamic feature that was introduced into C# 4.It allows an object to have properties dynamically added to it. I would use this for passing your view model state around.
Neither provide compile time checking, which is the beauty of them, you can add anything you want. With that said it’s always good practice to use strongly typed view models over ViewBag and ViewData.
If you'd rather put something in your view model instead of adding properties to ViewBag, than just add another property in each view model called PreviousViewModel and populate it any time you use the model.
Examples using ViewBag or ViewData
ViewData["LastViewModel"] = "CutLengths";
ViewBag.LastViewModel = "CutLengths";
Access your ViewBag in the Views is no problem, they have global scope. ViewBag is like a global variable that you can attach anything to-- so I'd use them judiciously-- maybe some type of a singleton application manager would be a better design.
Hope this helps
I have solved this now. First I changed my HTML
<div class="col-xs-3">
<a class="block btn-back" href="#" data-bind="visible: showBack, click: goBack"></a>
</div>
<div class="col-xs-3">
<a class="block btn-home" href="#/home" data-bind="visible: showHome"></a>
</div>
Then I edited my app.viewmodel.js file and added these
// Data
self.back = ko.observable(null);
// UI state
self.showBack = ko.observable(true);
self.showHome = ko.observable(true);
self.goBack = function () {
if (self.back()) {
window.location.href = self.back();
} else {
window.history.back();
}
self.back(null); // Reset
};
self.setBackUrl = function (url) {
self.back(url);
}
Then on my addViewModel navigate function, I added this:
if (typeof (options.navigatorFactory) !== "undefined") {
navigator = options.navigatorFactory(self, dataModel);
} else {
navigator = function () { // This is our navigator function which sets the current view
self.showBack(true);
self.showHome(true);
self.error(null); // Reset errors
self.view(viewItem);
};
}
And then in my other view models, I just make a call to setBackUrl like this:
app.setBackUrl("#/cut-lengths");
And if I want to hide my buttons, that is easy too. I just create a navigatorFactory on the viewModel like this:
app.addViewModel({
name: "Home",
bindingMemberName: "home",
factory: HomeViewModel,
navigatorFactory: function (app) {
return function () {
app.showBack(false);
app.showHome(false);
app.error(null);
app.view(app.Views.Home);
}
}
});
I'm trying to implement the select2 on my master layout of my ASP.NET MVC 4 website.
I want it to, as soon as the user starts typing (minimum of 2 letters), call my method to query the database using the letters the user has already typed. Except, when I start typing, my method never gets called. I threw in some alerts and I'm able to see what I'm typing, but the select2 isn't firing, I think.
Here's the script and css references in the tag:
<script src="/Content/select2/select2.js"></script>
<link href="/Content/select2/select2.css" rel="stylesheet" />
Here's the search box in my layout.cshtml file:
<div class="navbar-header hidden-xs pull-right" style="padding-top: 10px; margin-left: 10px;">
<input id="search" class="form-control" placeholder="Search..." type="text" data-autocomplete-url="/Home/AutoFillValues" />
</div>
And here's the bottom of my layout page where the select2 stuff appears:
<script src="/Scripts/jquery-1.9.1.min.js"></script>
<script src="/Scripts/bootstrap.min.js"></script>
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryui")
#Scripts.Render("~/bundles/jqueryval")
#RenderSection("scripts", required: false)
<script type="text/javascript">
$(document).ready(function () {
$("#search").select2({
placeholder: "Search...",
minimumInputLength: 2,
ajax: {
url: "~/Home/AutoFillSearch",
dataType: 'jsonp',
data: function(search) {
return {
q: search
};
},
results: function(search, page) {
return { results: data.Results };
}
}
});
});
</script>
Lastly, here's my controller method (I'm not worried about the code here yet, I just want to see my breakpoint get hit at the first line...):
public ActionResult AutoFillValues()
{
// Get all wrestlers and all teams
var players = _playerRepo.Query().Where(w => w.Division == "Division I" && w.Conference != null);
var schools = players.Select(w => w.School).Distinct();
ArrayList list = new ArrayList();
foreach (var school in schools)
{
var hyperlinkFormat = "{1}";
//#HttpUtility.UrlEncode(Model.Team.Name, System.Text.Encoding.UTF8)
list.Add(string.Format(hyperlinkFormat, string.Format(RosterPath, school), string.Format(RosterText, school)));
}
foreach (var player in playerss)
{
var hyperlinkFormat = "{1}";
//#HttpUtility.UrlEncode(Model.Team.Name, System.Text.Encoding.UTF8)
list.Add(string.Format(hyperlinkFormat, string.Format(RosterPath, player.School), string.Format(RosterText, player.School)));
}
return Json(list.ToArray());
}
I had to upgrade to jQuery 2.1 (because of the issue with version 1.9 and the .live/.on functions) in order for my select2 to start firing.
Now I just need to get my select2 styled...