how to use the groovy controller element in JavaScript - grails

I have
def exception = request.exception.stackTraceLines
in Groovy controller. How can I get the value of the exception in the JavaScript.

If you are adding exception to your return like this.
flash.message = message(exception: 'Error: xxx');
you can get it like this
<div class="message" role="status"> ${flash.message} </div>
just use ${ your flash.your_var_name}

You have multiple options to fix this. When using javascript I usually use a wrapper which can hold the error message/stacktrace. It depends on where and how you want to do the error handling. Example:
def book = new Book(params)
book.validate()
render (contentType: "text/json") {[
"data": book,
"errors": book.hasErrors() ? book.errors : null
]}
Then you can check if "errors" has a value when getting back your JSON to determine if there are errors in the input for instance. (Elvis operator probably works too, book.errors ?: null) Other (uncatched) exceptions I handle in the error callback I usually define in my JavaScript. (jQuery mostly, (with malsup jquery.form.js in this case))
$(function() {
$("form").live("submit", function() {
$(this).ajaxSubmit({
error: function (msg) {
/* Catch hard errors here (500's) */
alert("Error occurred: " + msg);
},
success: function(wrapper) {
if (wrapper.errors != null) {
/* Handle model errors here */
} else {
/* Parse data here */
var book = wrapper.data;
}
}
});
return false;
});

You can also do this
render(template:"book",model:[book:theBook, exception:exception])
Or like this (didnt try, don't know if works)
render(template:"book",model:[book:theBook, exception:exception!=null?exception:""])
and then access from the GSP like this
${exception.getMessage()}

Related

Function call and checking results in DustJs

I am combining connect-roles with dust template
ejs template have something like this syntax
<% if (userCan('impersonate')) { %>
<button id="impersonate">Impersonate</button>
<% } %>
and that in jade
if userCan('impersonate')
button#impersonate Impersonate
How to do this in dust?
{#eq key=userCan('edit data') value="true" }
<td><a href='/assets/edit/{.ID_ASSET}'>Edit</a></td>
<td><a href='/assets/delete/{.ID_ASSET}'>Delete</a></td>
{:else}
{/eq}
This code get me an error
Wed, 06 Jan 2016 16:57:47 GMT uncaughtException Expected end tag for assets but it was not found. At line : 42, column : 13
Edit:
I have this in {#contextDump key="full"/}
"tail": {},
"isObject": true,
"head": {
"enrouten": {
"routes": {},
"path": "function path(name, data) {var route;route = this.routes[name];if (typeof route === 'string') {return path2regexp.compile(route)(data);}return undefined;}"
},
"userIs": "function (action) {var act = ert(req, action);return roles.test(req, act)}",
"userCan": "function (action) {var act = ert(req, action);return roles.test(req, act)}",
"isAuthenticated": "function () { [native code] }",
"_csrf": "FSaqN0PWxOF4slTUfnGHXJ0NkPOTJFl0u57eM=",
"title": "Справочник спецификаций",
"assets": [
{
"ID_ASSET": 1,
"SYMBOL_KODE": "12.TR.18",
"DOK_NAME": "ТХ9042",
"DESCRIPTION": "Контроллер программируемый ТХ9042",
"DATE_RELISE": "2001-10-04T21:00:00.000Z",
"POS_KODE": "pos kode 1 ",
And this is my controller
router.get('/', function (req, res) {
var context = {
req: req, // from Express callback
userCan: function(chunk, context, bodies, params) {
var permission = context.resolve(params.permission);
return context.get('req').userCan(permission);
}
}
models.SPR_ASSET.findAll({
include: [ models.SPR_TYPE_UM, models.SPR_TYPE_ASSETS, models.SPR_ASSETS_DS ]
}).then(function(assets) {
res.render('assets', {
title: 'Справочник спецификаций',
assets: assets
context: context
});
});
var context here didn't work
Dust doesn't know what functions are. It only knows about "references", which are keys in your context.
If a key in your context is a function, Dust will invoke the function and use the result in its rendering. Such functions are called context helpers, and you can read about them in the documentation. Alternatively, you can register functions directly with Dust that are accessible anywhere-- these are referred to as global helpers.
If userCan is some sort of global that connect-roles makes available, you might make use of it in a global helper like this:
dust.helpers.userCan = function(chunk, context, bodies, params) {
var permission = context.resolve(params.permission);
return userCan(permission);
}
And in your template:
{#userCan permission="edit data"}
<td><a href='/assets/edit/{.ID_ASSET}'>Edit</a></td>
<td><a href='/assets/delete/{.ID_ASSET}'>Delete</a></td>
{:else}
Please log in.
{/userCan}
Edit: It looks like userCan is request-scoped, so you can use it by adding the request variable to your context. Something like:
var context = {
req: req, // from Express callback
userCan: function(chunk, context, bodies, params) {
var permission = context.resolve(params.permission);
return context.get('req').userCan(permission);
}
}
{#userCan permission="edit data"}
<td><a href='/assets/edit/{.ID_ASSET}'>Edit</a></td>
<td><a href='/assets/delete/{.ID_ASSET}'>Delete</a></td>
{:else}
Please log in.
{/userCan}
To be clear, there is no method to invoke a function directly in your template in Dust-- it does not support arbitrary JS execution. To invoke a function with parameters, you must provide a helper that invokes the function so that it can transform the params into the format that your function expects.

Setting error message in html with knockout validation

I would like to use knockout-validation by only adding validation rules in HTML5, which works great:
http://jsfiddle.net/gt228dgm/1/
I would then like to change the default error messages generated by the browser (like "This field is required." or "Invalid"). This is pretty easily done in javascript code, But I believe this kind of texts should go into the HTML. Is that possible and how? I guess I'm looking for something like:
<input data-bind='value: firstName, validate: { message: "Please enter first name" }' required pattern="^[A-Za-z]{1,255}$"/></label>
I have implemented a custom knockout binding that solves my issue:
ko.bindingHandlers.validate = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var valueBinding = allBindings().value;
var value = valueAccessor();
if (value) {
valueBinding.extend(value);
}
}
};
The binding then looks like this
<input data-bind='value: firstName, validate: { required: { message: 'Full name is missing' }, pattern: { message: 'Full name should be max. 255 alphanumeric characters' } }' required pattern="^[A-Za-z]{1,255}$"/>
Any other suggestions?
I've been playing these days with knockout validation. For your need, you could use validationMessage binding. Here is an example of your code modified, to use this binding: http://jsfiddle.net/elbecita/gt228dgm/4/
Basically, you'd end up having something like:
<span data-bind="validationMessage: firstName, text: 'Your error.'"></span>
<input data-bind='value: firstName, valueUpdate: "input"' required pattern="^[A-Za-z]{1,255}$" />
I've added the valueUpdate binding just to have the validation message shown immediately. And then, in your javascript code, when you define the validation configuration, be sure to turn to false the insertMessages option. If not, you'll see the default message too.
But... I think is easier and cleaner to do this with the knockout extenders in javascript code, having all custom messages centralized in some sort of constants file, instead of having them scattered through the html.
Anyway, dig into the knockout validation bindings, they may help you with your needs. :)
Proper way to handle custom validation message in KO is to create a custom validation not binding.
Here is an example of simple number validation
ko.validation.rules['integerFormat'] = {
validator: function (value, options) {
if (!value) return true;
var regex = /^\d+$/;
var matches = regex.test(value);
if(matches){
value = parseInt(value,10);
return typeof value== "number" && isFinite(value) && value%1===0;
}else{
return false
}
},
message: app.MyCustomIntegerNotValidMSG
}
app.MyCustomIntegerNotValidMSG is just a variable holding your MSG
You would use this in your ViewModel like this
self.myIntegerToValidate = ko.observable().extend({
integerFormat:{}
});;

Backbonejs routing VS emberjs routing

This is how I do my routes in backbonejs where the routing and its params are obtained first before deciding which external template to call. I find this is quite flexible.
var Router = Backbone.Router.extend({
routes: {
//'': 'renderBasic',
':module/:method/': 'renderDynamicViewPageBasic',
':module/:branch/:method/': 'renderDynamicViewPageBranch',
':module/:branch/:method/set:setnumber/page:pagenumber/': 'renderDynamicViewPagePager',
':module/:branch/:method?set=:setnumber&page=:pagenumber': 'renderDynamicViewPagePager'
},
renderDynamicViewPageBasic: function (module,method) {
$(el).html(Handlebars.getTemplate('template1')(data));
},
renderDynamicViewPageBranch: function (module,branch,method) {
$(el).html(Handlebars.getTemplate('template2')(data));
},
renderDynamicViewPagePager: function (module,branch,method,setnumber,pagenumber) {
$(el).html(Handlebars.getTemplate('template3')(data));
}
});
How about in emberjs, can I do the same - do the rout and get its params afirst before deciding which external template to call?
I read the documentation and tested it. It seems to be less flexible - for instance,
App.Router.map(function() {
this.route("about", { path: "/about" });
this.route("favorites", { path: "/favs" });
});
Is it possible to get the route and params and then the controller before getting the template?
if not, it seems to be the same as case using Angularjs which I finally decided not to use it because it gets the template first before sorting out the params.
You can define the template "post params" in EmberJs using the renderTemplate hook, where you can customize which template you'd like to use.
http://emberjs.jsbin.com/oXUqUJAh/1/edit
App.Router.map(function() {
this.route('apple', {path: 'apple/:id'});
});
App.AppleRoute = Ember.Route.extend({
model: function(params) {
return {coolProperty: params.id};
},
renderTemplate: function(controller, model) {
// send in the template name
this.render(model.coolProperty);
}
});
You can pass a function together with $route params to get customized result in angularjs actually.
template: function($params) {
return app.$templateCache.get($params); // or make template yourself from another source
}

How to upload recorded audio (recorder.js-master) through MVC Controller?

I've been working with a great audio recorder which is recorder.js-master.
But I can't make the upload work with MVC Controller.
Here's my javascript function.
function AudioUpload(title, description) {
Recorder.upload({
method: "POST",
url: '#Url.Action("Upload", "Audio")',
audioParam: "Recording",
params: {
"Title": title,
"Description": description
},
success: function (responseText) {
alert(responseText.Success);
},
error: function () {
alert("error");
},
progress: NULL
});
}
Here's my AudioController
public JsonResult Upload(VoicePassage passage)
{
//upload Audio
return Json(new { Success = true });
}
I have a breakpoint in Upload just to know that it's going to my Controller but it doesn't.
Please help.
Here's the reference I follow. http://marc.codewisp.com/2013/05/07/in-browser-audio-recording-recorder-js-asp-net-mvc/?replytocom=12#respond
Regards,
Christian
I'm the author of the original post you linked to.
A few things to try out:
Add a breakpoint to the JavaScript function: if it doesn't hit, something's wrong with your JavaScript.
Check your browser's network activity: In Chrome, under Developer Tools, go to the Network tab and attempt the upload. See if it is hitting the right URL.
Make sure you're using '#Url.Action...' inside a Razor view. It won't work in external JavaScript files.
If you need #Url.Action, you can probably use it in your Razor view, assign it to a temporary global variable and use it in your external JavaScript file.
In your View, add the following before the reference to your external JS:
<script type="text/javascript">
var audioAction = '#Url.Action("Upload", "Audio")';
</script>
In your JavaScript file, change '#Url.Action("Upload", "Audio")' to audioAction, as in:
function AudioUpload(title, description) {
Recorder.upload({
method: "POST",
url: audioAction,
audioParam: "Recording",
params: {
"Title": title,
"Description": description
},
success: function (responseText) {
alert(responseText.Success);
},
error: function () {
alert("error");
},
progress: NULL
});
}

global ajaxError event

i have a global ajaxError event in my base.master, something like this
$(document).ajaxError(function(event, request, settings,thrownError) {
$("#results").append( "<li>some error msg.</li>" );
});
but i dont want to use "#results". i want it to be dynamic. i want the function to always display the error msg in the update-target element. how can i find the UpdateTarget Id that was used from my ajax call in the error event? thanks.
using (Ajax.BeginForm("action", null,
new AjaxOptions {
UpdateTargetId = "results", <--find this element in my error event
LoadingElementId = "loading",
I am not familiar with ASP, but the ajaxError receives all settings that are passed to the ajax method in the aptly named settings variable.
So if you have an AJAX call like this:
$.ajax({
url: '/some/url.asp',
data: myData,
resultsEle: $('#myResults')
});
You can access resultsEle in the ajaxError like this:
$(document).ajaxError(function(event, request, settings, thrownError) {
settings.resultsEle.append( "<li>some error msg.</li>" );
});
Again, I am not familiar with the way you are calling the AJAX method in your ASP code, but my guess would be you can get to the element this way:
$(document).ajaxError(function(event, request, settings, thrownError) {
$('#'+settings.UpdateTargetId).append( "<li>some error msg.</li>" );
});

Resources