I am developing a front end request/data management system in order to clean up/organize my API calls/refactor how I interface with my backend platform. I am extending the jquery ajax api call to interface with it and I am looking for some advice on where to stick api-specific implementation.
please keep in mind this is a web-application platform and I am trying to make it easier to manage front-end components
The goal is to take defining a request from something like...
var requestObj = new Object();
requestObj.callback = function(responseObj){deleteUserComplete(responseObj); };
requestObj[0] = new Object();
requestObj[0].module = "usermanager";
requestObj[0].context = "#someTable";
requestObj[0].action = "DELETE_USER";
requestObj[0].dataObj = new Object();
requestObj[0].dataObj.userId = $("#ui-tabs-4 .az-switch-panel-off input[name$=userId]").val();
To...
$("button.apiSubmit").apiManager('submitApi');
or
var options = {};
options.callback = someFunction;
options.context = "#someTable";
//etc...
$("button.apiSubmit").apiManager('submitApi', options);
I'm sure you get the idea... but i want to move the ugly request object creation to a factory-type object (mainly just processing forms into objects my backend understands) and moving the api-specific implementation (module, action, context etc) to the markup.
Now for the question(s)...
1) What are the benefits/pitfalls of moving my api-specific request information to the markup?
2) (again, pretty much convinced moving request info to the markup is the right move) class tags or html5 data attributes (x-browser isn't an issue... they are internal apps)?
EX: of class attributes would be... class="apiButton apiButton-module-MODULE_NAME apiButton-action-ACTION_NAME" - obviously a bit ugly... but manageable straightforward way to go about htis.
3) Are there any alternatives to making my api requests more reusable/easier to read? It's the only way I communicate with php so it's very... very important this system is solid.
1) Whereas I somewhat agree with Marcel Korpel on using HTML5 data attributes, I think that using the markup explicitly presents a couple potential problems: first off you are exposing your API/backend internals to the end-user, which is never ideal and secondly its kind of volatile because it could be easily changed (firebug, js) and mess up the behaviour associated with that element.
2) The more elegant (but slightly harder to implement method) would be to use jQuery's .data() method to store related information - this way you keep your markup clean and still have the flexibility of storing as much information as you want related to the element. It is also "hidden" from the end-user (sure firebug/js can access it but it's slightly harder to come by than right in the markup). There are basically 2 ways I can think of how you could implement this: -1 - if you are creating the markup dynamically then wrap the element in a jQuery object and apply the metadata before inserting it into the DOM or -2- if it is being created with PHP you could store it as a serialized string in "rel" or "rev" or someother little-used attribute and then use jQuery to grab it, store in metadata and clear the attribute.
3) However, now that I think about it, whereas using .data() is more elegant, I guess it doesn't make it all the more easier to understand because you are effectively hiding away applications internals. Perhaps you could implement getter/setters to retrieve the metadata or something along those lines.
Related
I have a solution (see below), but it's awkward and klunky & I'd like to hear how others have solved this problem.
Here's a simple example to illustrate the problem:
Imagine I have an Eloquent User model and I'm putting together an "edit user" page that uses Knockout to handle value-to-DOM-element bindings.
In my controller method for this page, I'm doing the following:
public function getEditUser($id = null)
{
return View::make("account.edituser")
->with("user", User::find($id))
->with("groups", Group::all());
}
where the Group model is for my access control groups. The user can be a member of zero or more groups and the relationship is set up in the normal way for Eloquent models.
In the template, I have a bunch of elements (text, select, radio buttons, etc.) set up with data-bind attributes to bind my Knockout view model to the elements.
In the <script> portion of the page, I have the following:
var createUserViewModel = function (user, groups) {
return {
id: ko.observable(user.id),
name: ko.observable(user.name),
groups: ko.observableArray(user.groups),
// ...other attributes go here
// the list of available groups
availableGroups: ko.observableArray(groups)
};
};
// instantiate the view model
var viewModel = createUserViewModel(
// note that I'm using Smarty for my templating system. The
// lines below apply the json_encode method and disable the
// htmlspecialchars function which I have set up to encode
// all output by default
{$user|json_encode nofilter},
{$groups|json_encode nofilter}
);
ko.applyBindings(viewModel);
The user argument to the createUserViewModel function will be JSON-encoded, so it will look like this: { id: 1234, name: "Joe Smith", groups: ['group1', 'group2'], ...}
This is a simple example: I have several forms for entering other information which are significantly more complicated than this.
There are a couple of problems with this:
As I said, it's awkward. The source generated by this is difficult to read for larger models.
I suspect there's an XSS vulnerability here, since I'm echoing data via json_encode directly into the body of the script element. I haven't been able to exploit this, but I think someone else could find a way.
But I can't think of another way to do it that doesn't radically change the way the application works (ie. use AJAX calls to retrieve the data).
Can anyone share a better way to do this?
Re (1) you need to look at the very useful mapping which is designed for exactly this - http://knockoutjs.com/documentation/plugins-mapping.html ... this will replace the createUserViewModel function for you, and save you having to manually turn a JSON object into something observable.
Re (2) I'm not expert enough with security and XSS to give an authoritative answer, but it looks OK to me. So long as the user and group data was sanitised before you stored it, then it should be OK to echo it back as JSON. If you're not providing a textbox or other input for a user to type into that then affects this data then there's no angle for an attacker to inject a malicious script. Plus JSON-encoding itself is quite a good sanitisation, in that any JavaScript will just end up as a string. So to be sure, given:
{$user|json_encode nofilter}
will print out some JSON, you could immediately try JSON.parse on it:
var user = JSON.parse({$user|json_encode nofilter})
before you then do anything with it in your script.
I want to query object from Parse DB through javascript, that has only 1 of some specific relation object. How can this criteria be achieved?
So I tried something like this, the equalTo() acts as a "contains" and it's not what I'm looking for, my code so far, which doesn't work:
var query = new Parse.Query("Item");
query.equalTo("relatedItems", someItem);
query.lessThan("relatedItems", 2);
It seems Parse do not provide a easy way to do this.
Without any other fields, if you know all the items then you could do the following:
var innerQuery = new Parse.Query('Item');
innerQuery.containedIn('relatedItems', [all items except someItem]);
var query = new Parse.Query('Item');
query.equalTo('relatedItems', someItem);
query.doesNotMatchKeyInQuery('objectId', 'objectId', innerQuery);
...
Otherwise, you might need to get all records and do filtering.
Update
Because of the data type relation, there are no ways to include the relation content into the results, you need to do another query to get the relation content.
The workaround might add a itemCount column and keep it updated whenever the item relation is modified and do:
query.equalTo('relatedItems', someItem);
query.equalTo('itemCount', 1);
There are a couple of ways you could do this.
I'm working on a project now where I have cells composed of users.
I currently have an afterSave trigger that does this:
const count = await cell.relation("members").query().count();
cell.put("memberCount",count);
This works pretty well.
There are other ways that I've considered in theory, but I've not used
them yet.
The right way would be to hack the ability to use select with dot
notation to grab a virtual field called relatedItems.length in the
query, but that would probably only work for me because I use PostGres
... mongo seems to be extremely limited in its ability to do this sort
of thing, which is why I would never make a database out of blobs of
json in the first place.
You could do a similar thing with an afterFind trigger. I'm experimenting with that now. I'm not sure if it will confuse
parse to get an attribute back which does not exist in its schema, but
I'll find out, by the end of today. I have found that if I jam an artificial attribute into the objects in the trigger, they are returned
along with the other data. What I'm not sure about is whether Parse will decide that the object is dirty, or, worse, decide that I'm creating a new attribute and store it to the database ... which could be filtered out with a beforeSave trigger, but not until after the data had all been sent to the cloud.
There is also a place where i had to do several queries from several
tables, and would have ended up with a lot of redundant data. So I wrote a cloud function which did the queries, and then returned a couple of lists of objects, and a few lists of objectId strings which
served as indexes. This worked pretty well for me. And tracking the
last load time and sending it back when I needed up update my data allowed me to limit myself to objects which had changed since my last query.
I have a Person class where edits made to the person must be verified by an admin user.
Each attribute has an "approved" and "tmp" version. Sometimes the "tmp" version is not set:
person = {first:'Bob', firstTmp:'Robert', last:'Dobbs', lastTmp:undefined}
When displaying the person, I want to display the "tmp" value if it is set, otherwise display the "approved" value. When writing, I want to write to the "tmp" value (unless logged in as an admin).
Ideally, this would not require a lot of custom markup, nor writing cover methods for each property (there are around 100 of them). Something like this would be nice:
<input ng-model="person.first"
tmp-model="person.firstTmp"
bypass-tmp="session.user.isAdmin" />
When displaying the value, display the tmp value if it is defined. Otherwise display the approved value.
When writing the value, write to the tmp value, unless logged in as an admin. Admins write directly to the approved value.
What's a good clean way to implement this in Angular?
Extend NgModelController somehow?
Use a filter/directive on the input?
Cover methods?
Just do the writing server-side?
I will try to go through your options one by one:
Extend NgModelController somehow?
I don't think this is a good idea. It won't be nice if something goes wrong and you don't know if you can even rely on something as basic as ng-model
Just do the writing server-side?
This would seem like the easier way (if you already know or find it easy to manage it in the back end), although the interaction would need a new request to the server.
Use a filter/directive on the input?
I believe this would be the best way to do it, as it is easy to understand what is going on by just taking a look at the markup. It's angular, you already know that some property like tmp-model is extending the markup.
Cover methods?
This would also be easy to implement, and you would be implementing some sort of "business logic" as a validator in your cover method.
Given that I've extended a bit in my answer, I can give you an inline example of the last one.
<input ng-model="person.firstTmp"
ng-init="person.firstTmp = person.firstTmp || person.first"
ng-change="updateProperty(person, 'first')" />
And on the controller, you could do something like:
$scope.updateProperty = function(person, propertyName) {
// The temporary property has already been changed, update the original one.
if($scope.session.user.isAdmin)
person[propertyName] = person[propertyName + 'Tmp'];
}
I have a requirement to "nag" a user about unsaved changes when they switch between different Backbone collection models (by clicking on a table row). I've googled for "check backbone model dirty data" (for instance) and not found anything definitive.
I accomplished this using underscore's "some" and isEqual functionality, in a manner such as the following, "some()" being sufficient to determine if there are any un-saved changes (as opposed to what those precise changes might be), in particular because the model attribute is actually an array of objects.
var anyDirty = _.some(myCollection.models, function(model) {
return !_.isEqual(model.get('nodes'), model.previousAttributes()['nodes]);
});
I am new to Backbone and am wondering if this is an accepted sort of approach for adhoc checking for dirty model data. Or, does Backbone provide some sort of built in functionality for this purpose, that my initial attempts at googling did not reveal?
I have another attribute I need to monitor in addition to 'nodes', so I'm switching to using changedAttributes(): http://backbonejs.org/#Model-changedAttributes:
var anyDirty = _.some(myCollection.models, function(model) {
return model.changedAttributes();
});
What may make this an imperfect solution is that it seems like it will return an object of changedAttributes even if the attribute got changed back to it's original value. So it almost seems that what I need in the long run is to take a snapshot of the original data and compare against that. Still though, using model.changedAttributes() is a more concise alternative to what I first posted.
I will first explain what I'm trying to do then I will explain why just in case you get bored of reading the whole scenario.
Basically I have some HTML markup stored in a variable I now need to a wait to access the different elements within the variable. For example:
var markUp = "<h3>h3 tag</h3><p>paragraph tag</p>";
What I need to know is if there is a way for me to query the variable to retrieve say the h3 tag, in a similar way you would use the query function ? I have seen some other practices where people append the var to a hidden div then query the div. I would prefer to avoid this but if that is the only way I will proceed.
I have come across this problem whilst developing a drag and drop application, on drop i use a custom creator function to change the items structure once it is dropped.
If further explanation is needed please say, thanks advance Jonathan
You can use dojo._toDom to create a DOM fragment from your string.
var markup = "<h3>h3 tag</h3><p>paragraph tag</p><p>another paragraph</p>";
var domFragment = dojo._toDom(markup);
dojo.query("p", domFragment).forEach(function(element,i) {
console.debug(element.innerHTML);
});
The underscore prefix in _toDom means that it's a "private" member method of dojo. Normally, it's bad practice to use these as if they were public (like I do here). However, in the case of _toDom I believe it's generally considered acceptable, and according to this trac entry, it sounds like it'll be made public in the next version.