Shopify PLUS - additional checkout custom field - shopify

I was trying to add additional custom field in the checkout screen and here is my code:
<div class="additional-checkout-fields" style="display:none">
<div class="fieldset fieldset--address-type" data-additional-fields>
<div class="field field--optional field--address-type">
<h2 class="additional-field-title">ADDRESS TYPE</h2>
<div class="field__input-wrapper">
<label>
<input data-backup="Residential" class="input-checkbox" aria-labelledby="error-for-address_type" type="checkbox" name="checkout[Residential]" id="checkout_attributes_Residential" value="Residential" />
<span>Residential</span>
</label>
<label>
<input data-backup="Commercial" class="input-checkbox" aria-labelledby="error-for-address_type" type="checkbox" name="checkout[Commercial]" id="checkout_attributes_Commercial" value="Commercial" />
<span>Commercial</span>
</label>
</div>
</div>
</div>
</div>
<script type="text/javascript">
if (window.jQuery) {
jquery = window.jQuery;
} else if (window.Checkout && window.Checkout.$) {
jquery = window.Checkout.$;
}
jquery(function() {
if (jquery('.section--shipping-address .section__content').length) {
var addType = jquery('.additional-checkout-fields').html();
jquery('.section--shipping-address .section__content').append(addType);
}
});
</script>
It returns the checkout page like this -
The problem is - once I click continue button and comes back to this page again, I don't see the checkbox checked. I feel the values are not being passed or may be something else.
What am I missing?

From the usecase, it looks like you want the user to select the Address Type either Residential or Commercial so a raido button group seems more suitable. I have edited the HTML to create the Radio Button instead of Checkbox. To maintain the state, I have used Session Storage. You may also replace Session Storage with Local Storage if you want to do so. For explanation check code comments.
<div class="additional-checkout-fields" style="display:none">
<div class="fieldset fieldset--address-type" data-additional-fields>
<div class="field field--optional field--address-type">
<h2 class="additional-field-title">ADDRESS TYPE</h2>
<div class="field__input-wrapper">
<label>
<input class="input-radio" aria-label="" type="radio" name="checkout[address_type]" id="checkout_attributes_Residential" value="residential" checked>
<span>Residential</span>
</label>
<label>
<input class="input-radio" aria-label="" type="radio"name="checkout[address_type]" id="checkout_attributes_Commercial" value="commercial">
<span>Commercial</span>
</label>
</div>
</div>
</div>
</div>
JavaScript part
<script type = "text/javascript" >
if (window.jQuery) {
jquery = window.jQuery;
} else if (window.Checkout && window.Checkout.$) {
jquery = window.Checkout.$;
}
jquery(function() {
if (jquery('.section--shipping-address .section__content').length) {
var addType = jquery('.additional-checkout-fields').html();
jquery('.section--shipping-address .section__content').append(addType);
// Get saved data from sessionStorage
let savedAddressType = sessionStorage.getItem('address_type');
// if some value exist in sessionStorage
if (savedAddressType !== null) {
jquery('input[name="checkout[address_type]"][value=' + savedAddressType + ']').prop("checked", true);
}
// Listen to change event on radio button
jquery('input[name="checkout[address_type]"]').change(function() {
if (this.value !== savedAddressType) {
savedAddressType = this.value;
sessionStorage.setItem('address_type', savedAddressType);
}
});
}
});
</script>

You are responsible for managing the state of your added elements. Shopify could care a less about stuff you add, so of course when you flip around between screens, it will be up to you to manage the contents. Use localStorage or a cookie. Works wonders. As a bonus exercise, ensure that your custom field values are assigned to the order when you finish a checkout. You might find all your hard work is for nothing as those value languish in la-la land unless you explicitly add them as order notes or attributes.

Related

Assign a value in Vue.js onchange of input

I there guy's im struggling with a form that i need to complete using Vue.JS. Basically i need that the value of the field 'price_vat' its updated with some calculations evry time that input called 'price_user' is updated. Usign jquery evrything is going as well. Butt data is not passed to POST method using Vue.
<div class="col-md-6" v-show="form.active">
<div class="form-group">
<label >{{__('Price')}}</label>
<input type="number" v-model="form.price_user" class="form-control">
</div>
</div>
<div class="col-md-6" v-show="form.active">
<div class="form-group">
<label >{{__('Price with VAT')}}</label>
<input type="number" v-model="form.price_vat" class="form-control">
</div>
</div>
if i understand you correctly,, you want form.price_vat to change, every time you change form.price_user by typing inside the input.
you can do this using watch. just add the below your methods in vue:
watch:{
'form.price_user':function():{
this.form.price_vat += 1
},
}
so in this code, you update the value of form.price_vat by 1 every time the form.price_user changes. you can do anything inside the function of watch.
the complete vue part will be :
data(){
return:{
form:{
price_vat :'',
price_user : '',
}
}
},
methods:{},
watch:{
'form.price_user':function():{
this.form.price_vat += 1
},
}

How to pass a dynamicy changed model to Partial view?

I have a list of "workbooks" displayed in a table. Each workbook has a "Share" button next to the workbook's title. When the user clicks on the share button a modal dialog is shown containing a form.
The form allows the user to enter a list of the recipient's emails separated by a comma which is validated on the client-side.
As the dialog is located in a partial view _ShareView.cshtml that allows me to pass a modal WorkbookShareModel that has some fields like WorkbookId and Title. The goal here is to pass the details of each workbook when the user presses the share button (i.e. construct a modal and pass it to the already rendered model).
I am not sure how to pass a model to an already rendered view?
The solution have to be done on the client (i.e. dont involve actions on the server that return the partial view provided the parameters are passed). I want to avoid unnesessary calls to the server - we have all the data on the client regarding a workbook and I need to do a POST when the user types in list of emails.
This is my index.cshtml:
#section BodyFill
{
<div id="shareFormContainer">
#{ await Html.RenderPartialAsync("_ShareView", new WorkbookShareModel());}
</div>
<div class="landing-container">
<div class="workbook-container">
<table class="table">
<tbody>
#foreach (var workbook in Model.Workbooks)
{
string trClassName, linkText;
if (workbook.Metadata.SharedBy == null)
{
trClassName = "saved-workbooks";
linkText = workbook.Name;
} else {
trClassName = "shared-with-me";
linkText = string.Format(
BaseLanguage.SharedWithMeWorkbook,
workbook.Name,
workbook.Metadata.SharedBy,
workbook.Metadata.SharedDate.ToShortDateString()
);
}
<tr class="#trClassName">
<td>#Html.ActionLink(linkText, "Open", "OpenAnalytics", new { id = Model.Id, workbook = workbook.Name })</td>
<td class="last-modified-date" title="Last Modified Date">#workbook.ModifiedDate.ToShortDateString()</td>
<td class="share">
<button title="Share" class="share-button" onclick='showSharingView("#workbook.Name", "#workbook.Id", "#Model.Id")'> </button>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
#section Scripts
{
<!--Load JQuery 'unobtrusive' validation -->
#await Html.PartialAsync("_ValidationScriptsPartial")
<script type="text/javascript">
// hide the modal as soon as the page loads
$('#shareFormModal').modal("hide");
function showSharingView(title, workbookId, id) {
$('#shareFormModal').modal("show");
// how to pass a WorkbookShareModel to my partial view from here?
}
function hideDialog() {
var form = $("#partialform");
// only hide the dialog if the form is valid
if (form.valid()) {
activateShareButtons();
$('#shareFormModal').modal("hide");
}
}
// Helper method that validates list of emails
function IsEmailValid(emailList, element, parameters) {
var SPLIT_REGEXP = /[,;\s]\s*/;
var EMAIL_REGEXP =
/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+##[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$/i;
var emails = emailList.split(SPLIT_REGEXP);
for (var i = emails.length; i--;) {
if (!EMAIL_REGEXP.test(emails[i].trim())) {
return false;
}
}
return true;
}
</script>
}
That is my dialog:
#using DNAAnalysisCore.Resources
#model DNAAnalysisCore.Models.WorkbookShareModel
#* Partial view that contains the 'Share Workbook dialog' modal *#
<!-- Modal -->
<div onclick="activateShareButtons()" class="modal fade" id="shareFormModal" role="dialog">
<div class="modal-dialog modal-md">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Share Workbook - #Model.Title</h4>
</div>
#using (Html.BeginForm("ShareWorkbook", "Home", FormMethod.Post, new { #id = "partialform" }))
{
<div class="modal-body">
<label>#BaseLanguage.Share_workbook_Instruction_text</label>
<div class="form-group">
<textarea class="form-control" asp-for="Emails" rows="4" cols="50" placeholder="#BaseLanguage.ShareDialogPlaceholder"></textarea>
<span asp-validation-for="Emails" class="text-danger"></span>
</div>
<input asp-for="Title" />
<input asp-for="Id" />
<input asp-for="WorkbookId"/>
</div>
<div class="modal-footer">
<button onclick="hideDialog()" type="submit" class="btn btn-primary">Share</button>
<button onclick="activateShareButtons()" id="btnCancelDialog" type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
}
</div>
</div>
</div>
There are two solutions to solve your problem :
Option 1 :
Since you have got the parameters(title, workbookId, id) , you can call server side function using AJAX to render the partial view , then replace the DIV contained in the partial view with the updated contents in the callback function of AJAX .
You can click here for code sample .
Option 2 :
Directly update related input/area using Jquery . For example , the input tag helper :
<input asp-for="<Expression Name>">
generates the id and name HTML attributes for the expression name specified in the asp-for attribute. So you can set the value using Jquery like :
$("#Title").val("Title")
Please click here for Tag Helpers in forms in ASP.NET Core
With Option 2 , you need to clear the Emails area firstly after user click the share button ; With Option 1 , you don't need to care that since the HTML will replace entirely .

How to validate a formcontrol with prepopulated data in Angular 5?

I have a form-group. In the form-group, i have a form-array. I have initialized the array with items created from data in the data model with the method as described in angular's documentation on reactive forms, under the section Initialize the secretLairs FormArray. The problem is i need to perform validation.required for each form control in the array. However, since the form-controls already hold values, i do not know how to perform validation.required. The documentation did not go further on how to validate formcontrols in a formarray that are prepopulated.
Here are my source codes:
.html
<!-- list of Questions -->
<div formArrayName="questions">
<!-- <div *ngFor="let que of Questions; let k=index"> -->
<div *ngFor="let question of Ques ; let i=index" [formGroupName]="i" >
<!-- The repeated questions template -->
<h4>{{question.ques}}</h4>
<div style="margin-left: 1em;">
<!-- <div class="form-group">
<label class="center-block">
<input class="form-control" formControlName="ques" >
</label>
</div> -->
<div class="form-group radio" *ngFor="let choice of
question.choices; let j = index">
<input type="radio" formControlName="choices"
class="custom-control-input" [value]="choice.choiceText">
<label>{{choice.choiceText}}</label>
</div>
<br>
<!-- End of the repeated questions template -->
</div>
</div>
</div>
<button type="submit" class="btn btn-danger"
[disabled]="!CheckListForm.valid">Submit</button>
</form>
.ts
export class CheckListFormComponent implements OnInit, OnChanges {
CheckListForm: FormGroup;
Ques: Questions[];
employmenttype = ['Permanent', 'contractor'];
constructor(private fb: FormBuilder,
private checklistservice: ChecklistService) {
this.CreateForm();
}
ngOnInit() {
this.checklistservice.getQuestions(1).subscribe(res =>{ this.Ques =res;
this.setquestions(this.Ques)
});
this.CheckListForm.get('EmploymentType').valueChanges.subscribe(
(EmploymentType: string) => {
if (EmploymentType === 'Permanent') {
this.CheckListForm.get('HRMS').setValidators([Validators.required]);
this.CheckListForm.get('CompanyName')
.setValidators([Validators.nullValidator]);
} else if (EmploymentType === 'contractor') {
this.CheckListForm.get('CompanyName').
setValidators([Validators.required]);
this.CheckListForm.get('HRMS').
setValidators([Validators.nullValidator]);
}
this.CheckListForm.get('HRMS').updateValueAndValidity();
this.CheckListForm.get('CompanyName').updateValueAndValidity();
}
)
}
CreateForm() {
this.CheckListForm = this.fb.group({
name: ['', Validators.required],
EmploymentType: ['', Validators.required],
HRMS: [''],
CompanyName:[''],
questions: this.fb.array([])
})
}
get questions(): FormArray {
return this.CheckListForm.get('questions') as FormArray;
}
setquestions(questions: Questions[]) {
const QuestionsFGs = questions.map(questions => this.fb.group(questions));
const QuestionsFormArray = this.fb.array(QuestionsFGs);
this.CheckListForm.setControl('questions', QuestionsFormArray);
}
As usual, as there is no response from anyone, I will post the answer which i have painstakingly solved.
You cannot validate form-controls through the method i have performed(see my codes). The reason is because the form-controls already hold data and is not empty in the first place. Hence, validation.required will not work.
A solution will be to instead push empty form-controls for each item in the array of the data model.
Validation.required will then work for this case.

Getting form data on submit?

When my form is submitted I wish to get an input value:
<input type="text" id="name">
I know I can use form input bindings to update the values to a variable, but how can I just do this on submit. I currently have:
<form v-on:submit.prevent="getFormValues">
But how can I get the value inside of the getFormValues method?
Also, side question, is there any benefit to doing it on submit rather than updating variable when user enters the data via binding?
The form submit action emits a submit event, which provides you with the event target, among other things.
The submit event's target is an HTMLFormElement, which has an elements property. See this MDN link for how to iterate over, or access specific elements by name or index.
If you add a name property to your input, you can access the field like this in your form submit handler:
<form #submit.prevent="getFormValues">
<input type="text" name="name">
</form>
new Vue({
el: '#app',
data: {
name: ''
},
methods: {
getFormValues (submitEvent) {
this.name = submitEvent.target.elements.name.value
}
}
}
As to why you'd want to do this: HTML forms already provide helpful logic like disabling the submit action when a form is not valid, which I prefer not to re-implement in Javascript. So, if I find myself generating a list of items that require a small amount of input before performing an action (like selecting the number of items you'd like to add to a cart), I can put a form in each item, use the native form validation, and then grab the value off of the target form coming in from the submit action.
You should use model binding, especially here as mentioned by Schlangguru in his response.
However, there are other techniques that you can use, like normal Javascript or references. But I really don't see why you would want to do that instead of model binding, it makes no sense to me:
<div id="app">
<form>
<input type="text" ref="my_input">
<button #click.prevent="getFormValues()">Get values</button>
</form>
Output: {{ output }}
</div>
As you see, I put ref="my_input" to get the input DOM element:
new Vue({
el: '#app',
data: {
output: ''
},
methods: {
getFormValues () {
this.output = this.$refs.my_input.value
}
}
})
I made a small jsFiddle if you want to try it out: https://jsfiddle.net/sh70oe4n/
But once again, my response is far from something you could call "good practice"
You have to define a model for your input.
<input type="text" id="name" v-model="name">
Then you you can access the value with
this.name inside your getFormValues method.
This is at least how they do it in the official TodoMVC example: https://v2.vuejs.org/v2/examples/todomvc.html (See v-model="newTodo" in HTML and addTodo() in JS)
Please see below for sample solution, I combined the use of v-model and "submitEvent" i.e. <input type="submit" value="Submit">. Used submitEvent to benefit from the built in form validation.
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue"></script>
</head>
<body>
<div id="app">
<form #submit.prevent="getFormValues">
<div class="form-group">
<input type="email" class="form-control form-control-user"
v-model="exampleInputEmail"
placeholder="Enter Email Address...">
</div>
<div class="form-group">
<input type="password" class="form-control"
v-model="exampleInputPassword" placeholder="Password"> </div>
<input type="submit" value="Submit">
</form>
</div>
<script>
const vm = new Vue({
el: '#app',
methods: {
getFormValues (submitEvent) {
alert("Email: "+this.exampleInputEmail+" "+"Password: "+this.exampleInputPassword);
}
}
});
</script>
</body>
</html>
The other answers suggest assembling your json POST body from input or model values, one by one. This is fine, but you also have the option of grabbing the whole FormData of your form and whopping it off to the server in one hit. The following working example uses Vue 3 with Axios, typescript, the composition API and setup, but the same trick will work anywhere.
I like this method because there's less handling. If you're old skool, you can specify the endpoint and the encoding type directly on the form tag.
You'll note that we grab the form from the submit event, so there's no ref, and no document.getElementById(), the horror.
I've left the console.log() there to show that you need the spread operator to see what's inside your FormData before you send it.
<template>
<form #submit.prevent="formOnSubmit">
<input type="file" name="aGrid" />
<input type="text" name="aMessage" />
<input type="submit" />
</form>
</template>
<script setup lang="ts">
import axiosClient from '../../stores/http-common';
const formOnSubmit = (event: SubmitEvent) => {
const formData = new FormData(event.target as HTMLFormElement);
console.log({...formData});
axiosClient.post(`api/my-endpoint`, formData, {
headers: {
"Content-Type": "multipart/form-data",
}
})
}
</script>

Use AngularJS with ASP.NET MVC 4 to detect changes

I have an existing MVC 4 application with several groups of checkboxes and I need to detect when a user has made a change, i.e. checked or unchecked a checkbox. If the user has made a change and they try to navigate away from the page I need to prompt them to save their changes. I am just learning AngularJS, but figured I could use it to detect when a checkbox state has change and also use routes in angular to detect when a user is navigating away from the page.
I have based my code on the answer here. Since the view is already being rendered by MVC I cannot use REST services and angular to populate the model and view.
HTML rendered in the view
<div id="userPermissions" class="userWrapper" ng-app="myApp.User">
<div id="actionCategories" class="section" ng-controller="UserCtrl">
<div class="actionGroupBody">
<div class="actionGroupAction">
<input type="checkbox" value="Create new users"
id="action-f9ae022b-5a53-4824-8a79-f7bbac844b11"
data-action-category="8aefed6e-b76c-453f-94eb-d81d2eb284f9"
ng-checked="isSelected('f9ae022b-5a53-4824-8a79-f7bbac844b11')"
ng-click="updateSelection($event,'f9ae022b-5a53-4824-8a79-f7bbac844b11')"/>Create new users
</div>
<div class="actionGroupAction">
<input type="checkbox" value="Edit users"
id="action-5525d5e7-e1dd-4ec3-9b1d-3be406d0338b"
data-action-category="8aefed6e-b76c-453f-94eb-d81d2eb284f9"
ng-checked="isSelected('5525d5e7-e1dd-4ec3-9b1d-3be406d0338b')"
ng-click="updateSelection($event,'5525d5e7-e1dd-4ec3-9b1d-3be406d0338b')"/>Edit users
</div>
<div class="actionGroupAction">
<input type="checkbox" value="Edit personal account"
id="action-9967c1c2-c781-432b-96df-224da760bfb6"
data-action-category="8aefed6e-b76c-453f-94eb-d81d2eb284f9"
ng-checked="isSelected('9967c1c2-c781-432b-96df-224da760bfb6')"
ng-click="updateSelection($event,'9967c1c2-c781-432b-96df-224da760bfb6')"/>Edit personal account
</div>
</div>
<div class="caption">
<label class="actionGroupCaption">Store</label> <span class="actionCategorySelectAll"><input type="checkbox" value="select all Store" id="7bace6c1-4820-46c2-b463-3dad026991f2" data-action-category="selectall"/>All</span>
</div>
<div class="actionGroupBody">
<div class="actionGroupAction">
<input type="checkbox" value="Access home page"
id="action-fba7e381-4ed8-47ce-8e85-b5133c9ba9f7"
data-action-category="7bace6c1-4820-46c2-b463-3dad026991f2"
ng-checked="isSelected('fba7e381-4ed8-47ce-8e85-b5133c9ba9f7')"
ng-click="updateSelection($event,'fba7e381-4ed8-47ce-8e85-b5133c9ba9f7')"/>Access home page
</div>
<div class="actionGroupAction">
<input type="checkbox" value="Edit settings"
id="action-2d02b77b-14a4-4136-a09f-fd51eecd2dbe"
data-action-category="7bace6c1-4820-46c2-b463-3dad026991f2"
ng-checked="isSelected('2d02b77b-14a4-4136-a09f-fd51eecd2dbe')"
ng-click="updateSelection($event,'2d02b77b-14a4-4136-a09f-fd51eecd2dbe')"/>Edit settings
</div>
<div class="actionGroupAction">
<input type="checkbox" value="Edit products"
id="action-f42f933c-a2b8-42e8-af4b-d52f90f58ddb"
data-action-category="7bace6c1-4820-46c2-b463-3dad026991f2"
ng-checked="isSelected('f42f933c-a2b8-42e8-af4b-d52f90f58ddb')"
ng-click="updateSelection($event,'f42f933c-a2b8-42e8-af4b-d52f90f58ddb')"/>Edit products
</div>
<div class="actionGroupAction">
<input type="checkbox" value="Edit orders"
id="action-92ed258b-c954-46e4-b5c9-a89fdb5c54d9"
data-action-category="7bace6c1-4820-46c2-b463-3dad026991f2"
ng-checked="isSelected('92ed258b-c954-46e4-b5c9-a89fdb5c54d9')"
ng-click="updateSelection($event,'92ed258b-c954-46e4-b5c9-a89fdb5c54d9')"/>Edit orders
</div>
</div>
</div>
</div>
here's the angular code
var app = angular.module('myApp.User', []);
app.controller('UserCtrl', function ($scope) {
$scope.entities = [{ //how to populate with checkboxes state from view? factory maybe similar to below??? // }];
$scope.selected = [];
var updateSelected = function (action, id) {
if (action == 'add' & $scope.selected.indexOf(id) == -1)
$scope.selected.push(id);
if (action == 'remove' && $scope.selected.indexOf(id) != -1)
$scope.selected.splice($scope.selected.indexOf(id), 1);
};
$scope.updateSelection = function ($event, id) {
var checkbox = $event.target;
var action = (checkbox.checked ? 'add' : 'remove');
updateSelected(action, id);
};
$scope.selectAll = function ($event) {
var checkbox = $event.target;
var action = (checkbox.checked ? 'add' : 'remove');
for (var i = 0; i < $scope.entities.length; i++) {
var entity = $scope.entities[i];
updateSelected(action, entity.id);
}
};
$scope.getSelectedClass = function (entity) {
return $scope.isSelected(entity.id) ? 'selected' : '';
};
$scope.isSelected = function (id) {
return $scope.selected.indexOf(id) >= 0;
};
$scope.isSelectedAll = function () {
return $scope.selected.length === $scope.entities.length;
};
});
app.factory('UserDataService', function () {
var service = {}
service.getData = function () {
var actions = $("input[id^=action-]");
return actions;
}
return service;
});
Whenever I click a checkbox none of the $scope functions (.updateSelection, .isSelected, etc.) fire in the controller.