Angular input model conditionally reads from one property, writes to another - javascript

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'];
}

Related

What parameters does shopify's 'OptionSelectors' actually need?

I'm running circles here and I'm out of idea's/google searches. There are so many different examples but all seem to do something different or don't work. According to shopify, this is the only documentation I can find around using their API: https://shopify.dev/tutorials/customize-theme-use-products-with-multiple-options
A ghost object I see, no matter, more and more searches I still can't figure out what this parameter is supposed to be.
I've attempted passing a json object of products as I've seen it done in various other theme examples:
var product = document.querySelector("[data-product-json]").innerHTML,
product = JSON.parse(product || '{}');
console.log(product);
jQuery(function($) {
new Shopify.OptionSelectors('productSelect-' + product.id, {
product: product,
onVariantSelected: selectCallback
});
});
The console log gives the correct object and json, nice.
OptionSelectors errors out:
Uncaught TypeError: Cannot read property 'parentNode' of null
at Shopify.OptionSelectors.replaceSelector (option_selection-fe6b72c2bbdd3369ac0bfefe8648e3c889efca213baefd4cfb0dd9363563831f.js:1)
at new Shopify.OptionSelectors (option_selection-fe6b72c2bbdd3369ac0bfefe8648e3c889efca213baefd4cfb0dd9363563831f.js:1)
I've given it just the product.id and various other things.
I'm going to out on a whim here and say the Shopify documentation is detailed, yes, but it is not developer-friendly in my opinion. They give you so much information but never what you really need.
Shopify products have a pretty simple, but weird in a way organization. First off, a product has an array of up to three things known as options. Can be empty. But as soon as you assign an option to a variant, this gets filled in. So you have your three options. Eg: Name, Size, Color, Material and on and on.
A variant has the actual value of the options. So if you provided option 1 as Size, a variant would have option1 equal to a size value, like "large". Repeat and layer in the other options, till a variant perhaps has 3. Now, reverse that process to simply get an ID so you can update a price, or some other logic!
So in this way, up to 100 variants can have 3 distinguishing options, all different. Going way back to Shopify early days, they produced some code that ended up lasting about ten years, and your snippet of OptionSelectors is an offshoot of that mess.
The challenge is to do what that old code did, but for your theme purposes. Many libraries and themes have done just that. But be aware they also used code that is not exactly easy to fork and use for your own purposes either.
So if you find hacking this old Shopify code to be a mind-numbing experience, you might do better to just rebuild Humpty Dumpty yourself so you completely understand it. You do not need to use their code. It is also super confusing because when Theme authors spawn yet another version of this code, they often thought they'd be clever and rename a few things or target a few different things, and thus establish themselves as "unique, more skilled" players, but in fact, this just adds to your misery, as they did not accomplish much.
So yes, all the best to you in your endeavors. Taking apart that code and learning it is a rite of passage most theme authors undergo. Yes, they swear. Yes it reveals some WTF moments. But in the end, you'll control your variants, and achieve glory.
I strongly recommend David Lazar's answer and that you take some time to build your own function(s) that can do the job of breaking down a product's options and associated values into customer-friendly options and then translating those selections into a single valid variant ID. It's not too hard and sometimes kinda fun.
However, if you just want to get the OptionSelectors code working:
var product = document.querySelector("[data-product-json]").innerHTML,
product = JSON.parse(product || '{}');
console.log(product);
jQuery(function($) {
new Shopify.OptionSelectors('productSelect-' + product.id, {
product: product,
onVariantSelected: selectCallback
});
});
The first parameter that goes into the function is the ID of the (usually hidden) element inside your form that will store the value of the ID of the selected variant.
The error you are getting from the OptionSelectors code:
Uncaught TypeError: Cannot read property 'parentNode' of null
is most often thrown when the code doesn't find that element.
So to fix your problem, in your product form you should just need to find (or create) an input field with name="id" and make sure that element has an ID that matches what you're using.
For example:
<input type="hidden" id="productSelect-{{ product.id }}" name="id" value="{{ product.selected_or_first_available_variant.id }}" />
(Alternatively, find your input with name="id" and take the ID attribute from that field and use it in your call to OptionSelectors)
The problem you will encounter is that different themes change what they pass. You are better off creating your own event handler to get the variant id and lookup the variant details that your code requires.
See Shopify trigger function on variant selection but don't override existing functionality for some tips on how to set that up.

Checking for any dirty Backbone model data within collection

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.

Get Titanium.App.Properties in another win or make it global available

Please don’t answer this if you don’t take the time to understand my question or have a reasonable answer. I have got a few answers that is far on the side and I think I explain my problem very clear. Shall this problem drive me nuts or is there somebody out there with a straight and clear answer on Titanium.App.Properties?
I have a login form that stores the username in one:
Titanium.App.Properties.setString("userName",e.value);
and the password in another:
Titanium.App.Properties.setString("passWord",e.value);
The forms TextFields holds these values(after a store) even if I close the window, shut down and restarts the app. This because of the App.Properties.getString("value"); I suppose….?!
But when I copy the hole form with its App.Properties.getString("value"); to another window, the TextFields are empty and contains no value at all. I understand that the Properties string must be there some where in App.Properties, but how can grab it and use it another place in the app?
My question is: How to get the value from my
var userNameValue = Titanium.App.Properties.getString("userNameValue");
to be available in another window or for the hole app(global)? How can I grab it and make use of it a another place in the app? I don’t see a good reason to make these, only separate words, into objects(JSON) since the userName only contains a e-mail address and the passWord consist only of continuing letters or numbers. But if you mean I have too, -how do I set this from my TextField and get it in another TextField somewhere else in my app. I have not had any luck so far. I hope you can help me out and I can keep sane.
Titanium.App.Properties.getString("userNameValue");
This is globally Available, any Propertie of the Titanium Object is accessible in each file.
but if for some reason this doesnt work for you and you want to set a global variable,
you could do the following:
Create a file called myGlobals.js //or anything else,
//Put this in there e.g
var myProperties = {};
in any file you want to use it write in the first line
Ti.include('myGlobals.js');
Then you can make a propertie global available, for example write this in app.js somewhere where the app initializes
myProperties.Username = Titanium.App.Properties.getString("userName");
Then you can get the value in each file by accesing the propertie
myProperties.Username
//of course the Propertie has to be set before you can get them
( Titanium.App.Properties.setString("userName",e.value); ) //like you do it
But, Titanium.App.Properties.getString("userName");
should be avilable from any file anyway, (but you can give this a try although i dont think its nice to do it like this)
i had a similar problem where this didnt get any value from a propertie set in the ios settings as default value.
I had to go to the settings and manually change or edit the default value and then after a restart
Titanium.App.Properties.getString("userName");
returned the value as it should,
i hope this helps you =)
Answer to the Comment
I'm glad i could help you =)
Yes you can Use an focus EventHandler like this :
textfield.addEventListener("focus",function(){
textfield.value = "Test";
});
Beside that , are you using the identical Textfield for both windows ? like
var Textfield = Ti.Ui.createTextField({...});
and add it to 2 different windows ?
win1.add(Textfield);
win2.add(Textfield);
That led for me to Problems with labels in TableViewRows, using an identical Row 2 times in the TableView
The Text displayed only on 1 Label, sometimes it switched the Labels
I think you can't add one and the same titanium object to more then one other object
Maybe that could be the culprit,
dunno just an idea =)

Custom jQuery Request Object - should I move my implementation to my markup?

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.

Making JavaScript object member values more readable with a map

So I have this Omniture object. It's called s.
s
Inside s, we keep track of a bunch of information, inside "props" and "eVariables".
s.prop5 = 'foo'
s.prop22 = 'baz'
s.var6 = 'bar'
Which prop variables and which evars we choose to assign, depends on which page we're tracking.
For example, on the homepage, we may wish to track prop5, prop6, and evar2, but on the registration page, we may wish to track prop4, prop5, prop9, prop10, evar4, evar5. It varies.
Each variable and each prop represents some kind of key analytics information.
Now, even though this solution is not ideal, because the prop#s can all blend together, we do have a master list that we keep internally, explaining which variable represents what.
prop5 means "page name"
prop6 means "page category"
(et cetera)
Now, this is fine, and it works well enough, but we often have to pass the code off to third parties so they can assign values themselves. We might have a 3rd party create a page, and we want to do analytics on it, but we need them to be able to get the appropriate information to track. To make it more readable, we were considering of implementing some mapping code.
companyName.pageName = 'This is the page name'
companyName.contentType = 'This is the content type'
companyName.campaignId = 'This is the campaign ID'
This is more readable. We would then loop through the "companyName" object, and assign every value back to 's' where appropriate.
What do you guys think? Would this be a good practice?
Honestly I can't see why you would use the cryptic property names in the first place. Why not use the names you would give to 3rd parties internally as well. Wouldn't it just make your life easier?

Categories