I have a rather complex relation with a polymorphic through-model. In the app, there is a tag system, where users and another model can be tagged. To avoid duplicate string entries, I used a through model (TagLink) which contains the tag ID, the tagged model name and the tagged instance ID.
So I have
User --+
|
+---> TagLink -----------> Tag
| - tagId - id
Item --+ - taggableId - text
- taggableName
(User or Item)
The linking relation is hasAndBelongsToMany. The problem is, when I post a new tag to /items/:id/tags or /users/:id/tags, it is saved, but I can create as many as I want with the same text without any error.
What I would like is, upon posting to /items/:id/tags or /users/:id/tags, it :
creates a new tag entry if it does not already exists, then adds a new TagLink entry (this is the current behaviour, but even when the same tag already exists)
solely creates a TagLink entry when a tag already exists
I thought about two solutions :
Somehow override the create method of Tag to check for existence, then manually create a TagLink entry if it exists. If not, proceed as by default.
Expose the tag list (/tags) with ~all CRUD URIs, then force to use the tag ID on /{items,users}/:id/tags.
Ideally, I would prefer the first one as it is more transparent and makes a smoother API.
So, any leads would be welcome!
I ended up doing the first solution, which was pretty easy. The idea is to replace the User.create method in a boot script which finds a tag with the same text, and returns it if one is found.
module.exports = function(app) {
var Tag = app.models.Tag;
// Override Tag.create to avoid duplicates
Tag.createRaw = Tag.create;
Tag.create = function(data, token, cb) {
// Find matching tag
Tag.findOne({where: {text: data.text}}, (err, tag) => {
if (err) cb(err);
// If the tag is found, return it
else if (tag) cb(null, tag);
// Else create it
else Tag.createRaw(data, token, cb);
});
}
}
Related
i have a webpage where user can select one of different packages to buy from a list. Package details are coming from a database.
HTML Code
<div data-package='2346343' class="retail-package">Cost : 10$</div>
<div data-package='5465654' class="retail-package">Cost : 20$</div>
<div data-package='3455675' class="retail-package">Cost : 30$</div>
Jquery Code
$('.retail-package').on('click', function() {
$(this).addClass("selected-package");
var selectedPackage = $(this).data("package");
});
Now above code shows how we(specially i) normally select a particular thing out of a list when clicked, In this procedure, as you can see in HTML Code, i am giving out or showing the pakcageId to users i.e. anyone can do a inspect element in a browser and view or even manipulate the data-package attribute, for safety i do a server side check of selected data.
My Question
Is there a way to hide this data exposure, or is there any other cleaner way to accomplish this, because i have seen people using Angular, Webpack etc able to implement a list selection without giving out or showing any data which can be seen by inspect element feature in a browser.
Note : i am sorry if my question is too basic, if this cannot done using jquery what are other technologies which i can use ?
You may create a Map where keys are arbitrary, auto-incremented identifiers, and values are package numbers:
const idPackageMap = new Map()
// id generator: whenever you call it, "i" is incremented and returned
const id = (() => {
let i = 0
return () => ++i
})()
const addPackage = package =>
idPackageMap.set(id(), package)
addPackage(2346343)
addPackage(5465654)
addPackage(3455675)
console.log('contents: ', [...idPackageMap.entries()])
console.log('package number for id 2: ', idPackageMap.get(2))
Now, when you insert those <div> elements you may set the arbitrary identifier, and when you need to locate the actual package number is just about using Map#get: idPackageMap.get(1) (change 1 with any arbitrary identifier).
I wrote a script that allows you to delete a property in the caches of the application, however I need to run this script only once when I install the application.
someone has an idea, thanks
var executed = 0;
if(executed === 0){
Ti.App.Properties.removeProperty("My_Property");
executed++;
}
The only ways you can hold some value across app sessions are Ti.App.Properties or sql database. So you can do it in two ways as below:
Solution 1: Use another property to know that you have deleted the desired property.
// for first time installation, the default value (or 2nd parameter) will be false as you have not deleted the property yet
var isDeleted = Ti.App.Properties.getBool('My_Property_Deleted', false);
if (isDeleted) {
Ti.App.Properties.removeProperty("My_Property");
// since you have deleted it, now set it to true so this 'if' block doesn't runs on any next app session
Ti.App.Properties.setBool('My_Property_Deleted', true);
} else {
// you have already deleted the property, 'if' block won't run now
}
Solution 2: Create a new database or pre-load a shipped db with your app.
// Titanium will create it if it doesn't exists, or return a reference to it if it exists (after first call & after app install)
var db = Ti.Database.open('your_db');
// create a new table to store properties states
db.execute('CREATE TABLE IF NOT EXISTS deletedProperties(id INTEGER PRIMARY KEY, property_name TEXT);');
// query the database to know if it contains any row with the desired property name
var result = db.execute('select * from deletedProperties where name=?', "My_Property");
if (result.rowCount == 0) { // means no property exists with such name
// first delete the desired property
Ti.App.Properties.removeProperty("My_Property");
// insert this property name in table so it can be available to let you know you have deleted the desired property
db.execute('insert into deletedProperties(name) values(?)', "My_Property");
} else {
// you have already deleted the property, 'if' block won't run now
}
// never forget to close the database after no use of it
db.close();
There can be other ways as well, but these 2 will work for what you want. Read more about Ti.Database here
I am working on a project that is being built around SignalR then JavaScript appending the data to elements.
$("#" + div).html(html);
The problem I am having is that I am calling the server every 2 seconds
$.connection.hub.start().done(function () {
chat.server.send("0", 1);
setInterval(function () {
chat.server.send("0", 1);
}, 2000);
});
This means it's then updating every div, every time the server returns the data, I have been looking for a way to get JavaScript to handle only updating elements if the data returned has changed, there were two ways I was going to go about doing this.
Get the HTML content from the div and do a comparison, however that was flawed because different browsers return different HTML, so that's a no
The second way was to store the content in a global variable and when it runs again, compare the global against the current, if it's different update
However I am looking at possibly something built into JavaScript which could handle this for me, currently I am using .html but when looking into this more, I found out that it first does .empty() and re-appended the data and I'm looking for something that'll only update if it's different.
-- EDIT --
How the project is currently setup is that I have a class containing different properties of what needs to be displayed, e.g.
public class MyClass {
public List<DivOneContent> Content1;
public List<DivTwoContent> Content2;
}
On the server side I have it fetch the content and populate the class
public void Send(string tab, string subTab = null)
{
MyClass data = PrepareModel(tab, subTab);
Clients.Client(Context.ConnectionId).broadcastObject(data);
}
When this is this is turned back to the client side, I have JavaScript functions which take one of the properties and appends that as needed
// First param is the content and the second one is the div ID
devious.htmlStringBuilder.buildContentOne(data.Content1, "Content1");
In the function I just loop and build a string and append such string to the div using .html
I have an <a> tag which I'm using to redirect the user to another xpage.
Its href property is:
<a target="_blank" href="http://serv/MyBase.nsf">
I use a simple view listing a doc. which contains the server and the name of the application.
So, I want to use some #DbLookup function in javascript to get into 2 var the above server and app name:
var server = #Unique(#DbColumn(#DbName(), "myVw", 1);
var name = #Unique(#DbColumn(#DbName(), "myVw", 2);
var concat = server+"/"+name;
return concat;
How can I compute the href property to return the concat variable?
Create a Link control xp:link and calculate the URL in attribute value:
<xp:this.value><![CDATA[#{javascript:var server .... }]]></xp:this.value>
Knut's approach is correct, but your code isn't :-). For every XPages load (or refresh) you do 4 #DbLookup. You can do a set of optimisations here:
Combine the result you want in the view itself, so you only need one lookup
Cache the value in the session (or application scope)
something like this (add nice error handling):
if (sessionScope.myHref) {
// Actually do nothing here
} else {
sessionScope.myHref = #Unique(#DbColumn(#DbName(), "myVw", 3);
}
return sessionScope.myHref;
The 3rd column would have the concatenation in the view already. That little snippet does a lookup only once per session. If it is the same for all users, use the applicationScope then it is even less.
I'm trying to figure out whether or not this is possible. First off, I should say that I'm cloning the FileReader object and maintaining that throughout my application (so I can allow the user to remove files before they officially upload them).
All that works fine, but I'm hung up trying to figure out the best way to allow a user to remove a member from this cloned object (members are the same as files [images in my case] which would be found within a normal FileReader object).
So basically I've cloned the object, add a pseudoHash to it which allows me to reference the cloned member that the user wants to delete, and upon clicking on a "Remove" icon, I search for the associated pseudoHash which I've tied to that icon, within the cloned object, and delete the member. The would work fine if the object were in its own function, but it's not. So I'm having trouble trying to delete the object's members. I made files global in this case, but it's still not working.
// Our FileList object
files = e.target.files;
// Create a clone since FileList is readonly
files = cloneObject(files);
// If files isn't empty, give the user the option to remove members
if (files.length) {
files = removeSelected(files);
}
Here is my function. Unfortunately when I click to remove an image from the "upload queue", it should go through this function, but it actually doesn't delete the object, as I know Javascript doesn't ever completely delete objects, so if it's the last member, I try setting it to an empty object, but that doesn't work. If it's not the last member, I just want to remove it within its place. Again, because this is in a separate function and not the parent, it's only deleting the local reference to the variable.
// Remove selected file from cloned fileList object
var removeSelected = function(files) {
var dataPseudoHash = $(this).attr('data-pseudo-hash');
$('#js-selected-files').delegate('.js-remove-selected', 'click', function() {
if (files.length == 1) {
$('.js-image-upload').attr('disabled', true).addClass('btn_disabled');
files = {};
delete files;
} else {
// Loop through our files object and if we find a member with the same
// pseudoHash as the attribute from whatever 'X' icon that was clicked, remove it
$.each(files, function(i, dataPseudoHash) {
if (files[i].pseudoHash == dataPseudoHash) {
delete files[i];
files.length -= 1;
}
});
}
// Remove hidden input that prevents duplicate files being uploaded of
$('.js-pseudo-hash[value="' + dataPseudoHash + '"]').remove();
$(this).parent().fadeOut('normal', function() {
$(this).remove();
});
return files;
};
What's the best way to handle this? Maybe setting a flag to true or false, and then delete the object or its members accordingly in the parent function depending on the case? Not quite sure. Other than that, I've got it uploading the files fine, but I need to find out how to remove properties of the object or the object altogether (if all "Remove" icons are clicked).
Firstly you should know that the delete operator deletes only an element oof an array or element of an object not the whole array. In your first if condition
if (files.length == 1) {
$('.js-image-upload').attr('disabled', true).addClass('btn_disabled');
files = {};
delete files;
}
You are deleting the whole object which returns false.
Now it will be better if you can manually check whether the second if condition is true or not in any case.