Is there any GridView in .net MVC core like below MVC webgrid? which support sorting,filtering using Angularjs.
#grid.GetHtml(
tableStyle: "webgrid-table",
headerStyle: "webgrid-header",
footerStyle: "webgrid-footer",
alternatingRowStyle: "webgrid-alternating-row",
selectedRowStyle: "webgrid-selected-row",
rowStyle: "webgrid-row-style",
columns: grid.Columns(
grid.Column("GRNNo"),
grid.Column("InvoiceNo"),
grid.Column("Total",format:(item)=>string.Format("{0:c2}", item.Total)),
grid.Column("InvoiceDate",format:(item)=>string.Format("{0:d}", item.InvoiceDate)),
grid.Column(format: (item) => new HtmlString(
Html.EnActionLink("Details", "Details", "GRNs", new { id = item.ID }, new { #class = "glyphicon glyphicon-eye-open" }).ToHtmlString() + " | " +
Html.EnActionLink((item.IsDeleted == false) ? "Delete" : "Activate", "Delete", "GRNs", new { id = item.ID }, new { #class = (item.IsDeleted == false) ? "glyphicon glyphicon-trash colorred" : "glyphicon glyphicon-open colorspringgreen" }).ToHtmlString()))
)
)
There is a NonFactors.Grid.Mvc6 NuGet package in .net core, have you considered using it? The following are the usage steps and example.
Install NonFactors.Grid.Mvc6 Package.
After successful installation, navigate to the following folder (directory):
`%UserProfile%\.nuget\packages\nonfactors.grid.mvc6\{version}\content`
Then, copy the \css\mvc-grid folder and paste it inside the wwwroot/css folder, copy the \js\mvc-grid folder and paste it inside the wwwroot/js folder, and copy the \Views\Shared\MvcGrid folder and paste it inside the Views/Shared folder:
Then add the following in _Layout.cshtml:
<link href="~/css/mvc-grid/mvc-grid.css" rel="stylesheet" />
<script src="~/js/mvc-grid/mvc-grid.js"></script>
<script>
[].forEach.call(document.getElementsByClassName('mvc-grid'), function (element) {
new MvcGrid(element);
});
</script>
Send data from controller to view:
public IActionResult Index()
{
var data = ...;
//Queryable is required
return View(data.AsQueryable())
}
View:
#using NonFactors.Mvc.Grid
#*IQueryable is required*#
#model IQueryable<Project.Models.TestModel>
#(Html.Grid(Model).Build(columns =>
{
columns.Add(model => model.Id).Titled("ID");
columns.Add(model => model.Name).Titled("Name").Sortable(false);
columns.Add(model => model.Age).Titled("Age");
})
.Using(GridFilterMode.Header)
.Empty("No data found")
.Filterable()
.Sortable()
.Pageable(pager =>
{
pager.PageSizes = new Dictionary<Int32, String> { { 0, "All" }, { 2, "2" }, { 4, "4" } };
pager.ShowPageSizes = true;
pager.PagesToDisplay = 3;
pager.CurrentPage = 2;
pager.RowsPerPage = 2;
})
)
Result:
For more details, you can refer to this link.
Related
We have a web application that is built using JSP pages. We are trying to migrate UI to React. Migration needs to be incremental as it's a huge application and we cannot migrate it completely in one go.
We are trying to run a poc to see how we will integrate react components in phased manner. We are able to integrate a vanilla react component (a static Select) following this React Docs page.
Problem comes when we started using useState hook. We started to get "Invalid Hook Call Warning".
We created a react app and created components there, it works as react application. We converted JSX components to plain JS using Babel cli (steps as mentioned on the React Doc Page).
Next we loaded React and React-DOM in the application through script tag as suggested on the page, except that we downloaded the script and referred from the file system.
<script src="https://unpkg.com/react#18/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#18/umd/react-dom.production.min.js" crossorigin></script>
<script type="text/javascript" src="<path to component JS>"></script>
When we tried to load the Select component in the target DIV element, we got the hook warning.
I extracted code into a sample html
<html>
<head>
<title>My Page</title>
</head>
<body>
<h1>Try React</h1>
<div id="targetDiv">
<h5>Place content here</h5>
</div>
<script type="text/javascript" src="./react/react.development.js"></script>
<script type="text/javascript" src="./react/react-dom.development.js"></script>
<script type="text/javascript" src="./react/components/core/coreSelect.js"></script>
<script type="text/javascript">
function getSelectOptions() {
const options = [];
options.push({ text: "Select...", value: "" });
options.push({ text: "Arizona", value: "AZ" });
options.push({ text: "Canada", value: "CA" });
options.push({ text: "Europe", value: "EU" });
options.push({ text: "Hawai", value: "HW" });
options.push({ text: "Mexico", value: "MX" });
options.push({ text: "New York", value: "NY" });
return options;
};
let selectArgs = {id:"mySelect", name: "mySelect", options: getSelectOptions(), value: "CA"};
let root = document.getElementById('targetDiv');
console.log({root});
ReactDOM.createRoot(root).render(Select(selectArgs));
</script>
</body>
</html>
Following is the content of coreSelect.js
var _slicedToArray = function () {
function sliceIterator(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i)
break;
}
} catch (err) {
_d = true; _e = err;
} finally {
try {
if (!_n && _i["return"])
_i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
return function (arr, i) {
if (Array.isArray(arr)) { return arr; }
else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); }
else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
};
}();
function Select(_ref4) {
var id = _ref4.id,
name = _ref4.name,
value = _ref4.value,
options = _ref4.options;
var optArray = options ? options : [{ text: 'Select', value: '' }];
console.log("Before useState7", {useState});
var _useState7 = React.useState(options ? options : [{ text: 'Select', value: '' }]),
_useState8 = _slicedToArray(_useState7, 2),
optArray = _useState8[0],
setOptArray = _useState8[1];
console.log("Before useState9");
var _useState9 = React.useState(value),
_useState10 = _slicedToArray(_useState9, 2),
selectedVal = _useState10[0],
setSelectedVal = _useState10[1];
console.log("Before useState11");
var _useState11 = React.useState(""),
_useState12 = _slicedToArray(_useState11, 2),
effectiveClasses = _useState12[0],
setEffectiveClasses = _useState12[1];
var disabled = options && options.length > 0 ? false : true;
var onFocusClass = "active";
function processOnClick() {
if (!effectiveClasses || effectiveClasses.search(onFocusClass) < 0) {
setEffectiveClasses(function (prevClasses) {
var newClasses = (prevClasses ? prevClasses.trim() + " " : "") + onFocusClass;
return newClasses;
});
} else {
setEffectiveClasses(function (prevClasses) {
var newClasses = prevClasses.replace(onFocusClass).trim();
return newClasses;
});
}
}
return React.createElement(
"select",
// { id: id, name: name, className: "active", defaultValue: value, onClick: processOnClick, disabled: disabled },
{ id: id, name: name, className: effectiveClasses, defaultValue: selectedVal, onClick: processOnClick, disabled: disabled },
optArray && optArray.map(function (opt) {
var optValue = opt.value;
var optText = opt.text;
return React.createElement(
"option",
{ key: optValue, value: optValue },
optText
);
})
);
};
I have modified the JS file as generated from babel cli to not use imports/exports. I have verified on browser console that React, ReactDOM and Select component are available.
As an experiment I tried to run the command
ReactDOM.createRoot(document.getElementById('targetDiv')).render(Select({id:"mySelect", name: "mySelect", options: getSelectOptions(), value: "CA"}));
from browser console and I still got the react hook error.
I have been trying to search internet to find a solution but all available posts work with npm and try to resolve issues with react version mismatch, but I could not find any that would discuss problem with react integration with existing non-react applications.
Any help in this regard would be greatly appreciated.
I have a component with prop List. List is list of input files. At once input changed I add another one input.
Weird behavior if I try to delete .
https://jsfiddle.net/apokjqxx/115/
removeAnother: function(item) {
var vm = this;
var num = vm.$parent.cornerList.indexOf(item);
vm.$parent.cornerList.splice(num, 1);
},
How to reproduce:
choose file in first input
choose file in second input (will added after step 1)
choose file in third input (will added after step 2)
then click to remove on first item in list
Expected: removed first item but has removed last added
Use a key on your list.
<div v-for="(item, index) in list" :key="item.id">
I modified your fiddle to generate an id for each object added to the cornerList array.
var formuploadimage = Vue.extend({
template: '#template-form-upload-image',
props: {
list: {
type: Array
}
},
data: function() {
return {
isFileChanged: false
}
},
watch: {
validCnt: function() {
},
},
methods: {
onFileChange: function(item) {
var vm = this;
let id = Math.max.apply(Math, vm.$parent.cornerList.map(c => c.id)) + 1
var newItem = {id};
vm.$parent.cornerList.push(newItem);
},
removeAnother: function(item) {
var vm = this;
var num = vm.$parent.cornerList.indexOf(item);
vm.$parent.cornerList.splice(num, 1);
},
},
});
var app = new Vue({
el: ".lists-wrappers",
data: {
cornerList: [{id: 1}],
},
components: {
formuploadimage: formuploadimage
},
methods: {
},
});
.select-file{
width:250px;
border:1px solid red;
}
<script src="https://unpkg.com/vue#2.4.4/dist/vue.js"></script>
<div class="lists-wrappers">
<formuploadimage :list="cornerList"></formuploadimage>
</div>
<script type="text/x-template" id="template-form-upload-image">
<div>
<div v-for="(item, index) in list" :key="item.id">
<div class="select-file">
REMOVE<br/>
<label for="file-input">
+Add photo
</label>
<input type="file" #change="onFileChange(item)" />
</div>
</div>
</div>
</script>
I am adding a plugin which insert twitter embed code. the problem is that I can see the tweet on the editor but not in the source code and preview. And I can't save it. I saw in forum that I have to add 'http:' to '//platform.twitter.com/widgets.js' and put it before , unfortunately, it's not working. This is the code I put:
tinymce.PluginManager.add('twitter', function(editor, url) {
editor.on('init', function (args) {
editor_id = args.target.id;
});
editor.addButton('twitter', {
text: 'Twitter',
icon: false,
onclick: function () {
editor.windowManager.open({
title: 'Twitter Embed',
body: [
{ type: 'textbox',
size: 40,
height: '100px',
name: 'twitter',
label: 'twitter'
}
],
onsubmit: function(e) {
var embedCode = e.data.twitter;
var script = embedCode.match(/<script.*<\/script>/)[0];
var scriptSrc = script.match(/".*\.js/)[0].split("\"")[1];
console.log(script);
var sc = document.createElement("script");
sc.setAttribute("src", "https:"+scriptSrc);
sc.setAttribute("type", "text/javascript");
var iframe = document.getElementById(editor_id + "_ifr");
var iframeHead = iframe.contentWindow.document.getElementsByTagName('head')[0];
var iframeBody = iframe.contentWindow.document.getElementsByTagName('body')[0];
embedCode1 = embedCode.replace('//platform.twitter.com/widgets.js','https://platform.twitter.com/widgets.js');
iframeBody.appendChild(sc);
editor.insertContent(embedCode1);
iframeHead.appendChild(sc);
// setTimeout(function() {
// iframe.contentWindow.twttr.widgets.load();
// }, 1000)
}
});
}
});
});
I am trying to create a list of collapsible containers. The container should collapse and expand. I have created an example list item renderer.
qx.Class.define("mb.ui.list.QuotaWeekListRenderer",
{
extend : qx.ui.mobile.list.renderer.Default,
members :
{
__collapsible : null,
__weeksContainer : null,
_init : function()
{
this.ignoreBase;
this.__collapsible = this._createCollapsible();
this.add(this.__collapsible);
},
setTitle : function(title)
{
this.ignoreBase;
if (title && title.translate)
{
this.__collapsible.setTitle(title.translate());
}
else
{
this.__collapsible.setTitle(title);
}
},
addWeek : function(value)
{
var label = new qx.ui.mobile.basic.Label(value);
this.__collapsible.add(label);
},
_createCollapsible : function()
{
return new qx.ui.mobile.container.Collapsible();
},
// overridden
reset : function()
{
this.ignoreBase;
this.setTitle("");
this.__collapsible.getContent().removeAll();
}
}
});
var page = new qx.ui.mobile.page.NavigationPage();
page.setTitle("List");
page.addListener("initialize", function()
{
// List creation
var list = new qx.ui.mobile.list.List({
configureItem : function(item, data, row)
{
item.setTitle("Week " + parseInt(data.weekNo));
for (var i = 0, l = data.weekDates.length; i < l; i++)
{
item.addWeek(data.weekDates[i]);
}
},
createItemRenderer : function()
{
return new mb.ui.list.QuotaWeekListRenderer();
}
});
// Create the data
var data = [{title: "title1", weekNo: 1, weekDates : ["1/2/2014", "2/2/2014"]},
{title: "title2", weekNo : 2, weekDates : ["2/3/2015", "9/3/2015"]}];
list.setModel(new qx.data.Array(data));
page.getContent().add(list);
},this);
this.getManager().addDetail(page);
page.show();
The above can be run in Playground
My problem is that the items don't expand on 'tap'. Listener toggles the "collapsed" property of the container, but it has no effect on the DOM element. Any ideas how to fix it?
something like this, maybe?
Playground example
Hi I have a problem with $bind, I am binding a model and outputting the models via a ng-repeat. The ng-repeat outputs the stored data and also offers some fields for adding/changing data. The changes are reflected in the scope but are not being synced to Firebase.
Is this a problem with my implementation of $bind?
The HTML:
<iframe id="fpframe" style="border: 0; width: 100%; height: 410px;" ng-if="isLoaded"></iframe>
<form>
<ul>
<li ng-repeat="asset in upload_folder" ng-class="{selected: asset.selected}">
<div class="asset-select"><input type="checkbox" name="selected" ng-model="asset.selected"></div>
<div class="asset-thumb"></div>
<div class="asset-details">
<h2>{{asset.filename}}</h2>
<p><span class="asset-filesize" ng-if="asset.size">Filesize: <strong><span ng-bind-html="asset.size | formatFilesize"></span></strong></span> <span class="asset-filetype" ng-if="asset.filetype">Filetype: <strong>{{asset.filetype}}</strong></span> <span class="asset-dimensions" ng-if="asset.width && asset.height">Dimensions: <strong>{{asset.width}}x{{asset.height}}px</strong></span> <span class="asset-type" ng-if="asset.type">Asset Type: <strong>{{asset.type}}</strong></span></p>
<label>Asset Description</label>
<textarea ng-model="asset.desc" cols="10" rows="4"></textarea>
<label>Creator</label>
<input type="text" ng-model="asset.creator" maxlength="4000">
<label>Release Date</label>
<input type="text" ng-model="asset.release">
<label for="CAT_Category">Tags</label>
<input type="text" ng-model="asset.tags" maxlength="255">
</div>
</li>
</ul>
</form>
The Controller: (fpKey is a constant that stores the Filepicker API key)
.controller('AddCtrl',
['$rootScope', '$scope', '$firebase', 'FBURL', 'fpKey', 'uploadFiles',
function($rootScope, $scope, $firebase, FBURL, fpKey, uploadFiles) {
// load filepicker.js if it isn't loaded yet, non blocking.
(function(a){if(window.filepicker){return}var b=a.createElement("script");b.type="text/javascript";b.async=!0;b.src=("https:"===a.location.protocol?"https:":"http:")+"//api.filepicker.io/v1/filepicker.js";var c=a.getElementsByTagName("script")[0];c.parentNode.insertBefore(b,c);var d={};d._queue=[];var e="pick,pickMultiple,pickAndStore,read,write,writeUrl,export,convert,store,storeUrl,remove,stat,setKey,constructWidget,makeDropPane".split(",");var f=function(a,b){return function(){b.push([a,arguments])}};for(var g=0;g<e.length;g++){d[e[g]]=f(e[g],d._queue)}window.filepicker=d})(document);
$scope.isLoaded = false;
// Bind upload folder data to user account on firebase
var refUploadFolder = new Firebase(FBURL.FBREF).child("/users/" + $rootScope.auth.user.uid + "/upload_folder");
$scope.upload_folder = $firebase(refUploadFolder);
$scope.upload_folder.$bind($scope,'upload_folder');
// default file picker options
$scope.defaults = {
mimetype: 'image/*',
multiple: true,
container: 'fpframe'
};
// make sure filepicker script is loaded before doing anything
// i.e. $scope.isLoaded can be used to display controls when true
(function chkFP() {
if ( window.filepicker ) {
filepicker.setKey(fpKey);
$scope.isLoaded = true;
$scope.err = null;
// additional picker only options
var pickerOptions = {
services:['COMPUTER', 'FACEBOOK', 'GMAIL']
};
var storeOptions = {
location: 'S3',
container: 'imagegrid'
};
var options = $.extend( true, $scope.defaults, pickerOptions );
// launch picker dialog
filepicker.pickAndStore(options, storeOptions,
function(InkBlobs){
uploadFiles.process(InkBlobs, $scope.upload_folder);
},
function(FPError){
$scope.err = FPError.toString();
}
);
} else {
setTimeout( chkFP, 500 );
}
})();
}])
I also have a service handling the input from Filepicker, this creates new entries in the firebase at the reference that is bound (using Firebase methods rather than AngularFire maybe this breaks the binding?)
.service('uploadFiles', ['$rootScope', 'FBURL', function($rootScope, FBURL) {
return {
process: function(InkBlobs, upload_folder) {
var self = this;
var countUpload = 0;
// write each blob to firebase
angular.forEach(InkBlobs, function(value, i){
var asset = {blob: value};
// add InkBlob to firebase one it is uploaded
upload_folder.$add(asset).then( function(ref){
self.getDetails(ref);
countUpload++;
});
});
// wait for all uploads to complete before initiating next step
(function waitForUploads() {
if ( countUpload === InkBlobs.length ) {
self.createThumbs(upload_folder, { multi: true, update: false, location: 'uploads' });
} else {
setTimeout( waitForUploads, 500 );
}
})();
},
getDetails: function(ref) {
// after InkBlob is safely stored we will get additional asset data from it
ref.once('value', function(snap){
filepicker.stat(snap.val().blob, {size: true, mimetype: true, filename: true, width: true, height: true},
function(asset) {
// get asset type and filetype from mimetype
var mimetype = asset.mimetype.split('/');
asset.type = mimetype[0].replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
asset.filetype = mimetype[1];
// add metadata to asset in upload folder
ref.update(asset);
});
});
},
createThumbs: function(ref, options) {
var self = this;
// default options
options.multi = options.multi || false;
options.update = options.update || false;
options.location = options.location || 'asset';
// if pathbase is not provided generate it based on provided location
if (!options.pathbase) {
if (options.location === 'assets') {
options.pathbase = FBURL.LIBRARY + "/assets/";
} else if (options.location === 'uploads') {
options.pathbase = "/users/" + $rootScope.auth.user.uid + "/upload_folder/";
} else {
throw new Error('SERVICE uploadFiles.createThumbs: options.location is not valid must be assets or uploads');
}
}
var generateThumb = function(blob, path) {
filepicker.convert( blob,
{ width: 200, height: 150, fit: 'crop' },
{ location: 'S3', access: 'public', container: 'imagegrid', path: '/thumbs/' },
function(tnInkBlob){
var refThumbBlob = new Firebase(FBURL.FBREF).child(path);
refThumbBlob.set(tnInkBlob);
},
function(FPError){
alert(FPError);
},
function(percentage){
// can use to create progress bar
}
);
};
if (options.multi) {
// look at all assets in provided ref, if thumbnail is mission or update options is true generate new thumb
angular.forEach(ref, function(value, key){
if (typeof value !== 'function' && (!value.tnblob || options.update)) {
// thumb doesn't exist, generate it
var blob = value.blob;
var path = options.pathbase + key + '/tnblob';
generateThumb(blob, path);
}
});
} else {
// getting thumbnail for a single asset
var refAsset = new Firebase(FBURL.FBREF).child(options.pathbase + ref);
var blob = refAsset.val().blob;
var path = options.pathbase + ref + '/tnblob';
generateThumb(blob, path);
}
}
};
}]);
So to recap, data is being saved to /users/$rootScope.auth.user.uid/upload_folder and this is being rendered in the HTML. Changes in the HTML form are reflected in the scope but not in Firebase, despite the binding:
var refUploadFolder = new Firebase(FBURL.FBREF).child("/users/" + $rootScope.auth.user.uid + "/upload_folder");
$scope.upload_folder = $firebase(refUploadFolder);
$scope.upload_folder.$bind($scope,'upload_folder');
Any ideas as to why this is? Is my implementation incorrect or am I somehow breaking the binding? Is $bind even supposed to work with ng-repeat in this manner?
Thanks
Shooting myself for how simple this is, the error was in how I defined the binding. You can't set the binding on itself, you need two separate objects in the scope...
The firebase reference $scope.syncdata loads the initial data and all modifications made to $scope.upload_folder will be synced to firebase.
var refUploadFolder = new Firebase(FBURL.FBREF).child("/users/" + $rootScope.auth.user.uid + "/upload_folder");
$scope.syncdata = $firebase(refUploadFolder);
$scope.syncdata.$bind($scope,'upload_folder');