After generating Angular code for an entity the entity.component.ts file contains a method named clear().
It is quite similar to transient() but not eactly the same. It seems it can be delete without breaking anything.
What is it for? When should I use this method?
Why is router.navigate using a different notation?
Why is the query param size: this.itemsPerPage not included?
When generating Angular-Code for an Entity with the entity.component.ts file
This code gets generated when the entity has "pagination": "pagination" (and "jpaMetamodelFiltering": true) specified.
transition() {
this.router.navigate(['/entity'], {
queryParams: {
page: this.page,
size: this.itemsPerPage,
sort: this.predicate + ',' + (this.reverse ? 'asc' : 'desc')
}
});
this.loadAll();
}
clear() {
this.page = 0;
this.router.navigate([
'/entity',
{
page: this.page,
sort: this.predicate + ',' + (this.reverse ? 'asc' : 'desc')
}
]);
this.loadAll();
}
Related
I'm changing an attribute of a Lit web component, but the changed value won't render.
I have an observed array: reports[] that will be populated in firstUpdated() with reports urls fetched from rest apis. The loading of the array is done by:
this.reports.push({ "name" : report.Name, "url" : this.apiUrl + "/" + report.Name + "?rs:embed=true" });
see below:
import { LitElement, html, css } from 'lit';
import {apiUrl, restApiUrl} from '../../config';
export default class Homepage extends LitElement {
static properties = {
apiUrl: '',
restApiUrl: '',
reports: []
}
...
constructor() {
super();
this.apiUrl = apiUrl;
this.restApiUrl= restApiUrl;
this.reports = [];
}
firstUpdated() {
...
// Fetch all reports from restApiUrl:
rsAPIDetails(restApiUrl).then(reports =>{
for(const report of reports.value)
{
rsAPIDetails(restApiUrl + "(" + report.Id + ")/Policies").then(policies => {
for(const policy of policies.Policies)
{
if(policy.GroupUserName.endsWith(usernamePBI))
{
for(const role of policy.Roles)
{
if(role != null && (role.Name== "Browser" || role.Name== "Content Manager"))
{
// User has access to this report so i'll push it to the list of reports that will show in the navbar:
this.reports.push({ "name" : report.Name, "url" : this.apiUrl + "/" + report.Name + "?rs:embed=true" });
}
}
}
}
});
}
}).then(q => {
console.log(this.reports);
});
}
render() {
return html`
<div id="sidenav" class="sidenav">
...
<div class="menucateg">Dashboards</div>
${this.reports.map((report) =>
html`<a #click=${() => this.handleMenuItemClick(report.url)}>${report.name}</a>`
)}
<div class="menucateg">Options</div>
</div>
`;
}
At console I can clearly see that the array is loaded with the correct values.
But the render() function won't update the web component with the new values of reports[]:
The links should be added inside 'Dashboards' div
If instead I statically populate reports[] with values (in the ctor), it renders the links just fine.
So why isn't the component updated when the observed array is changed ?
Thank you!
Array.push mutates the array, but doesn't change the actual value in memory.
To have LitElement track updates to arrays and objects, the update to the value needs to be immutable.
For example, we can make your example work by doing it this way:
const newReports = this.reports.slice();
newReports.push({ "name" : report.Name, "url" : this.apiUrl + "/" + report.Name + "?rs:embed=true" });
this.reports = newReports;
Or by using array spread
this.reports = [...this.reports, { "name" : report.Name, "url" : this.apiUrl + "/" + report.Name + "?rs:embed=true" }]
The reason why this works is that when you do this.reports.push(), you're not actually changing the "reference" of this.reports, you're just adding an object to it. On the other hand, when you re-define the property with this.reports = ..., you are changing the "reference", so LitElement knows the value has changed, and it will trigger a re-render.
This is also true for objects. Let's say you have a property obj. If you updated the object by just adding a property to, the element wouldn't re-render.
this.obj.newProp = 'value';
But if you re-define the object property as a whole by copying the object and adding a property, it will cause the element to update properly.
this.obj = {...this.obj, newProp: 'value'}
You can see the values that are getting tracked and updated by using the updated method.
I'm trying to put a JSON object "Synced" (Which you will see in the code)
This is the code for a function "addServer(userid, serverid)"
The function is being required from another javascript file
db.all(`SELECT * FROM Users WHERE Tag = ? LIMIT 1`, userid, async(error,element) => {
if(element[0].Synced === '') {
var sJSON = {
users:{
[userid]:4,
},
servers:[`${serverid}`]
}
var serverJSON = JSON.stringify(sJSON)
console.log(serverJSON)
} else {
//Else statement not done yet
}
db.run(`UPDATE Users SET Synced = "${serverJSON}" WHERE Tag = "${userid}"`)
})
Solved. Needed to change quoting.
As Dave Newton said, I had to check my quoting. What I did was change my double quotes to single quotes which solved the problem.
I'm trying to get some info out of a API call in Nodejs, structured something like a JSON:
{
"generated":"2019-11-04T09:34:11+00:00",
"event":{
"id":"19040956",
"start_":"2019-11-16T11:30:00+00:00",
"event_context":{
"sport":{
"id":"1",
"name":"Soccer"
}
}
}
}
I'm not sure about the presence of none of these fields(Json could be incomplete).
Is there a better way to get the value of "name" in JSON.event.event_context.sport.name without an ugly if to not get errors like "cannot get field 'sport' of undefined"?
Currently, I'm doing
if(json.event && json.event.event_context && json.event.event_context.sport) {
return json.event.event_context.sport.name;
}
Is there a better way?
Thank you!
what do you mean by saying "I'm not sure about the presence of none of these fields"?
i don't understand what your'e trying to achieve.
Looks like there is also an interesting package that will allow more conditions on searching json :
https://www.npmjs.com/package/jspath
let getNested = (path, obj) => {
return path.split(".").reduce( getPath, obj);
}
let getPath = (path, key) => {
return (path && path[key]) ? path[key] : null
}
let test = {
"foo": "bar",
"baz": { "one": 1, "two": ["to", "too", "two"] },
"event": { "event_context": { "sport": { "name": "soccer" } } }
}
console.log(getNested("none", test))
console.log(getNested("baz.one", test))
console.log(getNested("baz.two", test))
console.log(getNested("event.event_context.sport.name", test))
You can use lodash get to get a potentially deeply-nested value, and also specify a default in case it doesnt exist.
Example
const _ = require('lodash');
const my_object = {
"generated":"2019-11-04T09:34:11+00:00",
"event":{
"id":"19040956",
"start_":"2019-11-16T11:30:00+00:00",
"event_context":{
"sport":{
"id":"1",
"name":"Soccer"
}
}
};
_.get(my_object, 'event.event_context.sport.name'); // "Soccer"
_.get(my_object, 'event.event_context.sport.nonExistentField', 'default val'); // "default val"
Article: https://medium.com/#appi2393/lodash-get-or-result-f409e73e018b
You can check by using a function to check object keys like :
function checkProperty(checkObject, checkstring){
if(!checkstring)
return false;
var propertiesKeys = checkstring.split('.');
propertiesKeys.forEach(element => {
if(!checkObject|| !checkObject.hasOwnProperty(element)){
return false;
} else {
checkObject= checkObject[element];
}
})
return true;
};
var objectToCheck = {
"generated":"2019-11-04T09:34:11+00:00",
"event":{
"id":"19040956",
"start_":"2019-11-16T11:30:00+00:00",
"event_context":{
"sport":{
"id":"1",
"name":"Soccer"
}
}
}
}
if (checkProperty(objectToCheck ,'event.event_context.sport.name'))
console.log('object to find is : ', objectToCheck .event.event_context.sport.name;)
Yeah there are better ways!
For example, you could use lodash's get() method to reach a nested value.
var object = { 'a': [{ 'b': { 'c': 3 } }] };
_.get(object, 'a[0].b.c');
// => 3
But there is also a native solution.
Currently (11.2019) only Babel can handle this.
I am speaking of Optional chaining. It's new in the Ecmascript world.
Why I like it? Look here!
// Still checks for errors and is much more readable.
const nameLength = db?.user?.name?.length;
What happens when db, user, or name is undefined or null? With the optional chaining operator, JavaScript initializes nameLength to undefined instead of throwing an error.
If you are using Babel as a compiler then you could use it now.
Related link: https://v8.dev/features/optional-chaining
I found an important security fault in my meteor app regarding subscriptions (maybe methods are also affected by this).
Even though I use the check package and check() assuring that the correct parameters data types are received inside the publication, I have realised that if a user maliciously subscribes to that subscription with wrong parameter data types it is affecting all other users that are using the same subscription because the meteor server is not running the publication while the malicious user is using incorrect parameters.
How can I prevent this?
Packages used:
aldeed:collection2-core#2.0.1
audit-argument-checks#1.0.7
mdg:validated-method
and npm
import { check, Match } from 'meteor/check';
Server side:
Meteor.publish('postersPub', function postersPub(params) {
check(params, {
size: String,
section: String,
});
return Posters.find({
section: params.section,
size: params.size === 'large' ? 'large' : 'small',
}, {
// fields: { ... }
// sort: { ... }
});
});
Client side:
// in the template:
Meteor.subscribe('postersPub', { size: 'large', section: 'movies' });
// Malicious user in the browser console:
Meteor.subscribe('postersPub', { size: undefined, section: '' });
Problem: The malicious user subscription is preventing all other users of getting answer from their postersPub subscriptions.
Extra note: I've also tried wrapping the check block AND the whole publication with a try catch and it doesn't change the effect. The error disappears from the server console, but the other users keep being affected and not getting data from the subscription that the malicious user is affecting.
Check method and empty strings
There is one thing to know about check and strings which is, that it accepts empty strings like '' which you basically showed in your malicious example.
Without guarantee to solve your publication issue I can at least suggest you to modify your check code and include a check for non-empty Strings.
A possible approach could be:
import { check, Match } from 'meteor/check';
const nonEmptyString = Match.Where(str => typeof str === 'string' && str.length > 0);
which then can be used in check like so:
check(params, {
size: nonEmptyString,
section: nonEmptyString,
});
Even more strict checks
You may be even stricter with accepted parameters and reduce them to a subset of valid entries. For example:
const sizes = ['large', 'small'];
const nonEmptyString = str => typeof str === 'string' && str.length > 0;
const validSize = str => nonEmptyString(str) && sizes.indexOf( str) > -1;
check(params, {
size: Match.Where(validSize),
section: Match.Where(nonEmptyString),
});
Note, that this also helps you to avoid query logic based on the parameter. You can change the following code
const posters = Posters.find({
section: params.section,
size: params.size === 'large' ? 'large' : 'small',
}, {
// fields: { ... }
// sort: { ... }
});
to
const posters = Posters.find({
section: params.section,
size: params.size,
}, {
// fields: { ... }
// sort: { ... }
});
because the method does anyway accept only one of large or small as parameters.
Fallback on undefined cursors in publications
Another pattern that can support you preventing publication errors is to call this.ready() if the collection returned no cursor (for whatever reason, better is to write good tests to prevent you from these cases).
const posters = Posters.find({
section: params.section,
size: params.size === 'large' ? 'large' : 'small',
}, {
// fields: { ... }
// sort: { ... }
});
// if we have a cursor with count
if (posters && posters.count && posters.count() >= 0)
return posters;
// else signal the subscription
// that we are ready
this.ready();
Combined code example
Applying all of the above mentioned pattern would make your function look like this:
import { check, Match } from 'meteor/check';
const sizes = ['large', 'small'];
const nonEmptyString = str => typeof str === 'string' && str.length > 0;
const validSize = str => nonEmptyString(str) && sizes.indexOf( str) > -1;
Meteor.publish('postersPub', function postersPub(params) {
check(params, {
size: Match.Where(validSize),
section: Match.Where(nonEmptyString),
});
const posters = Posters.find({
section: params.section,
size: params.size,
}, {
// fields: { ... }
// sort: { ... }
});
// if we have a cursor with count
if (posters && posters.count && posters.count() >= 0)
return posters;
// else signal the subscription
// that we are ready
this.ready();
});
Summary
I for myself found that with good check matches and this.ready() the problems with publications have been reduced to a minimum in my applications.
I have one form and i want to search in my database. I create the object with all parameters but i have one problem. When write in one textfield search works fine and the query run correctly . When write two or more textfields params doesn't work and i have a fail execution of query :
WHERE
((((id_reservation=:id_reservation) AND (start=:start)) AND (end=:end)) AND
(fkCustomer.first_name=:first_name))
params doesn't replace.
$criteria=new CDbCriteria;
$criteria->with =array('fkCustomer');
if(!empty($start))
{
$criteria->addCondition('start=:start');
$criteria->params=array(':start'=>$start);
}
if(!empty($end))
{
$criteria->addCondition('end=:end');
$criteria->params=array(':end'=>$end);
}
if(!empty($merge->customer_name))
{
$criteria->addCondition('fkCustomer.first_name=:first_name');
$criteria->params=array(':first_name'=>$merge->customer_name);
}
if(!empty($merge->customer_surname))
{
$criteria->addCondition('fkCustomer.last_name=:last_name');
$criteria->params=array(':last_name'=>$merge->customer_surname);
}
if(!empty($merge->customer_email))
{
$criteria->addCondition('fkCustomer.email=:email');
$criteria->params=array(':email'=>$merge->customer_email);
}
$criteria->limit = 100;
It's because in every if block you replace the params array. Build an array in the if blocks then add it to $criteria->params on the last line, outside the blocks.
For instance:
$criteria=new CDbCriteria;
$criteria->with =array('fkCustomer');
$my_params = array();
if(!empty($end))
{
$criteria->addCondition('end=:end');
$my_params['end'] = $end;
}
if(!empty($merge->customer_name))
{
$criteria->addCondition('fkCustomer.first_name=:first_name');
$my_params['first_name'] = $merge->customer_name;
}
// other ifs ..
//then
$criteria->limit = 100;
$criteria->params = $my_params;
Also, if I remember correctly, you don't need to write ':end' and ':first_name' in the params array, it will work without the colon.
You also have the following alternative
$criteria=new CDbCriteria;
$criteria->with = 'fkCustomer';
if(!empty($end))
{
$criteria->compare('end', $end);
}
if(!empty($merge->customer_name))
{
$criteria->compare('fkCustomer.first_name', $merge->customer_name);
}
// The following conditions ..
// Limit:
$criteria->limit = 100;
Hi i think this is the same problem i have before with the params variable, for avoid this problem i use the CMap::mergeArray function
this happens because you overwrite the variable each time that the condition passed over it.
This is the syntax for avoid it, its an example
$criteria=new CDBCriteria;
$criteria->addBetweenCondition("Date",$datestart,$dateend);
$criteria->addCondition("Type=:type");
//$criteria->params=array(":type"=>"1"); //This is wrong, overwrites addBetweenCondition params
$criteria->params=CMap::mergeArray($criteria->params,array(
":type"=>"1",
)); //This is ok, mantain all parameters in the params var
$query=Model::findAll($criteria);
можно обратиться напрямую
$criteria=new CDbCriteria;
$criteria->with =array('fkCustomer');
if(!empty($start))
{
$criteria->addCondition('start=:start');
$criteria->params['start']=$start;
}
if(!empty($end))
{
$criteria->addCondition('end=:end');
$criteria->params['end']=$end;
}
if(!empty($merge->customer_name))
{
$criteria->addCondition('fkCustomer.first_name=:first_name');
$criteria->params['first_name']=$merge->customer_name;
}
if(!empty($merge->customer_surname))
{
$criteria->addCondition('fkCustomer.last_name=:last_name');
$criteria->params['last_name']=$merge->customer_surname;
}
if(!empty($merge->customer_email))
{
$criteria->addCondition('fkCustomer.email=:email');
$criteria->params['email']=$merge->customer_email;
}
$criteria->limit = 100;