How do I force razor to switch back to client side code? - asp.net-mvc-4

I've got the following code in one of my views
#if (ViewBag.LoginInfo != null)
{
var loginToken = "#ViewBag.LoginInfo.Token";
var loginUser = "#ViewBag.LoginInfo.UserNameJs";
var notifyUrl = "#ViewBag.LoginInfo.NotificationUrl";
}
The code between { } should be rendered to the page as javascript, however it seems to be getting run as serverside code. I'm aware razor switches back to client code when it sees html in this case the code is valid as C# and javascript. How to I force everthing between { } to be written to the page as javasript?
Thanks

Alternatively use #:
#if (ViewBag.LoginInfo != null)
{
#:var loginToken = #Html.Raw(Json.Encode(ViewBag.LoginInfo.Token);
#:var loginUser = #Html.Raw(Json.Encode(ViewBag.LoginInfo.UserNameJs);
#:var notifyUrl = #Html.Raw(Json.Encode(ViewBag.LoginInfo.NotificationUrl);
}

You could wrap them in <text> tags:
#if (ViewBag.LoginInfo != null)
{
<text>
var loginToken = #Html.Raw(Json.Encode(ViewBag.LoginInfo.Token);
var loginUser = #Html.Raw(Json.Encode(ViewBag.LoginInfo.UserNameJs);
var notifyUrl = #Html.Raw(Json.Encode(ViewBag.LoginInfo.NotificationUrl);
</text>
}
Also notice how I have safely encoded the values. Your example will produce invalid javascript if for example your token contains the " character. You should never be mixing javascript and server side values without using a safe serializer as shown in my example.

Related

How to prevent closing of cell edit mode on validation errors with custom vue components in ag-grid

I have succesfully rendered my own component as the cellEditor and would like and on-leave I would like it to try to validate the value and prevent the closing if it fails.
If I look at this then https://www.ag-grid.com/javascript-grid-cell-editing/#editing-api there's cancelable callback functions for editing. But in this callback function is there a way to access the current instantiated component? I would think that would be the easiest way to handle this.
I'm using vee-validate so the validation function is async, just to keep in mind.
Use Full row editing.
Create a global variable like
var problemRow = -1;
Then Subscribe to this events:
onRowEditingStarted: function (event) {
if (problemRow!=-1 && event.rowIndex!=problemRow) {
gridOptions.api.stopEditing();
gridOptions.api.startEditingCell({
rowIndex: problemRow,
colKey: 'the column you want to focus',
});
}
},
onRowEditingStopped: function (event) {
if (problemRow==-1) {
if (event.data.firstName != "your validation") {
problemRow = event.rowIndex
gridOptions.api.startEditingCell({
rowIndex: problemRow,
colKey: 'the column you want to focus',
});
}
}
if (problemRow == event.rowIndex) {
if (event.data.firstName != "your validation") {
problemRow = event.rowIndex
gridOptions.api.startEditingCell({
rowIndex: problemRow,
colKey: 'the column you want to focus',
});
}
else{
problemRow=-1;
}
}
},
I had a similar issue - albeit in AngularJS and the non-Angular mode for ag-grid - I needed to prevent the navigation when the cell editor didn't pass validation.
The documentation is not very detailed, so in the end I added a custom cell editor with a form wrapped around the input field (to handle the niceties such as red highlighting etc), and then used Angular JS validation. That got me so far, but the crucial part was trying to prevent the user tabbing out or away when the value was invalid so the user could at least fix the issue.
I did this by adding a value parser when adding the cell, and then within that if the value was invalid according to various rules, throw an exception. Not ideal, I know - but it does prevent ag-grid from trying to move away from the cell.
I tried loads of approaches to solving this - using the tabToNextCell events, suppressKeyboardEvent, navigateToNextCell, onCellEditingStopped - to name a few - this was the only thing that got it working correctly.
Here's my value parser, for what it's worth:
var codeParser = function (args) {
var cellEditor = _controller.currentCellEditor.children['codeValue'];
var paycodeId = +args.colDef.field;
var paycodeInfo = _controller.paycodes.filter(function (f) { return f.id === paycodeId; })[0];
// Check against any mask
if (paycodeInfo && paycodeInfo.mask) {
var reg = new RegExp("^" + paycodeInfo.mask + '$');
var match = args.newValue.match(reg);
if (!match) {
$mdToast.show($mdToast.simple().textContent('Invalid value - does not match paycode format.').position('top right').toastClass('errorToast'))
.then(function(r) {
_controller.currentCellEditor.children['codeValue'].focus();
});
throw 'Invalid value - does not match paycode format.';
}
}
return true;
};
The _controller.currentCellEditor value is set during the init of the cell editor component. I do this so I can then refocus the control after the error has been shown in the toast:
CodeValueEditor.prototype.init = function (params) {
var form = document.createElement('form');
form.setAttribute('id', 'mainForm');
form.setAttribute('name', 'mainForm');
var input = document.createElement('input');
input.classList.add('ag-cell-edit-input');
input.classList.add('paycode-editor');
input.setAttribute('name', 'codeValue');
input.setAttribute('id', 'codeValue');
input.tabIndex = "0";
input.value = params.value;
if (params.mask) {
input.setAttribute('data-mask', params.mask);
input.setAttribute('ng-pattern','/^' + params.mask + '$/');
input.setAttribute('ng-class',"{'pattern-error': mainForm.codeValue.$error.pattern}");
input.setAttribute('ng-model', 'ctl.currentValue');
}
form.appendChild(input);
this.container = form;
$compile(this.container)($scope);
_controller.currentValue = null;
// This is crucial - we can then reference the container in
// the parser later on to refocus the control
_controller.currentCellEditor = this.container;
$scope.$digest();
};
And then cleared in the grid options onCellEditingStopped event:
onCellEditingStopped: function (event) {
$scope.$apply(function() {
_controller.currentCellEditor = null;
});
},
I realise it's not specifically for your components (Vue.js) but hopefully it'll help someone else. If anyone has done it a better way, I'm all ears as I don't like throwing the unnecessary exception!

VueJS handle URL fragment to action without Vue Router and without jQuery

I am currently using VueJS 2.x and have not gone VueRouter yet (and am not able anyway).
Quite simply, I want Vue to detect a URL fragment like https://example.com/mypage#record-17 and for example simulate the click of the following modal link:
<a :id="record.id" :click="openModal(record.id)">Open this record</a>
Should I just parse window.location myself or is there a more elegant way to do it? I also want to not use jQuery.
This is our script for parsing args from the hash. It lets you put a query string after the hash, that will be parsed by the script. If, like us, you also need to pass an url that you don't want parsed, put it at the end in a 'src' argument.
var args = (function () {
var returnVal = {};
var argString = window.location.hash;
//everything after src belongs as part of the url, not to be parsed
var argsAndSrc = argString.split(/src=/);
returnVal["src"] = argsAndSrc[1];
//everything before src is args for this page.
var argArray = argsAndSrc[0].split("&");
for (var i = 0; i < argArray.length; i++) {
var nameVal = argArray[i].split("=");
//strip the hash
if (i == 0) {
var name = nameVal[0];
nameVal[0] = name.slice(1);
}
returnVal[nameVal[0]] = decodeURI(nameVal[1]);
}
return returnVal
})();

formData get() Doesn't seem to work in Safari

This is my code. It works in Firefox and Chrome but not Safari. I get no errors.
<script>
var cleanData = new FormData();
cleanData.append("test", "test");
alert(cleanData.get("test"));
</script>
Does anyone know a workaround?
Apparently, Safari has no means of getting values stored in FormData objects at this time. There is no workaround at this time, and apparently it's not practical to polyfill.
Sorry :(
Notes:
https://developer.mozilla.org/en-US/docs/Web/API/FormData/get#Browser_compatibility
https://www.bountysource.com/issues/27573236-is-it-possible-to-polyfill-missing-formdata-methods
I solved this by conditionally (if Safari is the browser) iterating through the elements property of an actual form. For all other browser, my wrapper just iterates through FormData entries(). The end result of my function, in either case, is a simple javascript object (JSON) which amounts to name/value pairs.
function FormDataNameValuePairs(FormName)
{
var FormDaytaObject={};
var FormElement=$('#'+FormName).get(0);
if (IsSafariBrowser())
{
var FormElementCollection=FormElement.elements;
//console.log('namedItem='+FormElementCollection.namedItem('KEY'));
var JQEle,EleType;
for (ele=0; (ele < FormElementCollection.length); ele++)
{
JQEle=$(FormElementCollection.item(ele));
EleType=JQEle.attr('type');
// https://github.com/jimmywarting/FormData/blob/master/FormData.js
if ((! JQEle.attr('name')) ||
(((EleType == 'checkbox') || (EleType == 'radio')) &&
(! JQEle.prop('checked'))))
continue;
FormDaytaObject[JQEle.attr('name')]=JQEle.val();
}
}
else
{
var FormDayta=new FormData(FormElement);
for (var fld of FormDayta.entries())
FormDaytaObject[fld[0]]=fld[1];
}
return FormDaytaObject;
}
where IsSafariBrowser() is implemented by whatever your favorite method is, but I chose this:
function IsSafariBrowser()
{
var VendorName=window.navigator.vendor;
return ((VendorName.indexOf('Apple') > -1) &&
(window.navigator.userAgent.indexOf('Safari') > -1));
}
Example usage in OP's case, assuming that you have an actual form called CleanDataForm instead of creating a FormData from scratch:
var cleanData=FormDataNameValuePairs('CleanDataForm');
alert(cleanData.test);

Nativescript Webview callback uri

Can we have post back from external url to web view in nativescript and get the values from postback? It is the oauth2 flow with redirect uri where user display external link of website in native webview and get tokens value from postback url . Any suggestion or pointer to tut or blog? All the major players provide support for this and it is very much used for oauth.
This is my main-page.js where all the tokens and value i get within the function under args.url
var vmModule = require("./main-view-model");
var webViewModule = require('ui/web-view');
function pageLoaded(args) {
var page = args.object;
page.bindingContext = vmModule.mainViewModel;
var webView = page.getViewById('myWebView');
debugger;
//webView.url =
webView.on(webViewModule.WebView.loadFinishedEvent, function (args) {
alert(JSON.stringify(args.url));
});
webView.src = vmModule.mainViewModel.url;
}
exports.pageLoaded = pageLoaded;
And my view is
<Page xmlns="http://www.nativescript.org/tns.xsd" loaded="pageLoaded">
<GridLayout>
<WebView id="myWebView" />
</GridLayout>
</Page>
All the time it was written there in documentation and i just didn't look at it carefully. Hopefully it will help others.
You should be able to watch the urlProperty for changes. E.g.
Given you have a view which looks like this:
<Page loaded="loaded">
<WebView id="myWebView" src="{{ url }}" />
</Page>
Then you can attach an observer to that WebView and react to changes to the URL property like this:
var webViewModule = require('ui/web-view');
function loaded(args) {
var page = args.object;
var webView = page.getViewById('myWebView');
webView.on(webViewModule.WebView.urlProperty, function (changeArgs) {
console.dir(changeArgs);
// Do something with the URL here.
// E.g. extract the token and hide the WebView.
});
}
I Know this is old. But the code below can help a lot of people.
YOUR_WEB_VIEW_OBJECT.on(webViewModule.WebView.loadFinishedEvent, function (args) {
args.object.android.getSettings().setJavaScriptEnabled(true);
args.object.android.evaluateJavascript('(function() { console.log("LOGS"); return "MESSAGE"; })();', new android.webkit.ValueCallback({
onReceiveValue: function (result) {
console.log(result);
}
}));
});
This code currently works for Android. You can create iOS version as well by digging into their APIs Reference then converting it into {N} Suitable.
On IOS you can do it like this:
args.object.ios.stringByEvaluatingJavaScriptFromString('YOUR_JAVASCRIPT_CODE');
Just for the record, now this is how you do it
var webViewNat = this.webView.nativeElement;
this.oLangWebViewInterface = new webViewInterfaceModule.WebViewInterface(webViewNat)
webViewNat.ios.evaluateJavaScriptCompletionHandler(`var myvar = document.getElementById('userNameInput').value = '${getString('Email')}';`, (id, err) => {
if (err) {
return err;
}
return id;
});

Yii renderpartial (proccessoutput = true) Avoid Duplicate js request

Im creating a site who works with ajaxRequest, when I click a link, it will load using ajaxRequest. When I load for example user/login UserController actionLogin, I renderPartial the view with processOUtput to true so the js needed inside that view will be generated, however if I have clientScriptRegister inside that view with events, how can I avoid to generate the scriptRegistered twice or multiple depending on the ajaxRequest? I have tried Yii::app()->clientScript->isSCriptRegistered('scriptId') to check if the script is already registered but it seems that if you used ajaxRequest, the result is always false because it will only be true after the render is finished.
Controller code
if (Yii::app()->request->isAjaxRequest)
{
$this->renderPartial('view',array('model'=>$model),false,true);
}
View Code
if (!Yii::app()->clientScript->isScriptregistered("view-script"))
Yii::app()->clientScript->registerScript("view-script","
$('.link').live('click',function(){
alert('test');
})
");
If I request for the controller for the first time, it works perfectly (alert 1 time) but if I request again for that same controller without refreshing my page and just using ajaxRequest, the alert will output twice if you click it (because it keeps on generating eventhough you already registered it once)
This is the same if you have CActiveForm inside the view with jquery functionality.. the corescript yiiactiveform will be called everytime you renderPartial.
To avoid including core scripts twice
If your scripts have already been included through an earlier request, use this to avoid including them again:
// For jQuery core, Yii switches between the human-readable and minified
// versions based on DEBUG status; so make sure to catch both of them
Yii::app()->clientScript->scriptMap['jquery.js'] = false;
Yii::app()->clientScript->scriptMap['jquery.min.js'] = false;
If you have views that are being rendered both independently and as HTML fragments to be included with AJAX, you can wrap this inside if (Yii::app()->request->isAjaxRequest) to cover all bases.
To avoid including jQuery scripts twice (JS solution)
There's also the possibility of preventing scripts from being included twice on the client side. This is not directly supported and slightly more cumbersome, but in practice it works fine and it does not require you to know on the server side what's going on at the client side (i.e. which scripts have been already included).
The idea is to get the HTML from the server and simply strip out the <script> tags with regular expression replace. The important point is you can detect if jQuery core scripts and plugins have already been loaded (because they create $ or properties on it) and do this conditionally:
function stripExistingScripts(html) {
var map = {
"jquery.js": "$",
"jquery.min.js": "$",
"jquery-ui.min.js": "$.ui",
"jquery.yiiactiveform.js": "$.fn.yiiactiveform",
"jquery.yiigridview.js": "$.fn.yiiGridView",
"jquery.ba-bbq.js": "$.bbq"
};
for (var scriptName in map) {
var target = map[scriptName];
if (isDefined(target)) {
var regexp = new RegExp('<script.*src=".*' +
scriptName.replace('.', '\\.') +
'".*</script>', 'i');
html = html.replace(regexp, '');
}
}
return html;
}
There's a map of filenames and objects that will have been defined if the corresponding script has already been included; pass your incoming HTML through this function and it will check for and remove <script> tags that correspond to previously loaded scripts.
The helper function isDefined is this:
function isDefined(path) {
var target = window;
var parts = path.split('.');
while(parts.length) {
var branch = parts.shift();
if (typeof target[branch] === 'undefined') {
return false;
}
target = target[branch];
}
return true;
}
To avoid attaching event handlers twice
You can simply use a Javascript object to remember if you have already attached the handler; if yes, do not attach it again. For example (view code):
Yii::app()->clientScript->registerScript("view-script","
window.myCustomState = window.myCustomState || {}; // initialize if not exists
if (!window.myCustomState.liveClickHandlerAttached) {
window.myCustomState.liveClickHandlerAttached = true;
$('.link').live('click',function(){
alert('test');
})
}
");
The cleanest way is to override beforeAction(), to avoid any duplicated core script:
class Controller extends CController {
protected function beforeAction($action) {
if( Yii::app()->request->isAjaxRequest ) {
Yii::app()->clientScript->scriptMap['jquery.js'] = false;
Yii::app()->clientScript->scriptMap['jquery-2.0.0.js'] = false;
Yii::app()->clientScript->scriptMap['anything.js'] = false;
}
return parent::beforeAction($action);
}
}
Note that you have to put the exact js file name, without the path.
To avoid including script files twice, try this extension: http://www.yiiframework.com/extension/nlsclientscript/
To avoid attaching event handlers twice, see Jons answer: https://stackoverflow.com/a/10188538/729324