Telerik MVC Grid - ClientTemplate() - telerik-mvc

I have a DateTime that is rendering in the grid via ClientTemplate() like this:
/Date(1294030800000)/
I know it is a valid date.
Has anyone seen this or can provide a clue as to what I am doing wrong?

Here is what I did (thanks to SLaks for pointing out that it was a JSON date) which reminded me that the Telerik grid serializes responses as JSON when in Ajax mode.
I created a helper function in my view:
<script type="text/javascript" language="javascript">
function ConvertToDateFromJSON(jsonDate) {
var regex = /-?\d+/;
var numbers = regex.exec(jsonDate);
var d = new Date(parseInt(numbers[0]));
return d;
}
</script>
And then the call to ClientTemplate goes like this:
columns.Bound(model => model.DateAdmitted)
.Template(o => o.DateAdmitted.ToString("d"))
.ClientTemplate(
"<#= $.telerik.formatString('{0:MM/dd/yyyy}', ConvertToDateFromJSON(DateAdmitted)) #>"
);

Related

Convert Quill Delta to HTML

How do I convert Deltas to pure HTML? I'm using Quill as a rich text editor, but I'm not sure how I would display the existing Deltas in a HTML context. Creating multiple Quill instances wouldn't be reasonable, but I couldn't come up with anything better yet.
I did my research, and I didn't find any way to do this.
Not very elegant, but this is how I had to do it.
function quillGetHTML(inputDelta) {
var tempCont = document.createElement("div");
(new Quill(tempCont)).setContents(inputDelta);
return tempCont.getElementsByClassName("ql-editor")[0].innerHTML;
}
Obviously this needs quill.js.
I guess you want the HTML inside it. Its fairly simple.
quill.root.innerHTML
If I've understood you correctly, there's a quill thread of discussion here, with the key information you're after.
I've quoted what should be of most value to you below:
Quill has always used Deltas as a more consistent and easier to use (no parsing)
data structure. There's no reason for Quill to reimplement DOM APIs in
addition to this. quill.root.innerHTML or document.querySelector(".ql-editor").innerHTML works just fine (quill.container.firstChild.innerHTML is a bit more brittle as it depends on child ordering) and the previous getHTML implementation did little more than this.
Simple, solution is here:
https://www.scalablepath.com/blog/using-quill-js-build-wysiwyg-editor-website/
The main code is:
console.log(quill.root.innerHTML);
This is a very common confusion when it comes to Quilljs. The thing is you should NOT retrieve your html just to display it. You should render and display your Quill container just the same way you do when it is an editor. This is one of the major advantages to Quilljs and the ONLY thing you need to do is:
$conf.readOnly = true;
This will remove the toolbar and make the content not editable.
I have accomplished it in the backend using php.
My input is json encoded delta and my output is the html string.
here is the code , if it is of any help to you.This function is still to handle lists though and some other formats but you can always extend those in operate function.
function formatAnswer($answer){
$formattedAnswer = '';
$answer = json_decode($answer,true);
foreach($answer['ops'] as $key=>$element){
if(empty($element['insert']['image'])){
$result = $element['insert'];
if(!empty($element['attributes'])){
foreach($element['attributes'] as $key=>$attribute){
$result = operate($result,$key,$attribute);
}
}
}else{
$image = $element['insert']['image'];
// if you are getting the image as url
if(strpos($image,'http://') !== false || strpos($image,'https://') !== false){
$result = "<img src='".$image."' />";
}else{
//if the image is uploaded
//saving the image somewhere and replacing it with its url
$imageUrl = getImageUrl($image);
$result = "<img src='".$imageUrl."' />";
}
}
$formattedAnswer = $formattedAnswer.$result;
}
return nl2br($formattedAnswer);
}
function operate($text,$ops,$attribute){
$operatedText = null;
switch($ops){
case 'bold':
$operatedText = '<strong>'.$text.'</strong>';
break;
case 'italic':
$operatedText = '<i>'.$text.'</i>';
break;
case 'strike':
$operatedText = '<s>'.$text.'</s>';
break;
case 'underline':
$operatedText = '<u>'.$text.'</u>';
break;
case 'link':
$operatedText = ''.$text.'';
break;
default:
$operatedText = $text;
}
return $operatedText;
}
Here's a full function using quill.root.innerHTML, as the others didn't quite cover the complete usage of it:
function quillGetHTML(inputDelta) {
var tempQuill=new Quill(document.createElement("div"));
tempQuill.setContents(inputDelta);
return tempQuill.root.innerHTML;
}
This is just a slight different variation of km6 's answer.
For Quill version 1.3.6, just use:
quill.root.innerHTML;
Try it online: https://jsfiddle.net/Imabot/86dtuhap/
Detailed explaination on my blog
This link if you have to post the Quill HTML content in a form
quill.root.innerHTML on the quill object works perfectly.
$scope.setTerm = function (form) {
var contents = JSON.stringify(quill.root.innerHTML)
$("#note").val(contents)
$scope.main.submitFrm(form)
}
I put together a node package to convert html or plain text to and from a Quill Delta.
My team used it to update our data model to include both Quill's Delta and HTML. This allows us to render on the client without an instance of Quill.
See node-quill-converter.
It features the following functions:
- convertTextToDelta
- convertHtmlToDelta
- convertDeltaToHtml
Behind the scenes it uses an instance of JSDOM. This may make it best suited for migration scripts as performance has not been tested in a typical app request lifecycle.
Try
console.log ( $('.ql-editor').html() );
Here is how I did it, for you Express folks. It seems to have worked very well in conjunction with express-sanitizer.
app.js
import expressSanitizer from 'express-sanitizer'
app.use(expressSanitizer())
app.post('/route', async (req, res) => {
const title = req.body.article.title
const content = req.sanitize(req.body.article.content)
// Do stuff with content
})
new.ejs
<head>
<link href="https://cdn.quilljs.com/1.3.2/quill.snow.css" rel="stylesheet">
</head>
...
<form action="/route" method="POST">
<input type="text" name="article[title]" placeholder="Enter Title">
<div id="editor"></div>
<input type="submit" onclick="return quillContents()" />
</form>
...
<script src="https://cdn.quilljs.com/1.3.2/quill.js"></script>
<script>
const quill = new Quill('#editor', {
theme: 'snow'
})
const quillContents = () => {
const form = document.forms[0]
const editor = document.createElement('input')
editor.type = 'hidden'
editor.name = 'article[content]'
editor.value = document.querySelector('.ql-editor').innerHTML
form.appendChild(editor)
return form.submit()
}
</script>
express-sanitizer (https://www.npmjs.com/package/express-sanitizer)
document.forms (https://developer.mozilla.org/en-US/docs/Web/API/Document/forms)
My view only has one form, so I used document.forms[0], but if you have multiple or may extend your view in the future to have multiple forms, check out the MDN reference.
What we are doing here is creating a hidden form input that we assign the contents of the Quill Div, and then we bootleg the form submit and pass it through our function to finish it off.
Now, to test it, make a post with <script>alert()</script> in it, and you won't have to worry about injection exploits.
That's all there is to it.
Here is a proper way to do it.
var QuillDeltaToHtmlConverter = require('quill-delta-to-html').QuillDeltaToHtmlConverter;
// TypeScript / ES6:
// import { QuillDeltaToHtmlConverter } from 'quill-delta-to-html';
var deltaOps = [
{insert: "Hello\n"},
{insert: "This is colorful", attributes: {color: '#f00'}}
];
var cfg = {};
var converter = new QuillDeltaToHtmlConverter(deltaOps, cfg);
var html = converter.convert();
Refer https://github.com/nozer/quill-delta-to-html
For a jQuery-style solution that allows getting and setting the Quill value I am doing the following:
Quill.prototype.val = function(newVal) {
if (newVal) {
this.container.querySelector('.ql-editor').innerHTML = newVal;
} else {
return this.container.querySelector('.ql-editor').innerHTML;
}
};
let editor = new Quill( ... );
//set the value
editor.val('<h3>My new editor value</h3>');
//get the value
let theValue = editor.val();
quill-render looks like it's what you want. From the docs:
var render = require('quill-render');
render([
{
"attributes": {
"bold": true
},
"insert": "Hi mom"
}
]);
// => '<b>Hi mom</b>'
If you want to render quill using nodejs, there is a package quite simple based on jsdom, usefull to render backside (only one file & last update 18 days from now) render quill delta to html string on server
Just use this clean library to convert from delta from/to text/html
node-quill-converter
example:
const { convertDeltaToHtml } = require('node-quill-converter');
let html = convertDeltaToHtml(delta);
console.log(html) ; // '<p>hello, <strong>world</strong></p>'

Changing the template data not refreshing the elements

I have searched and tried suggestions mentioned in various posts but no luck so far.
Here is my issue.
I have created a custom element <month-view id="month-view-element"></month-view> in my mainpage.html. Inside mainpage.html when this page is initially loaded i created a empty json object for all the 30days of a month and print a placeholder type cards in UI. Using the code below.
var json = [];
for(var x = 0; x < total; x++) {
json.push({'hours': 0, 'day': x+1, 'year': year});
}
monthView.month = json; //Doing this line. Prints out the desired empty cards for me in the UI.
created a month-view.html something like below:
<dom-module id='month-view'>
<template>
<template is="dom-repeat" items= "{{month}}">
<paper-card class="day-paper-card" heading={{item.day}}>
<div class="card-content work">{{item.work}}</div>
<div class="card-actions containerDay layout horizontal">
<div style="display:inline-block" class="icon">
<paper-icon-button icon="icons:done" data-hours = "8" data-day$="{{item.day}}" data-month$={{item.month}} data-year$={{item.year}} on-click="updateWorkHours"></paper-icon-button>
<paper-tooltip>Full day</paper-tooltip>
</div>
</div>
</paper-card>
</template>
</template>
<script>
Polymer({
is: "month-view",
updateWorkHours: function (e, detail) {
console.log(e);
this.fire('updateWorkHour', {day: e.target.dataHost.dataset.day,
month: e.target.dataHost.dataset.month,
year: e.target.dataHost.dataset.year,
hours: e.target.dataHost.dataset.work
});
}
});
</script>
</dom-module>
There is another file script.js which contains the function document.addEventListener('updateWorkHour', function (e) { // doStuff });. I use this function to make a call to a google client API. I created a client request and then do request.execute(handleCallback);
Once this call is passed i landed in handleCallback function. In this function i do some processing of the response data and save parts of data into json variable available in the file already. And once all processing is done i did something like below.
monthView.month = json;
But this above line is not refreshing my UI with the latest data. Is there anything I am missing? Any suggestions or anything i am doing incorrectly.
You need to use 'set' or 'notifyPath' while changing Polymer Object or Arrays in javascript for the databinding/obserers to work. You can read more about it in https://www.polymer-project.org/1.0/docs/devguide/data-binding.html#path-binding
In your case try below code
monthView.set('month',json);
Updated suggestions:
Wrap your script on main page with. This is required for non-chrome browsers.
addEventListener('WebComponentsReady', function() {})
This could be scoping issue. Try executing 'document.querySelector('#month-view-element');' inside your callback addWorkHoursCallBack. Also, Use .notifyPath instead of .set.

cannot manipulate string with angular filter

I am an angular newbie and I study the book "Angular JS by example" and I try to create my own filter. (pp. 93-94).
In my controller this is the string I want to manipulate
procedure: "Assume a position, with feet together .\Slightly bend your knees, .\While in air, bring your legs out .\ As you are moving your legs outward"
and then I sanitise it
$scope.trustedHtml = $sce.trustAsHtml($scope.currentExercise.details.procedure);
Since this is an SPA , the filter is in the description-panel.ejs file, that is inside workout.ejs, that is inside index.ejs
description-panel.ejs has
<div class="panel-body" ng-bind-html ="trustedHtml | myLineBreakFilter"> </div>
workout.ejs has
<div id="video-panel" class="col-sm-3" ng-include="'description-panel.ejs'">
and index.ejs has
<script src="/javascripts/7MinWorkout/filters.js"></script>
filter.js has the filter
angular.module('7minWorkout').filter('myLineBreakFilter', function () {
return function (input) {
var str = input;
var br = "</br></br>";
var position = str.indexOf(".");
var output = [str.slice(0, position), br, str.slice(position)].join('');
return output ;
}
});
The filter should replace all the . with </br></br>.
This does not work and I get no text at all in my front-end. I get this error in the console
TypeError: str.slice is not a function at filters.js:22
Shouldn't basic js stuff like str.slice be supported out of the box? What am I missing?
Thanks
$sce.trustAsHtml() return you an object so slice will not work on it.You can pass that object to $sce.getTrustedHtml(object) to obtain the original value and then can apply slice on it.
angular.module('7minWorkout').filter('myLineBreakFilter', function ($sce) {
return function (input) {
var str = $sce.getTrustedHtml(input);
var br = "</br></br>";
var position = str.indexOf(".");
var output = [str.slice(0, position), br, str.slice(position)].join('');
return $sce.trustAsHtml(output) ;
}
});
Try this add this before the splice
str.toString();
str.splice(//pass parameters);

Kendo UI Grid Persist state in AngularJS

I have some problems loading the saved state of the grid in Angular.
This is the grids HTML:
<div id="grid" kendo-grid k-options="GridOptions" k-ng-delay="GridOptions"></div>
Later I start my Http call and the $scope.GridOptions are filled in and the grid works fine.
Then I save the state of my grid this way:
$scope.GridOptionsBackup = kendo.stringify($scope.GridOptions);
This works fine and when i print the output in the console. It looks like this:
{"dataSource":{"schema":{"data":"Data"},"transport":{},"serverSorting":true,"table":null,"fields":[{"encoded":true,"field":"WidgetName","title":"Name","template":"#: data.WidgetName#"},{"encoded":true,"field":"WidgetDescription","title":"Description","template":"#: data.WidgetDescription#"}]},"columns":[{"field":"WidgetName","title":"Name","template":"#: data.WidgetName#"},{"field":"WidgetDescription","title":"Description","template":"#: data.WidgetDescription#"}],"sortable":{"mode":"multiple","allowUnsort":true},"scrollable":true}
When i try to reload the grid with the saved state, i read the JSON, parse it and reassign it to $scope.GridOptions. But this don't work:
$scope.GridOptions = JSON.parse($scope.GridOptionsBackup);
Why is the grid not updated after this line of code?
I really appreciate any help you can provide!
I found the answer:
I had to give the kendo-grid a name:
<div kendo-grid="GridBram" k-options="GridOptions" k-ng-delay="GridOptions"></div>
In my Angular code, the name is automatically binded to a scope. There i can use the same (strange) get and setOptions methods that are used in jQuery. I also used a var to store the JSON.
This is my code:
var savedState = null;
$scope.saveO = function () {
savedState = kendo.stringify($scope.GridBram.getOptions());
console.log(test);
}
$scope.loadO = function () {
$scope.GridBram.setOptions(JSON.parse(savedState));
}
Like this, u can save and load the state of your grid in Angular!
Create 2 angular buttons
lt button kendo-button ng-click="save()" gt
Save State A
lt /button gt
lt button kendo-button ng-click="load()" gt
Load State A
lt /button gt
var savedState = null;
$scope.save = function () {
// alert('sav')
savedState = kendo.stringify($scope.GridMAS.getOptions());
}
$scope.load = function () {
//alert('lod')
$scope.GridMAS.setOptions(JSON.parse(savedState));
}
Worked for me

Google Map v3 Ground Overlay?

I have spent two days puzzling this and failed. Any assistance will be greatly appreciated.
I need a map centered on -18.975750, 32.669184 in a canvas of 1500px x 900px. I then to need to overlay coverage PNGs (obtained form www.heywhatsthat.com) with set code- transparency.
I have eventually arrived at the following code and it fails. I would like to add more than one PNG bound by it's co-ords, but cant even get one to work.
<script src="http://maps.google.com/maps?file=api&v=3&key=AIzaSyAGbZjXr2wr7dT2P3O5pNo5wvVF3JiaopU&sensor=false"
type="text/javascript"></script>
<script type="text/javascript">
function initialize() {
if (GBrowserIsCompatible()) {
var map = new google.maps.MAP(document.getElementById("map_canvas"));
map.setCenter(new GLatLng(-18.975750, 32.669184), 13);
map.setUIToDefault();
var imageBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(-19.000417,30.999583),
new google.maps.LatLng(-17.999583,32.000417));
var oldmap = new google.maps.GroundOverlay(
"http://www.earthstation.mobi/cloakpS19E031.png",imageBounds);
oldmap.setMap(map);
}
</script>
</head>
<body onload="initialize()" onunload="GUnload()">
<div id="map_canvas" style="width: 1500px; height: 900px"></div>
</body>
</html>
What am I mssing and please point me in the right direction add multiple png overlays with transparency in the options.
Thanks
Brian
Zimbabwe
You have a lot of issues with your code. It looks like you're trying to migrate from V2 to V3, and you still have V2 methods and objects in your code. You're also not loading the JS APi correctly when you call in the of your HTML.
Here is functional code that displays the overlay using the V3 API, but it looks like the original center coordinates that you used do not place the map at the center of the overlay (you'll need to figure that out yourself). I've added comments where relevant so that you can see where you went astray. Note the call to the API library in the first script tag.
<script src="http://maps.googleapis.com/maps/api/js?key=AIzaSyAGbZjXr2wr7dT2P3O5pNo5wvVF3JiaopU&sensor=false"
type="text/javascript"></script>
<script type="text/javascript">
function initialize() {
//You don't need to use GBrowserIsCompatible, it's only for V2 of the API
//if (GBrowserIsCompatible()) {
//You need to set up options for the map that you reference when you
//instantiate the map object
var myOptions = {
center: new google.maps.LatLng(-18.975750, 32.669184),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
//Your code references google.maps.MAP; it's google.maps.Map
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
var imageBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(-19.000417,30.999583),
new google.maps.LatLng(-17.999583,32.000417));
var oldmap = new google.maps.GroundOverlay(
"http://www.earthstation.mobi/cloakpS19E031.png",imageBounds);
oldmap.setMap(map);
//} <--Your code was missing this closing bracket for the conditional
//But the conditional is not even needed, since GBrowserCompatible is a V2 thing
}
</script>
</head>
<body onload="initialize()">
<div id="map_canvas" style="width: 1500px; height: 900px"></div>
</body>
</html>

Resources