Any Grid suggestions for Breeze? [closed] - breeze

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 9 years ago.
We decided to use Web API, EF + ASP.NET MVC 4 + Knockout + Breeze for our project after a long research.
But we can not find any working grid for Breeze. We want to bind breeze entities to grid and be able to edit data on grid for some scenarios.
We try new grids almost everyday but still no luck,
for example, we tried jqxGrid (from jqWidgets) but it throws an exception while binding data (possibly because of circular references between entities). If we don't use breeze entity and select an anonymous type it works ok.
Do you have any suggestion?
Thanks in advance.

we decided to go with KoGrid, after some research, we could do all we need. Thank you all for help. You may find our test code below. Have a nice day.
<!--3rd party library scripts -->
<script src="~/Scripts/jquery-1.7.1.min.js"></script>
<script src="~/Scripts/json2.js"></script>
<script src="~/Scripts/es5-sham.min.js"></script>
<script src="~/Scripts/es5-shim.min.js"></script>
<script src="~/Scripts/knockout-2.1.0.js"></script>
<script src="~/Scripts/q.js"></script>
<script src="~/Scripts/jquery.json-2.3.js"></script>
<script src="~/Scripts/KoGrid.debug.js"></script>
<script type="text/javascript">
$(document).ready(function () {
var GridViewModel = function () {
var self = this;
self.products = ko.observableArray();
self.currentPage = ko.observable(1);
self.pageSize = ko.observable(10);
self.totalServerItems = ko.observable(80);
self.selectedItem = ko.observable();
self.sortInfo = ko.observable();
self.filterInfo = ko.observable();
self.updateItem = function () { };
var entityModel = window.breeze.entityModel;
var entityManager = new entityModel.EntityManager('api/Service');
var metadataStore = entityManager.metadataStore;
metadataStore.importMetadata($.toJSON(metadata));
var op = window.breeze.FilterQueryOp;
this.getPagedDataAsync = function (pageSize, page, filterInfo, sortInfo) {
var columnName = "ProductID";
if (sortInfo != null)
columnName = sortInfo.column.field + " " + sortInfo.direction;
var query = entityModel.EntityQuery.from("Products").orderBy(columnName).skip((page - 1) * pageSize).take(pageSize);
for (var propertyName in filterInfo) {
query = query.where(propertyName, op.StartsWith, filterInfo[propertyName]);
}
entityManager.executeQuery(query).then(function (data) {
self.products.removeAll();
var items = data.results;
items.forEach(function (item) {
self.products.push(item);
});
});
};
this.dataChangedTrigger = ko.computed(function () {
var page = self.currentPage(),
pageSize = self.pageSize(),
filterInfo = self.filterInfo(),
sortInfo = self.sortInfo();
if (page && pageSize) {
self.getPagedDataAsync(pageSize, page, filterInfo, sortInfo);
}
return null;
});
}
var model = new GridViewModel();
ko.applyBindings(model);
model.getPagedDataAsync(10, 1, model.filterInfo(), model.sortInfo());
});
</script>
<div id="sandBox" class="example" style="height: 600px; width:600px; max-width: 700px;"
data-bind="koGrid: { data: products,
columnDefs: [{ field: 'ProductName', width: 200 },
{ field: 'QuantityPerUnit', width: 200 },
{ field: 'UnitPrice', width: 150 }],
autogenerateColumns: false,
isMultiSelect: false,
enablePaging: true,
useExternalFiltering: true,
useExternalSorting: true,
filterInfo: filterInfo,
sortInfo: sortInfo,
pageSize: pageSize,
pageSizes: [10, 20, 50],
currentPage: currentPage,
totalServerItems: totalServerItems,
selectedItem: selectedItem }">
</div>
<!-- Application scripts -->
<script src="~/Scripts/breeze.js"></script>
<script src="~/Scripts/app/metadata.js"></script>

I was searching for a grid for some time too. I examined jQGrid, koGrid, slickGrid and some more. I am using DataTables with a knockout extension now, which can be found here:
http://datatables.net/forums/discussion/4969/knockoutjs/p1
It can be styled with bootstrap and is highly customizable with templates and much more.
You have to do some linking between breeze and DataTables, but it works very well for me.

It is very easy to have an editable grid with KO.
This is a proof of concept: http://jsfiddle.net/vtortola/wx8cL/
(please don't mind the CSS :D )
Basically, you can have a row template for viewing, and a row template for editing:
<script id="inner-row-tmpl" type="text/html">
<td data-bind="text: par1"></td>
<td data-bind="text: par2"></td>
<td data-bind="text: par3"></td>
<td><button class="edit">Edit</button></td>
</script>
<script id="row-tmpl" type="text/html">
<tr data-bind="template: { name: 'inner-row-tmpl'}">
</tr>
</script>
<script id="editable-inner-row-tmpl" type="text/html">
<td class="editable-row" data-bind="text: par1"></td>
<td><input type="text" data-bind="value: par2"/></td>
<td><input type="text" data-bind="value: par3"/></td>
</script>
Cheers.

Related

Disable year for JQueryUI DatePicker

I'm trying to disable years from my "YearPicker?", it's a DatePicker where you're only able to pick years. I need to disable all years and enable only a range of years (From 2012 to now, for example).
I'm using the JQueryUI DatePicker and it looks like this:
CSS:
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<style>
.ui-datepicker-calendar {
display: none;
}
</style>
JS:
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
$( function() {
$('#datepicker').datepicker({
viewMode: 2,
format: 'yyyy',
maxDate: new Date(new Date().getFullYear(), 11, 31),
minDate: new Date(2012, 0, 1)
});
})
</script>
HTML:
<label>Year: <input class="form-control" type="text" id="datepicker"></label>
You can make what you're doing work, yet I think it's a bit more work than is needed. I would suggest maybe consider using Autocomplete instead. Take a look:
$(function() {
$("#datepicker").autocomplete({
minLength: 0,
source: function(req, resp) {
var today = new Date();
var min = today.getFullYear() - 6;
// Adjust above as needed, currently 6 years before this year
var results = [];
for (min; min <= today.getFullYear(); min++) {
results.push(min.toString());
}
resp(results)
}
}).focus(function(e) {
$(e.target).autocomplete("search", "");
});
})
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<label>Year: <input class="form-control" type="text" id="datepicker" data-years="6"></label>
Can move this to it's own function if needed too.
var function makeYearList(n){
var today = new Date();
var min = today.getFullYear() - n;
var results = [];
for (min; min <= today.getFullYear(); min++) {
results.push(min.toString());
}
return results;
}
You can also adjust the theming to list them horizontally versus vertically.
Hope that helps.
Solved! I was importing Bootstrap DatePicker and JQueryUI DatePicker, I thought that I was using the second one only, but the app was detecting only the first one, my code now is like this and working:
$('#datepicker').datepicker({
minViewMode: 2,
format: 'yyyy',
startDate: '2012',
endDate: 'y'
});
Thanks all for helping!
You could specify simple strings as well to achieve the desired formatting:
{
format: 'MM/yyyy',
startDate: '01/2022',
endDate: '12/2022',
}

Fusion Table Layer Wizard shows search but not map

I found several other people on here had the same issue with the Fusion Table Wizard generating HTML that works in the preview but when you try to embed the code on your own webpage pieces are missing. Mine for example, will show the search bar but not the map. Someone else had the same problem and it turned out that quotes were missing around the id number. But mine does have the appropriate quotes. So I'm wondering what is wrong with my code. I've compared my code to theirs several times looking for what is missing but can't figure it out.
Our webpage is hosted by jimdo, and I have to use the html/widget. I copy and paste the code into that. But it does say that some code requires you to edit the head section. That wouldn't be the problem would it? And if it is, what would I put in the head section and what would I put in the body section?
Here is my code:
<!DOCTYPE html>
<html>
<head>
<style>
#map-canvas { width:500px; height:400px; }
.layer-wizard-search-label { font-family: sans-serif };
</style>
<script type="text/javascript"
src="http://maps.google.com/maps/api/js?sensor=false">
</script>
<script type="text/javascript">
var map;
var layer_0;
var layer_1;
function initialize() {
map = new google.maps.Map(document.getElementById('map-canvas'), {
center: new google.maps.LatLng(45.0817001045586, -89.74025),
zoom: 6
});
var style = [
{
featureType: 'all',
elementType: 'all',
stylers: [
{ saturation: 11 }
]
}
];
var styledMapType = new google.maps.StyledMapType(style, {
map: map,
name: 'Styled Map'
});
map.mapTypes.set('map-style', styledMapType);
map.setMapTypeId('map-style');
layer_0 = new google.maps.FusionTablesLayer({
query: {
select: "col18",
from: "1_rIjSmE6MLjWMF4JH-OIzHFcYCjKr-8eytn9v6Y"
},
map: map,
styleId: 2,
templateId: 3
});
layer_1 = new google.maps.FusionTablesLayer({
query: {
select: "col4",
from: "1ZLOwVcCWsIA-1WNg_yICWjaRdWOTeVy9xpudaQ"
},
map: map
});
}
function changeMap_0() {
var whereClause;
var searchString = document.getElementById('search-string_0').value.replace(/'/g, "\\'");
if (searchString != '--Select--') {
whereClause = "'Counties' CONTAINS IGNORING CASE '" + searchString + "'";
}
layer_0.setOptions({
query: {
select: "col18",
from: "1_rIjSmE6MLjWMF4JH-OIzHFcYCjKr-8eytn9v6Y",
where: whereClause
}
});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="map-canvas"></div>
<div style="margin-top: 10px;">
<label class="layer-wizard-search-label">
Counties
<input type="text" id="search-string_0">
<input type="button" onclick="changeMap_0()" value="Search">
</label>
</div>
</body>
</html>
Can somebody help me, because I have been doing this for days now. And have finally decided to actually ask for help. Any input you have would be greatly appreciated!
Thanks, Angie
Paste this:
<div id="map-canvas" style="width:500px; height:400px;"></div>
<div style="margin-top: 10px;">
<label class="layer-wizard-search-label">
Counties
<input type="text" id="search-string_0">
<input type="button" onclick="changeMap_0()" value="Search">
</label>
</div>
<script type="text/javascript"
src="http://maps.google.com/maps/api/js?sensor=false">
</script>
<script type="text/javascript">
var map;
var layer_0;
var layer_1;
function initialize() {
map = new google.maps.Map(document.getElementById('map-canvas'), {
center: new google.maps.LatLng(45.0817001045586, -89.74025),
zoom: 6
});
var style = [
{
featureType: 'all',
elementType: 'all',
stylers: [
{ saturation: 11 }
]
}
];
var styledMapType = new google.maps.StyledMapType(style, {
map: map,
name: 'Styled Map'
});
map.mapTypes.set('map-style', styledMapType);
map.setMapTypeId('map-style');
layer_0 = new google.maps.FusionTablesLayer({
query: {
select: "col18",
from: "1_rIjSmE6MLjWMF4JH-OIzHFcYCjKr-8eytn9v6Y"
},
map: map,
styleId: 2,
templateId: 3
});
layer_1 = new google.maps.FusionTablesLayer({
query: {
select: "col4",
from: "1ZLOwVcCWsIA-1WNg_yICWjaRdWOTeVy9xpudaQ"
},
map: map
});
}
function changeMap_0() {
var whereClause;
var searchString = document.getElementById('search-string_0').value.replace(/'/g, "\\'");
if (searchString != '--Select--') {
whereClause = "'Counties' CONTAINS IGNORING CASE '" + searchString + "'";
}
layer_0.setOptions({
query: {
select: "col18",
from: "1_rIjSmE6MLjWMF4JH-OIzHFcYCjKr-8eytn9v6Y",
where: whereClause
}
});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
As it seems everything except the contents of <body> will be removed when you paste a complete HTML-source.

JQueryUI dialog as bindable template in KnockoutJS

This question is exposing that one: integrating jquery ui dialog with knockoutjs
I have Model with array of items like this:
var viewModel = {
items: ko.observableArray([])
}
viewModel.items.push(new DialogModel("title 1"));
viewModel.items.push(new DialogModel("title 2"));
viewModel.items.push(new DialogModel("title 3"));
Next I show these items in markup using foreach statement
<div data-bind="foreach: items">
<div data-bind="text: title"></div>
<button data-bind="click: open">Open</button>
<button data-bind="click: close" >Close</button>
</div>
I need to show JQueryUI dialog on clicking buttons and this dialog should be binded to ItemModel instance.
I do not want to include dialog code inside loop because it is copying in result DOM and makes it huge. I'd like to use dialog in template for example.
JSFiddle mockup here http://jsfiddle.net/YmQTW/8/
Any thoughts?
You can create an array that contains only the opened dialogs and bind this array to the template.
With this code only dom of opened dialogs are duplicated.
var DialogModel = function (title) {
var self = this;
self.title = ko.observable(title);
self.isOpen = ko.observable(false);
self.open = function () {
viewModel.shownDialogs.push(self);
setTimeout(function () { self.isOpen(true); }, 0);
};
self.close = function () {
this.isOpen(false);
};
self.isOpen.subscribe(function () {
if(self.isOpen() === false)
viewModel.shownDialogs.remove(self);
})
};
var viewModel = {
items: ko.observableArray([]),
shownDialogs: ko.observableArray([]),
};
The view :
<div data-bind="foreach: shownDialogs">
<div data-bind="template : 'tmpl'"></div>
</div>
See fiddle
I hope it helps.

kendo checkbox checked binding. Not able to bind to an array of checkboxes

I have an array of checkboxes and I would like to use it as an array, for example set single iitems in a group of options and retrieve the values of the group.
For a single checkbox I'm able to set it and get the click event, as an array I don't get anything.
HTML code :
<div class="k-group" id="chkbox-options">
<label>
Red
<input type="checkbox" id="chk1" value="Red" data-bind="checked: colors" />
Green
<input type="checkbox" id="chk2" value="Green" data-bind="checked: colors" />
Blue
<input type="checkbox" id="chk3" value="Blue" data-bind="checked: colors" />
</label>
</div>
Javascript code :
<script type="text/javascript">
var colordata = null;
$(document).ready(function () {
colordata = kendo.observable({
colors: ["Blue"]
});
kendo.bind($("chkbox-options"), colordata);
$("#dump-values").click(function () {
kendoConsole.log(colordata.colors.toString());
});
$("#chk1").click(function () {
kendoConsole.log("click chk1");
if (this.checked) {
kendoConsole.log("click chk1 true");
}
});
});
</script>
I can get the click event on a single checkbox, while I cannot set the values of the checkboxes in kendo.observable in the field var colordata.
I saw a similar example in the kendo documentation but I'm not able to make it work.
Thanks for your help
Marco
couple of points:
1. in kendo.bind # is missing for the div id chkbox-options
2. you need to read the changed colors inside the change event of the the observable object. The change happens after the click event so inside click event you always see the old data.
I have corrected your jsFiddle: http://jsfiddle.net/whizkid747/rPjjJ/4/
var colordata = null;
$(document).ready(function () {
colordata = kendo.observable({
colors: ["Blue"]
});
kendo.bind($("#chkbox-options"), colordata);
colordata.bind("change", function(e) {
var selectedColors = '';
$.each(colordata.colors, function(key, value){
selectedColors = selectedColors + " " + value;
});
if(colordata.colors.length == 0){
console.log('no colors selected');
}else{
console.log('selected colors:' + selectedColors);
}
});
});

knockout.js and jQueryUI to create an accordion menu

Got a slight problem trying to have jquery UI and knockout js to cohoperate. Basically I want to create an accordion with items being added from knockout through a foreach (or template).
The basic code is as follows:
<div id="accordion">
<div data-bind="foreach: items">
<h3></h3>
<div><a class="linkField" href="#" data-bind="text: link"></a></div>
</div>
</div>
Nothing impressive here... The problem is that if I do something like:
$('#accordion').accordion();
The accordion will be created but the inner div will be the header selector (first child, as default) so the effect is not the wanted one.
Fixing stuff with this:
$('#accordion').accordion({ header: 'h3' });
Seems to work better but actually creates 2 accordions and not one with 2 sections... weird.
I have tried to explore knockout templates and using "afterRender" to re-accordionise the div but to no avail... it seems to re-render only the first link as an accordion and not the second. Probably this is due to my beginner knowldge of jquery UI anyway.
Do you have any idea how to make everything work together?
I would go with custom bindings for such functionality.
Just like RP Niemeyer with an example of jQuery Accordion binding to knockoutjs http://jsfiddle.net/rniemeyer/MfegM/
I had tried to integrate knockout and the JQuery UI accordion and later the Bootstrap collapsible accordion. In both cases it worked, but I found that I had to implement a few workarounds to get everything to display correctly, especially when dynamically adding elements via knockout. The widgets mentioned aren't always aware of what is happening with regards to knockout and things can get messed up (div heights wrongly calculated etc...). Especially with the JQuery accordion it tends to rewrite the html as it sees fit, which can be a real pain.
So, I decided to make my own accordion widget using core JQuery and Knockout. Take a look at this working example: http://jsfiddle.net/matt_friedman/KXgPN/
Of course, using different markup and css this could be customized to whatever you need.
The nice thing is that it is entirely data driven and doesn't make any assumptions about layout beyond whatever css you decide to use. You'll notice that the markup is dead simple. This is just an example. It's meant to be customized.
Markup:
<div data-bind="foreach:groups" id="menu">
<div class="header" data-bind="text:name, accordion: openState, click: toggle"> </div>
<div class="items" data-bind="foreach:items">
<div data-bind="text:name"> </div>
</div>
</div>
Javascript:
ko.bindingHandlers.accordion = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
$(element).next().hide();
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var slideUpTime = 300;
var slideDownTime = 400;
var openState = ko.utils.unwrapObservable(valueAccessor());
var focussed = openState.focussed;
var shouldOpen = openState.shouldOpen;
/*
* This following says that if this group is the one that has
* been clicked upon (gains focus) find the other groups and
* set them to unfocussed and close them.
*/
if (focussed) {
var clickedGroup = viewModel;
$.each(bindingContext.$root.groups(), function (idx, group) {
if (clickedGroup != group) {
group.openState({focussed: false, shouldOpen: false});
}
});
}
var dropDown = $(element).next();
if (focussed && shouldOpen) {
dropDown.slideDown(slideDownTime);
} else if (focussed && !shouldOpen) {
dropDown.slideUp(slideUpTime);
} else if (!focussed && !shouldOpen) {
dropDown.slideUp(slideUpTime);
}
}
};
function ViewModel() {
var self = this;
self.groups = ko.observableArray([]);
function Group(id, name) {
var self = this;
self.id = id;
self.name = name;
self.openState = ko.observable({focussed: false, shouldOpen: false});
self.items = ko.observableArray([]);
self.toggle = function (group, event) {
var shouldOpen = group.openState().shouldOpen;
self.openState({focussed: true, shouldOpen: !shouldOpen});
}
}
function Item(id, name) {
var self = this;
self.id = id;
self.name = name;
}
var g1 = new Group(1, "Group 1");
var g2 = new Group(2, "Group 2");
var g3 = new Group(3, "Group 3");
g1.items.push(new Item(1, "Item 1"));
g1.items.push(new Item(2, "Item 2"));
g2.items.push(new Item(3, "Item 3"));
g2.items.push(new Item(4, "Item 4"));
g2.items.push(new Item(5, "Item 5"));
g3.items.push(new Item(6, "Item 6"));
self.groups.push(g1);
self.groups.push(g2);
self.groups.push(g3);
}
ko.applyBindings(new ViewModel());
Is there any reason why you can't apply the accordion widget to the inner div here? For example:
<div id="accordion" data-bind="foreach: items">
<h3></h3>
<div><a class="linkField" href="#" data-bind="text: link"></a></div>
</div>
I attempted the accepted solution and it worked. Just had to make a little change since i was getting following error
Uncaught Error: cannot call methods on accordion prior to initialization; attempted to call method 'destroy'
just had to add following and it worked
if(typeof $(element).data("ui-accordion") != "undefined"){
$(element).accordion("destroy").accordion(options);
}
for details please see Knockout accordion bindings break
You could try this to template it, similar to this:
<div id="accordion" data-bind="myAccordion: { },template: { name: 'task-template', foreach: ¨Tasks, afterAdd: function(elem){$(elem).trigger('valueChanged');} }"></div>
<script type="text/html" id="task-template">
<div data-bind="attr: {'id': 'Task' + TaskId}, click: $root.SelectedTask" class="group">
<h3><b><span data-bind="text: TaskId"></span>: <input name="TaskName" data-bind="value: TaskName"/></b></h3>
<p>
<label for="Description" >Description:</label><textarea name="Description" data-bind="value: Description"></textarea>
</p>
</div>
</script>
"Tasks()" is a ko.observableArray with populated with task-s, with attributes
"TaskId", "TaskName","Description", "SelectedTask" declared as ko.observable();
"myAccordion" is a
ko.bindingHandlers.myAccordion = {
init: function (element, valueAccessor) {
var options = valueAccessor();
$(element).accordion(options);
$(element).bind("valueChanged", function () {
ko.bindingHandlers.myAccordion.update(element, valueAccessor);
});
...
}
What I did was, since my data was being loaded from AJAX and I was showing a "Loading" spinner, I attached the accordion to ajaxStop like so:
$(document).ajaxStart(function(){$("#cargando").dialog("open");}).ajaxStop(function(){$("#cargando").dialog("close");$("#acordion").accordion({heightStyle: "content"});});
Worked perfectly.

Resources