Temporary form data to table as object in Angular - javascript

What I am trying to do is take 4-5 inputs in a form, store to an object and when user selects 'save', temporarily store in session and be able to access to show that object in a row in a table on same page and then allow user to enter data form input again to add to next object.
So in the end, the user may enter 1 object or up to 6 and when done, allow user to submit all objects as an array of objects or something to that effect.
So I am not an angular power user, but figure it has this power somewhere. So my questions evolve around a good approach to this and maybe trying to find a good example or so. Any of you able to help on this?
Thanks much.

Well, this is actually quite easy if you have the table and the form on the same page. You can simply use one scope variable for the array of entered objects and one scope variable for the current object itself.
I'll now do an example without all of your requirements (e.g. that the user can enter up to six objects the most), but this should be quite straight forward to implement.
First take the HTML:
<form ng-controller="RolesController">
<label for="name">Name</label>
<input type="text" id="name" ng-model="current.name">
<label for="slug">Slug</label>
<input type="text" id="slug" ng-model="current.slug">
<label for="level">Level</label>
<input type="number" id="level" ng-model="current.level">
<!-- buttons -->
<button type="button" ng-click="storeTemp()">Store temporarily</button>
<button type="button" ng-click="storeAll()">Store all</button>
</form>
And the Controller:
angular.module('administration').controller('RolesController',
['$scope', '$http', function($scope, $http) {
$scope.current = {};
$scope.all = [];
$scope.storeTemp = function() {
/* do validation here or in html before ... */
$scope.all.push($scope.current);
$scope.current = {};
};
$scope.storeAll = function() {
/* maybe another validation ... */
$http.post('admin/roles', { roles: $scope.all });
};
});
Obviously you could also use the same button for both actions if you require your user to always input the same amount of new objects.

Ah, master-detail form!
You do not need to worry about the multiple inputs the user are savings. Just declare an array and push your objects into it every time the user click 'save'. As long as you do not destroy your controller (i.e no page reload, no switch of controllers, etc) your data are always there.
//master
$scope.toBeSentToServer = [];
//details, declare your object wherever according to your needs
$scope.formVariable = {
var1:"",
var2:"",
var3:""
};
$scope.clickSave = function(){
$scope.toBeSentToServer.push($scope.formVariable);
//reinstantiate your $scope.formVariable so that your form is empty
$scope.formVariable = {};
}
And now you can use our beloved ng-repeat to iterate through scope.toBeSentToServer inside the table.
<table>
<tr ng-repeat= "item in toBeSentToServer">
<td>{{item.var1}}</td>
<td>{{item.var2}}</td>
<td>{{item.var3}}</td>
</tr>
</table>
Of course, there will be times you would want to use the cookies / local-data-storage, there is a plethora of libraries available to do it. Angular has its own $cookies service but I would highly recommend angular-local-storage from Gregory Pike. (Sorry maybe bias)
But before all these, if you are looking for data persistent mechanism, maybe try using a factory or sevice in angularjs?

Related

Overriding difficult view model

I am trying to replace some text in an input field using JS but the view model overrides my commands each time. This is the HTML I start with:
<td class="new-variants-table__cell" define="{ editVariantPrice: new Shopify.EditVariantPrice(this) }" context="editVariantPrice" style="height: auto;">
<input type="hidden" name="product[variants][][price]" id="product_variants__price" value="25.00" bind="price" data-dirty-trigger="true">
<input class="mock-edit-on-hover tr js-no-dirty js-variant-price variant-table-input--numeric" bind-event-focus="onFocus(this)" bind-event-blur="onBlur(this)" bind-event-input="onInput(this)">
</td>
I run this JS:
jQuery('#product_variants__price').siblings().removeAttr('bind-event-focus');
jQuery('#product_variants__price').siblings().removeAttr('bind-event-input');
jQuery('#product_variants__price').siblings().removeAttr('bind-event-blur');
jQuery('#product_variants__price').siblings().focus()
jQuery('#product_variants__price').siblings().val("34.00");
jQuery('#product_variants__price').val("34.00");
And I'm left with the following HTML:
<td class="new-variants-table__cell" define="{ editVariantPrice: new Shopify.EditVariantPrice(this) }" context="editVariantPrice" style="height: auto;">
<input type="hidden" name="product[variants][][price]" id="product_variants__price" value="34.00" bind="price" data-dirty-trigger="true">
<input class="mock-edit-on-hover tr js-no-dirty js-variant-price variant-table-input--numeric">
</td>
The problem is that each time I click the input field the value is reverted to what it was when the page loaded.
I've also tried running the command in the parent td along with my value change, to simulate the editing of a variant and preventing default with no success:
jQuery('#product_variants__price').siblings().bind('input', function (e) {
e.preventDefault();
return false;
});
jQuery('#product_variants__price').siblings().bind('focus', function (e) {
e.preventDefault();
return false;
});
jQuery('#product_variants__price').siblings().focus()
jQuery('#product_variants__price').siblings().val("£34.00");
jQuery('#product_variants__price').val("£34.00");
jQuery('#product_variants__price').siblings().keydown()
Parent td function:
new Shopify.EditVariantPrice(jQuery('#product_variants__price').parent())
So how can I successfully edit this value in the inputs and also update the Shopify view model?
You can try this for yourself by going here:
https://jebus333.myshopify.com/admin/products/2521183043
login jebus333#mailinator.com
password shop1
EDIT: I've tried to find the view model on the page but with no success. Plus, there are no network calls when editing the values in the input fields, leading me to believe the values are being pulled back from somewhere on page.
Try this:
var old = Shopify.EditVariantPrice.prototype.onFocus;
Shopify.EditVariantPrice.prototype.onFocus = function(t) {
this.price = '50.00'; // Use the price you want here
old.call(this, t);
};
jQuery('#product_variants__price').siblings().triggerHandler("focus");
jQuery('#product_variants__price').siblings().triggerHandler("blur");
If it works for you, it's possible that the following will be sufficient:
Shopify.EditVariantPrice.prototype.onFocus = function(t) {
this.price = '50.00'; // Use the price you want here
};
Well, there is a kind of a dirty solution...
First of all you'll need a sendkeys plugin. In fact that means you'll need to include this and this JS libraries (you can just copy-paste them in the console to test). If you don't want to use the first library (I personally find it quite big for such a small thing) you can extract only the key things out of it and use only them.
The next step is creating the function which is going to act like a real user:
function input(field, desiredValue) {
// get the currency symbol while value is still pristine
var currency = field.val()[0];
// move focus to the input
field.click().focus();
// remove all symbols from the input. I took 10, but of course you can use value.length instead
for (var i = 0; i < 10; i++) field.sendkeys("{backspace}");
// send the currency key
field.sendkeys(currency);
// send the desired value symbol-by-symbol
for (var i = 0; i < desiredValue.length; i++) field.sendkeys(desiredValue[i]);
}
Then you can simply call it with the value you wish to assign:
input($("#product_variants__price").next(), "123.00");
I did not really manage to fake the blur event because of lack of the time; that is why I was forced to read the currency and pass .00 as a string. Anyway you already have a way to go and a quite working solution.
Looks like you're trying to automate editing of variant prices of products in Shopify's admin panel.
Instead of playing around with the DOM of Shopify's admin page, I'll suggest using Shopify's bulk product editor which lets you set prices of all variants in a single screen. I feel that you'll have better luck setting the variant prices using JavaScript on the bulk product editor page.
Clicking on the 'Edit Products' button as shown in the screenshot below will open the bulk product editor.
Also check if browser based macro recording plugins like iMacro can be of your help (you can also code macros with JS in iMacro).

AngularJS - How to keep original value if I don't hit submit

I have a list of users on the left with the details on the right.
The right details are handled with a form, and inputs with ng-model. So when I click on a user on the left, I change the selected user and the model automatically shows me the proper details on the right.
I also have a submit button that saves changes to the server.
This all works well, but the problem is that if I change a user's name for example, click on another user, and then come back to the first user, I see the changed name, but the info was never saved on the server. So the client might think that the info was changed on the server, when it wasn't. After I hit refresh, I get the old, server values back.
So how can I revert model changes if I switch the model before hitting save?
By the way, I'm using ng-model instead of using {{ }} in the value field, because AngularJS validators don't work unless you use ng-model
Thanks
Everytime user selects the item in the left, copy that selected object into new variable. You can copy using angular.copy function. Then show that new variable on the right side in the form. Right now changes are "saved" because you are referencing items in your array (so when you change something in form it is changed into this item in array). If you have new variable for selectedItem you won't have this problem any more.
I suggest binding an object to the input fields of your form and using ng-submit to call a function to update your values on the server, much like Nemanja suggested.
I went ahead and made a simple JS Fiddle so you have a small example to structure your code around. Hope it helps!
HTML:
<form ng-model="data" ng-submit="simple.saveName()">
Name: {{ simple.data.name }}
<br />Twitter: {{ simple.data.twitter }}
<br />
<br />New name:
<input type="text" ng-model="simple.newName" />
<br />
<button>Submit</button>
</form>
JavaScript:
(function () {
angular
.module('app', [])
.controller('simpleController', SimpleController);
function SimpleController() {
var vm = this;
// Public stuff.
vm.data = {
name: 'Joe',
twitter: '#martellaj'
};
vm.newName;
vm.saveName = saveName;
// Private stuff.
function saveName() {
console.log('Saving name...');
vm.data.name = vm.newName;
};
};
})();

Watching for change in filtered items in angular

I have a simple Angular app which has some data, and an input box to filter that data.
<input class="form-control" ng-model="filters.generic" />
<h3>{{filteredUsers.length}} filtered users</h3>
<ul>
<li ng-repeat="user in filteredUsers = (data.userData | filter:filters.generic )">
{{user.registration.firstName}}
</li>
</ul>
The 'filteredUsers' variable stores the users with the applied filter. That all works absolutely fine. However, I'd like to be able to add a watch to execute a particular function whenever this 'filteredUsers' variable changes (whenever the user types anything in the input box).
I do not want to simply add a method to ng-change on the input, because I want to introduce other filters elsewhere.
I thought that I could do this in my controller with
$scope.$watch('filteredUsers', function () {
alert('something');
});
This executes once, when the application loads, and then never again. What must I do to make this watch for changes to filteredUsers?
You might need to use $watchCollection() instead of $watch()
Keep in mind that the filter on an ng-repeat could be running way more frequently than you need or expect it to, and adversely affect the performance of your code.
Even though you said you don't want to use ng-change, I think you should reconsider that and only update your array when needed.
<input class="form-control" ng-model="filters.generic" ng-change="genericFilterChange()" />
var updateFilteredUsers = function(){
$scope.filteredUsers = $filter('filter')($scope.data.userData, $scope.filters.generic);
alert('something');
};
var genericFilterChange = function(){
updateFilteredUsers();
updateOtherFilteredThings();
};

Making Meteor reactive for html elements

So I have a modal box that allows the user to edit / save some data.
I just want to add that unlike other Meteor apps, I don't want to save the data straight away - I want the user to fill in all the fields before hitting save where it will save to the database and send to server etc. This is mainly because I want the user to be able to hit the "cancel" button to revert all changes.
I have a drop down box at the start of the form where depending on the value, fields will be shown or hidden
<select class="form-control" id="ddlNewInputType" placeholder="Enter your input type">
<option value="input">Input</option>
<option value="formula">Formula</option>
</select>
And I have a handlebar around a field like this to determine whether I want to show it
{{#if isFormula }}
<div class="row">
<input type="text"
id="txtNewInputFormula" placeholder="Enter formula">
</div>
{{/if}}
With a helper looking like this
isFormula: ->
$('#ddlNewInputType').val() == 'formula'
However, this doesn't work. Aside from when it first loads onto the page, it never hits isFormula, probably because Meteor doesn't consider any of the HTML elements as reactive so it never re-evaluates when the HTML element changes.
What is a suitable way to get around this? Is it possible to make something reactive explicitly in Meteor? I was also considering putting the dropdown list value into a session variable but that just seems messy because I'm going to need to manage this session variable (remember to clear it when the modal box closes etc.)
Your analysis is correct - a reactive variable needs to be involved in order for your helper to reevaluate after changing the select element. The basic strategy looks like:
Initialize a reactive variable when the template is created.
Whenever the select changes, update the reactive variable.
Read the reactive variable in your helper.
Rather than use a session variable, let's use a ReactiveVar scoped to your template. Here's an example set of modifications:
Template.myTemplate.helpers({
isFormula: function() {
return Template.instance().isFormula.get();
}
});
Template.myTemplate.events({
'change #ddlNewInputType': function (e, template) {
var isFormula = $(e.currentTarget).val() === 'formula';
template.isFormula.set(isFormula);
}
});
Template.myTemplate.created = function() {
// in your code, default this to the current value from
// your database rather than false
this.isFormula = new ReactiveVar(false);
};
Remember that you'll need to also do:
$ meteor add reactive-var
See my post on scoped reactivity for a full explanation of this technique.

Angular, required check on controller throughout multiple forms

I am trying to find a simple solution to a required input type of scenario. I have multiple small forms that all send on one button save on the bottom of the page. What I am trying to accomplish is something like ngRequired, however across the whole controller, not just the individual forms. So the desired effect is pretty simple - if any of the inputs aren't filled out - set a boolean( or something) to false that disables the save button at the bottom.
So my first attempt is like this -
I have a model on each of the required items - there are 10 items
then I have a function that checks when you try to click the button how many are chcked like this
if($scope.modeltracker1){
//if there is anything inside model 1 add 1 to the tracker
$scope.modeltracker += 1;
}
and if the counter is not 10, don't do anything (all required are not filled out)
if($scope.modeltracker != 10){
//do nothing because all required are not filed out
}else{
//run save, all required all filed out
}
So - I feel like there should be a much easier solution than my first attempt here. Maybe something along the lines of checking if any individual one of these required fields is false, don't fire? I know that ngRequied would be great for this, but unfortunately the way this has to be structured, it cannot be one large form. There has to be a much easier way to accomplish this task with angular.
Any input would be much appreciated, thanks for reading!!
You can use ng-form to nest your multiple forms. It allows using nested forms and validating multiple forms as one form.
So, you need to nest your multiple forms in one root form.
<div ng-controller="demoController">
<form name="parentForm">
<ng-form name="firstForm">
<input type="text" ng-model="firstModel" required>
</ng-form>
<ng-form name="secondForm">
<input type="text" ng-model="secondModel" required>
</ng-form>
</form>
</div>
Then, all you need to do is to check parent form's validation status.
angular.module('formDemo', [])
.controller('demoController', ['$scope', function($scope) {
if($scope.parentForm.$valid) {
//run save, all required all filed out
} else {
//do nothing because all required are not filed out
}
}]);
you can use myForm.$invalid directive, as explained here: Disable submit button when form invalid with AngularJS

Categories