Changes to params in grails only seen in controller not in views - model-view-controller

Part of my UrlMappings looks like this:
"/$lang"(controller: "main", action: "front") {
constraints {
lang inList: ['hr', 'sl', 'si']
}
}
Because I want to set lang to 'sl' if it is 'si', I created following filter:
def filters = {
all(controller: '*', action: '*') {
before = {
if(params.lang == 'si') {
params.lang = 'sl'
}
}
}
}
Problem: params.lang inside controller gets the wanted value ('sl'), but in views, params.lang gets resolved to the original value ('si'). What would you suggest to solve this problem?
Thank you!

Try to use redirects. I did this way:
def filters = {
pages(controller: 'pages', action: 'home|services|projects|project_details|contact_us|career|about_us|downloadCaseStudy') {
before = {
if (params.lang) {
if (!(params.lang in grailsApplication.config.i18nFields.locales)) {
session."$SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME" = null
params.remove('lang')
response.sendError(404)
return
}
if (params.lang == grailsApplication.config.defaultLocale) {
RCU.getLocaleResolver(request).setLocale(request, response, new Locale(params.lang))
params.remove('lang')
chain(controller: "pages", action: params.action, model:chainModel?:[:],params: params)
return false
}
} else {
String langCode = RCU.getLocale(request).getLanguage()
if (!(langCode in grailsApplication.config.i18nFields.locales)) {
params.lang = grailsApplication.config.defaultLocale
return
} else if (langCode != grailsApplication.config.defaultLocale) {
params.lang = langCode
chain(controller: params.controller, action: params.action, model:chainModel?:[:], params: params)
}
return true
}
}
after = { Map model ->
}
afterView = { Exception e ->
}
}
A little explanation: I build urls like /ru/about_us.
1.If lang not in list - 404.
2.if lang = grailsApplication.config.defaultLocale show instead of /en/about_us just /about_us.
3. if no lang param provided - resolve from request.

Related

Mongodb aggregation remove null values from object with nested properties

Is there a way to remove literally all null or empty string values from an object? We have an aggregation which creates an object with empty fields and empty objects should the value be null.
What we wish to do is remove all null properties and empty objects and recreate the object, in order to keep the data as small as possible.
e.g. in the following object, only 'test' and 'more-nested-data' should be taken into account, the rest can be removed
{
"test": "some",
"test2": {
},
"test3": {
"some-key": {
},
"some-other-key": {
"more-nested-data": true,
"more-nested-emtpy": null
}
}
}
which should become:
{
"test": "some",
"test3": {
"some-other-key": {
"more-nested-data": true
}
}
}
I tried a lot, but I think by using objectToArray that something could be done, but I have not found the solution yet. The required aggregation should need to recursively (or by defined levels) remove null properties and empty objects.
Use the $function operator available in 4.4 (Aug 2021) to do this recursively as you note. Given this input which is a slightly expanded version of that supplied in the question:
var dd = {
"test": "some",
"test2": { },
"test3": {
"some-key": { },
"some-other-key": {
"more-nested-data": true,
"more-nested-emtpy": null,
"emptyArr": [],
"notEmptyArr": [
"XXX",
null,
{"corn":"dog"},
{"bad":null},
{"other": {zip:null, empty:[], zap:"notNull"}}
]
}
}
}
db.foo.insert(dd);
then this pipeline:
db.foo.aggregate([
{$replaceRoot: {newRoot: {$function: {
body: function(obj) {
var process = function(holder, spot, value) {
var remove_it = false;
// test FIRST since [] instanceof Object is true!
if(Array.isArray(value)) {
// walk BACKWARDS due to potential splice() later
// that will change the length...
for(var jj = value.length - 1; jj >= 0; jj--) {
process(value, jj, value[jj]);
}
if(0 == value.length) {
remove_it = true;
}
} else if(value instanceof Object) {
walkObj(value);
if(0 == Object.keys(value).length) {
remove_it = true;
}
} else {
if(null == value) {
remove_it = true;
}
}
if(remove_it) {
if(Array.isArray(holder)) {
holder.splice(spot,1); // snip out the val
} else if(holder instanceof Object) {
delete holder[spot];
}
}
};
var walkObj = function(obj) {
Object.keys(obj).forEach(function(k) {
process(obj, k, obj[k]);
});
}
walkObj(obj); // entry point!
return obj;
},
args: [ "$$CURRENT" ],
lang: "js"
}}
}}
]);
produces this result:
{
"_id" : 0,
"test" : "some",
"test3" : {
"some-other-key" : {
"more-nested-data" : true,
"notEmptyArr" : [
"XXX",
{
"corn" : "dog"
},
{
"other" : {
"zap" : "notNull"
}
}
]
}
}
}
A convenient way to debug such complex functions is by declaring them as variables outside of the pipeline and running data through them to simulate the documents (objects) coming out the database, e.g.:
ff = function(obj) {
var process = function(holder, spot, value) {
var remove_it = false;
// test FIRST since [] instanceof Object is true!
if(Array.isArray(value)) {
...
printjson(ff(dd)); // use the same doc as above
You can put print and other debugging aids into the code and then when you are done, you can remove them and call the pipeline to process the real data as follows:
db.foo.aggregate([
{$replaceRoot: {newRoot: {$function: {
body: ff, // substitute here!
args: [ "$$CURRENT" ],
lang: "js"
}}
}}
]);
Sounds like the unwind operator would help. Checkout the unwind operator at https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/

How to pass multiple value with key to url using vue.js

I have this attributes data
for(var k = 0;k<this.form.fields.length;k++)
{
this.dynamic_fields.push({attribute_id:attributes[k].id,value: attributes[k].value})
}
this.$router.push({
path: '/api/search-temp',
query:{
attributes: this.encodedAttributes()
}
});
encodedAttributes() {
const queryAttributes =this.dynamic_fields;
if (queryAttributes) {
return typeof queryAttributes !== "string"
? btoa(JSON.stringify(queryAttributes))
: queryAttributes;
}
return "";
},
I have a attribute id and an attribute value so i want to pass this id and value to url so that i cab loop in my controller attributes array and get id and value :
localhost:8000..?attributes[]['attribute_id_1']=attributevalue1&attributes[]['attribute_id_2']=attributevalue2...
I'm redirecting like this :
this.$router.push({ path: '/search-list',query:
{
}
Issue is i want to pass this multidimentional array to url, anyother workaround for this is also highly appreciated
What you may try is to json stringify and encode the object before passing it to the $route query
function encodedAttributes() {
const queryAttributes = this.$route.query.attributes;
if (queryAttributes) {
return typeof queryAttributes !== "string"
? btoa(JSON.stringify(this.$route.query.attributes))
: queryAttributes;
}
return "";
}
function decodedAttributes() {
const attributes = this.$route.query.attributes;
if (typeof attributes === "string" && attributes.length) {
return JSON.parse(atob(attributes));
} else {
return attributes;
}
}
And pass as query parameters to the route
this.$router.push({
path: '/search-list',
query:{
attributes: this.encodedAttributes()
}
Then in the Controller you can decode the attributes value from request data to get the associated array
class MyController extends Controller
{
public function index(Request $request)
{
$request->attributes = is_array(
$requestAttributes = json_decode(base64_decode($request->attributes), true)
)
? $requestAttributes
: [];
//Do other processing as needed
}
}
Had used something similar in one of my projects can't get my hands on the code right now.
Probably you can use function to escape unicode characters in the encodedAttributes as well as decodedAttributes if need be
function escapeUnicode(str){
return str.replace(/[^\0-~]/g, c => '\\u' + ('000' + c.charCodeAt().toString(16)).slice(-4))
}
function encodedAttributes() {
const queryAttributes = this.$route.query.attributes;
if (queryAttributes) {
return typeof queryAttributes !== "string"
? btoa(escapeUnicode(JSON.stringify(this.$route.query.attributes)))
: queryAttributes;
}
return "";
}
function decodedAttributes() {
const attributes = this.$route.query.attributes;
if (typeof attributes === "string" && attributes.length) {
return JSON.parse(atob(escapeUnicode(attributes)));
} else {
return attributes;
}
}
You're trying to set a nested object to the query params, it's not possible... your route's query object must be a flat object.
Summarizing the only way for you to have something like this:
?attributes[]['attribute_id_1']=attributevalue1&attributes[]['attribute_id_2']=attributevalue2
Would be from a query object like this:
query: {
"attributes[]['attribute_id_1']": 'attributevalue1',
"attributes[]['attribute_id_2']": 'attributevalue2',
}
You should flatten this multidimensional array into an simples object and use it as your query object.
Here is an example...
From this:
const multiDimArr = [
['attribute_1', 'value1'],
['attribute_2', 'value2']
];
Into:
const myObject = {
attribute_1: 'value1',
attribute_2: 'value2'
}
A way to do so would be:
const multiDimArr = [
['attribute_1', 'value1'],
['attribute_2', 'value2']
];
const myObject = {};
multiDimArr.forEach(arr => {
myObject[arr[0]] = arr[1];
});
And then use the object as the query object, so your url will look like this:
?attribute_1=value1&attribute_2=value2

How can I override jasmine's buildExpectationResult in order to modify message() function?

I am using protractor for my e2e tests and jasmine2 as framework. I am using a plugin for html reporter with screenshots ( html-report for protractor ).
In these reports there will be shown a list of all failed/passed expects. When the expect fails I get a descriptive message of the expectation. However when the expect passes I only see the word: Passed. The reason behind that is that jasmine overrides the message when the expect passes.
That is done in the following file:
node_modules/protractor/node_modules/jasmine/node_modules/jasmine-core/lib/jasmine-core/jasmine.js
getJasmineRequireObj().buildExpectationResult = function () {
function buildExpectationResult(options) {
var messageFormatter = options.messageFormatter || function () {
},
stackFormatter = options.stackFormatter || function () {
};
var result = {
matcherName: options.matcherName,
message: message(),
stack: stack(),
passed: options.passed
};
if (!result.passed) {
result.expected = options.expected;
result.actual = options.actual;
}
return result;
function message() {
if (options.passed) {
// Here is the message overriden
return 'Passed.';
} else if (options.message) {
return options.message;
} else if (options.error) {
return messageFormatter(options.error);
}
return '';
}
function stack() {
if (options.passed) {
return '';
}
var error = options.error;
if (!error) {
try {
throw new Error(message());
} catch (e) {
error = e;
}
}
return stackFormatter(error);
}
}
return buildExpectationResult;
};
What I wanted is to override this function in my protractor protractor.conf.js file. And replace it with one with the desired behaviour.
I've tried to do so unsuccessfully doing the following:
onPrepare: function () {
jasmine.buildExpectationResult = function () {
function buildExpectationResult(options) {
var messageFormatter = options.messageFormatter || function () {
},
stackFormatter = options.stackFormatter || function () {
};
return {
matcherName: options.matcherName,
expected: options.expected,
actual: options.actual,
message: message(),
stack: stack(),
passed: options.passed
};
function message() {
if (options.message) {
return options.message;
} else if (options.error) {
return messageFormatter(options.error);
}
return "";
}
function stack() {
if (options.passed) {
return "";
}
var error = options.error;
if (!error) {
try {
throw new Error(message());
} catch (e) {
error = e;
}
}
return stackFormatter(error);
}
}
return buildExpectationResult;
};
}
Then my questions is: What is the right way to override a jasmine method?
Since we use gulp task to run protractor tests, we override the lib (like jasmine lib) as one of the gulp task with custom copy. We do that as part of installation or every test execution.
I didn't find any good way to override it unless we create another npm module.
I had the same issue, I'm not sure if my solution
onPrepare: function () {
// ...
jasmine.Spec.prototype.addExpectationResult = function(passed, data, isError) {
var buildExpectationResult = function(options) {
var messageFormatter = options.messageFormatter || function() {},
stackFormatter = options.stackFormatter || function() {};
var result = {
matcherName: options.matcherName,
message: message(),
stack: stack(),
passed: options.passed
};
if(!result.passed) {
result.expected = options.expected;
result.actual = options.actual;
}
return result;
function message() {
if (options.passed) {
return options.message ? options.message : 'Passed';
} else if (options.message) {
return options.message;
} else if (options.error) {
return messageFormatter(options.error);
}
return '';
}
function stack() {
if (options.passed) {
return '';
}
var error = options.error;
if (!error) {
try {
throw new Error(message());
} catch (e) {
error = e;
}
}
return stackFormatter(error);
}
}
var exceptionFormatter = jasmine.ExceptionFormatter;
var expectationResultFactory = function(attrs) {
attrs.messageFormatter = exceptionFormatter.message;
attrs.stackFormatter = exceptionFormatter.stack;
return buildExpectationResult(attrs);
}
var expectationResult = expectationResultFactory(data);
if (passed) {
this.result.passedExpectations.push(expectationResult);
} else {
this.result.failedExpectations.push(expectationResult);
if (this.throwOnExpectationFailure && !isError) {
throw new j$.errors.ExpectationFailed();
}
}
};
// ...
}

How can I automatically map a json object to fields based off a viewmodel mapped to fields?

I have a view that is loaded with a blank viewmodel initially. I want to populate that already rendered view with a json object (obtained view ajax post) that was based off the viewmodel for that view.
Is there a way of automatically doing this?
Is there a way of doing it in reverse? (fields to matching viewmodel json object)
The only way I am aware of taking data return from an ajax call and putting it in a field is manually
$('#TextField1').val(result.TextField1);
etc..
to send it back to the controller you can do
data: $('form').serialize(),
this will take all of the fields in that form and send them back to the controller
Ok it looks like this will suit my needs.
I need to follow a convention of naming containers the same name as their respective properties as well as putting a class on them to indicate that they contain subfields.
function MapJsonObjectToForm(obj, $container) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var $field = $container.find('#' + key);
if ($field.is('div')) {
MapJsonObjectToForm(obj[key], $field);
} else {
if (obj[key] == null) {
if ($field.hasClass('select2-offscreen')) {
$field.select2('val', '');
$field.select2().trigger('change');
} else {
$field.val("");
}
} else {
if ($field.hasClass('select2-offscreen')) {
$field.select2('val', obj[key]);
$field.select2().trigger('change');
} else {
$field.val(obj[key]);
}
}
}
}
}
}
function MapFormToJsonObject(containerid) {
var obj = {};
$('.dataitem').each(function () {
var exclude = "s2id";
if ($(this).attr("ID").substring(0, exclude.length) !== exclude) {
var parents = $(this).parents(".has-sub-fields");
if (parents.length > 0) {
obj = FindParents(obj, parents.get(), $(this).attr("ID"), $(this).val());
} else {
obj[$(this).attr("ID")] = $(this).val();
}
}
});
return obj;
}
function FindParents(obj, arr, id, value) {
if (arr.length == 0) {
obj[id] = value;
return obj;
}
var parentID = $(arr[arr.length - 1]).attr("ID");
arr.pop();
if (obj[parentID] == null) {
obj[parentID] = {};
}
obj[parentID] = FindParents(obj[parentID], arr, id, value);
return obj;
}

Backbone.js Model validate method

How can I validate only certain attributes on a model? Currently I check if the attribute exists in the object passed into validate:
validate: function(attrs) {
// Number
if (attrs.minimum) {
if (isNaN(attrs.minimum)) {
return -1;
}
}
if (attrs.maximum) {
if (isNaN(attrs.maximum)) {
return -1;
}
}
}
but if I want to validate string value then:
if (attrs.mystring) {
// Do validation
}
would fail and the validation never takes place.
Backbone now supports the has property. So you can do something like that:
var Person = Backbone.Model.extend({
defaults: {
"name": "Kevin",
"age" : 26,
"job" : "web"
},
validate: function(attrs, options) {
for(k in attrs) {
if(!this.has(k)) {
return k + ' attribute is not exist';
}
}
}
});
var person = new Person;
person.on("invalid", function(model, error) {
console.log(error);
});
Im a little confused by your wording, but I think you want to check if its not an empty string first? and also work out the possibility that it is undefined..if so then this is what you'll want to do..
validate: function(attrs) {
// Number
if (attrs.minimum) {
if (isNaN(attrs.minimum)) {
return -1;
}
}
if (attrs.maximum) {
if (isNaN(attrs.maximum)) {
return -1;
}
}
if (typeof(attrs.mystring) != "undefined"){
if (!attrs.mystring){
return -1;
}
}
}
if you want to only validate one of your attributes, you should write your validate function to accommodate the options accordingly
validate: function(attrs, option) {
if (!option){
// Number
if (attrs.minimum) {
if (isNaN(attrs.minimum)) {
return -1;
}
}
if (attrs.maximum) {
if (isNaN(attrs.maximum)) {
return -1;
}
}
if (!attrs.mystring){
return -1;
}
}else{
switch(option){
case("string"):
if (!attrs.mystring){
return -1;
}
break;
case("number"):
// Number
if (attrs.minimum) {
if (isNaN(attrs.minimum)) {
return -1;
}
}
if (attrs.maximum) {
if (isNaN(attrs.maximum)) {
return -1;
}
}
break;
}
}
}
there are many ways to do this, this probably being the least efficient lol but using your example, it will do the job.
also, this isn't really a backbone.js problem per say...but general js

Resources