PayPal API PopUp closes immediately after clicking PayPal button (Sandbox) - django

I am trying to implement the PayPal API into my Django / Vue checkout system, but everytime i try to access checkout via the paypal checkout buttons, thepopup closes immediately and i get these errors:
Error messages in developer tools
Obviously it has something to do with the cart's items' attributes and i tried to adjust them accordingly, but i can't figure out how to fix it. My Code:
<template>
<div class="page-checkout">
<div class="columns is-multiline">
<div class="column is-12">
<h1 class="title">Kasse</h1>
</div>
<div class="column is-12 box">
<table class="table is-fullwidth">
<thead>
<tr>
<th>Artikel</th>
<th>Preis</th>
<th>Anzahl</th>
<th>Gesamt</th>
</tr>
</thead>
<!-- vue for loop for looping through items in cart and displaying them in a table -->
<tbody>
<tr
v-for="item in cart.items"
v-bind:key="item.product.id"
>
<td>{{ item.product.name }}</td>
<td>{{ item.product.price }}€</td>
<td>{{ item.quantity }}</td>
<td>{{ getItemTotal(item).toFixed(2) }}€</td>
</tr>
</tbody>
<tfoot>
<tr style="font-weight: bolder; font-size: larger;">
<td colspan="2">Insgesamt</td>
<td>{{ cartTotalLength }}</td>
<td>{{ cartTotalPrice.toFixed(2) }}€</td>
</tr>
</tfoot>
</table>
</div>
<!-- Fields for user information -->
<div class="column is-12 box">
<h2 class="subtitle">Versanddetails</h2>
<p class="has-text-grey mb-4">*Felder müssen ausgefüllt sein</p>
<div class="columns is-multline">
<div class="column is-6">
<div class="field">
<label>*Vorname</label>
<div class="control">
<input type="text" class="input" v-model="first_name">
</div>
</div>
<div class="field">
<label>*Nachname</label>
<div class="control">
<input type="text" class="input" v-model="last_name">
</div>
</div>
<div class="field">
<label>*E-Mail</label>
<div class="control">
<input type="email" class="input" v-model="email">
</div>
</div>
<div class="field">
<label>*Telefonnummer</label>
<div class="control">
<input type="text" class="input" v-model="phone">
</div>
</div>
<div class="column is-6">
<div class="field">
<label>*Adresse</label>
<div class="control">
<input type="text" class="input" v-model="address">
</div>
</div>
<div class="field">
<label>*Postleitzahl</label>
<div class="control">
<input type="text" class="input" v-model="zipcode">
</div>
</div>
<div class="field">
<label>*Ort</label>
<div class="control">
<input type="text" class="input" v-model="place">
</div>
</div>
</div>
<!-- looping through errors for authorization -->
<div class="notification is-danger mt-4" v-if="errors.length">
<p v-for="error in errors" v-bind:key="error">{{ error }}</p>
</div>
<hr>
<div id="card-element" class="mb-5"></div>
<!-- Part for PayPal implementation -->
<template v-if="cartTotalLength">
<hr>
<!-- Set up a container element for the button -->
<div id="paypal-button-container"></div>
<!-- Include the PayPal JavaScript SDK -->
<div ref="paypal"></div>
<!-- <button class="button is-dark" #click="submitForm">Weiter mit PayPal</button> -->
</template>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'Checkout',
data() {
return {
loaded: false,
paidFor: false,
cart: {
items: []
},
stripe: {},
card: {},
first_name: '',
last_name: '',
email: '',
phone: '',
address: '',
zipcode: '',
place: '',
errors: []
}
},
mounted() {
document.title = 'Kasse | RELOAD'
this.cart = this.$store.state.cart
// Imported PayPal functionality from https://fireship.io/lessons/paypal-checkout-frontend/
const script = document.createElement("script");
script.src = "https://www.paypal.com/sdk/js?client-id=AeAKmRDX7HTesdUvfqYqCQz3fZHIkjAoQ5-BG6K-7xnL5GBMVvwQba53v-I8Fx1p9wurUtoBuk7D6bV1"; // Replace YOUR-CLIENT-ID with paypal credentials
script.addEventListener("load", this.setLoaded);
document.body.appendChild(script); // Adds script to the document's body
},
watch: {
$route(to, from,) {
if (to.name === 'Category') {
this.getCategory()
}
}
},
methods: {
getItemTotal(item) {
return item.quantity * item.product.price
},
// Authorization methods
submitForm() {
this.errors = []
if (this.first_name === '') {
this.errors.push('Bitte Vornamen angeben')
}
if (this.last_name === '') {
this.errors.push('Bitte Nachnamen angeben')
}
if (this.email === '') {
this.errors.push('Bitte E-Mail angeben')
}
if (this.phone === '') {
this.errors.push('Bitte Telefonnummer angeben')
}
if (this.adress === '') {
this.errors.push('Bitte Adresse angeben')
}
if (this.zipcode === '') {
this.errors.push('Bitte Postleitzahl angeben')
}
if (this.place === '') {
this.errors.push('Bitte Ort angeben')
}
},
// Imported PayPal functionality from https://fireship.io/lessons/paypal-checkout-frontend/
setLoaded() {
this.loaded = true;
window.paypal
.Buttons({
// createOrder: (data, actions) => {
// return actions.order.create({
// purchase_units: [
// {
// description: this.product.description,
// amount: {
// currency_code: "EUR",
// value: this.product.price
// }
// }
// ]
createOrder: (data, actions) => {
return actions.order.create({
"purchase_units": [{
"amount": {
"currency_code": "EUR",
"value": item.product.price,
"breakdown": {
"item_total": { /* Required when including the items array */
"currency_code": "EUR",
"value": item.quantity * product.price
}
}
},
"items": [
{
"name": item.product.name, /* Shows within upper-right dropdown during payment approval */
"description": item.product.description, /* Item details will also be in the completed paypal.com transaction view */
"unit_amount": {
"currency_code": "EUR",
"value": item.product.price
},
"quantity": item.product.quantity
},
]
}]
});
},
onApprove: async (data, actions) => {
const order = await actions.order.capture();
this.paidFor = true;
console.log(order);
},
onError: err => {
console.log(err);
}
})
.render(this.$refs.paypal);
}
},
computed: {
// Computing total price and total amount of all items
cartTotalPrice() {
return this.cart.items.reduce((acc, curVal) => {
return acc += curVal.product.price * curVal.quantity
}, 0)
},
cartTotalLength() {
return this.cart.items.reduce((acc, curVal) => {
return acc += curVal.quantity
}, 0)
}
}
}
</script>

The createOrder function inside setLoaded() is assigning properties using item although it's not clear where item comes from as it's not being passed in from anywhere:
setLoaded() {
this.loaded = true;
window.paypal.Buttons({
createOrder: (data, actions) => {
return actions.order.create({
purchase_units: [
{
amount: {
currency_code: "EUR",
value: item.product.price, // <--- item is not defined here
breakdown: {
item_total: {
currency_code: "EUR",
value: item.quantity * product.price, // <--- or here
},
},
},
...
If you mean to reference the items in the cart data property you need to use this.cart.items and possibly loop through it if you're trying to send individual item details.

Related

Do not want to submit form until email and phone are validated - Vue.js 3

I have a form that I am validating - all fields are required. Everything seems to be working except my form submits even when an incorrect phone and email are entered (according to my regex Watch validation). What do I seem to be missing to make sure that the default refresh is prevented if the email and phone are entered incorrectly?
First section is my html form, the second is my Vue.js 3 app code.
<form name="mainForm" #submit="submitForm" method="post">
<div class="row mb-3 gx-2">
<div class="col-md-6">
<label class="form-label aw-font-freight-medium mb-0"
>First Name*</label
>
<input
type="text"
name="firstname"
class="form-control aw-font-freight-medium aw-firstname"
v-model="contact.firstName"
required
/>
</div>
<div class="col-md-6">
<label class="form-label aw-font-freight-medium mb-0"
>Last Name*</label
>
<input
type="text"
name="lastname"
class="form-control aw-font-freight-medium"
v-model="contact.lastName"
required
/>
</div>
</div>
<div class="row mb-3">
<div class="col">
<label class="form-label aw-font-freight-medium mb-0"
>Email Address *</label
>
<input
type="text"
name="email"
class="form-control aw-font-freight-medium"
v-model="contact.email"
required
/>
<small class="form-text text-danger" v-if="msg.email"
>{{msg.email}}</small
>
</div>
</div>
<div class="row mb-3 gx-2">
<div class="col">
<label class="form-label aw-font-freight-medium mb-0"
>Phone*</label
>
<input
type="text"
name="phone"
class="form-control aw-font-freight-medium"
v-model="contact.phone"
required
/>
<small class="form-text text-danger" v-if="msg.phone"
>{{msg.phone}}</small
>
</div>
</div>
<a href="#" target="_blank">
<button type="submit" class="btn aw-bg-orange text-light">
<strong>Download</strong>
</button></a
>
<div>
<small class="form-text text-muted">
<em>* Denotes a required field.</em>
</small>
</div>
</form>
const app = Vue.createApp({
data() {
return {
currentYear: new Date().getFullYear(),
now: new Date().toISOString(),
isSubmitted: false,
msg: [],
contact: {
firstName: "##firstname##",
lastName: "##lastname##",
email: "##email##",
phone: "##phone##",
address: "##address##",
city: "##city##",
state: "##state##",
zip: "##zip##",
checked: false,
},
};
},
watch: {
contact: {
handler(newContact) {
this.validateEmail(newContact.email);
this.validatePhone(newContact.phone);
},
deep: true,
},
},
methods: {
submitForm(e) {
const isValid =
this.contact.firstName ||
this.contact.lastName ||
this.contact.email ||
this.contact.phone;
if (!isValid) {
e.preventDefault();
}
},
validateEmail(value) {
if (
/^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*#[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(\.[a-zA-Z]{2,20})$/.test(
value
)
) {
this.msg["email"] = "";
} else {
this.msg["email"] = "Please enter a valid email address.";
}
},
validatePhone(value) {
if (
/^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/im.test(
value
)
) {
this.msg["phone"] = "";
} else {
this.msg["phone"] = "Please enter a valid, 10 digit phone number.";
}
},
},
});
app.mount("#awApp");
I think your isValid is not working as you expect
const isValid =
this.contact.firstName ||
this.contact.lastName ||
this.contact.email ||
this.contact.phone;
The value of isValid 👆 will return ##firstname##
You should use && instead of || to make sure all values are set. And you may want to include the validators.
You could do that by having validateEmail and validatePhone return a boolean and then you can do
submitForm(e) {
const isValid =
this.contact.firstName &&
this.contact.lastName &&
this.contact.email &&
this.contact.phone &&
this.validateEmail(this.contact.email) &&
this.validatePhone(this.contact.phone);
if (!isValid) {
e.preventDefault();
}
},
update
const app = Vue.createApp({
data() {
return {
currentYear: new Date().getFullYear(),
now: new Date().toISOString(),
isSubmitted: false,
msg: [],
contact: {
firstName: "##firstname##",
lastName: "##lastname##",
email: "##email##",
phone: "##phone##",
address: "##address##",
city: "##city##",
state: "##state##",
zip: "##zip##",
checked: false,
},
};
},
watch: {
contact: {
handler(newContact) {
this.validateEmail(newContact.email);
this.validatePhone(newContact.phone);
},
deep: true,
},
},
methods: {
submitForm(e) {
const isValid =
this.contact.firstName &&
this.contact.lastName &&
this.contact.email &&
this.contact.phone &&
this.validateEmail(this.contact.email) &&
this.validatePhone(this.contact.phone);
console.log(isValid)
if (!isValid) {
e.preventDefault();
}
},
validateEmail(value) {
if (
/^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*#[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(\.[a-zA-Z]{2,20})$/.test(
value
)
) {
this.msg["email"] = "";
return true
} else {
this.msg["email"] = "Please enter a valid email address.";
return false;
}
},
validatePhone(value) {
if (
/^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/im.test(
value
)
) {
this.msg["phone"] = "";
return true
} else {
this.msg["phone"] = "Please enter a valid, 10 digit phone number.";
return false;
}
},
},
});
app.mount("#app");
<script src="https://unpkg.com/vue#3.0.11/dist/vue.global.prod.js"></script>
<div id="app">
<form name="mainForm" #submit="submitForm" method="post">
<div>
<div>
<label>First Name*</label>
<input
type="text"
name="firstname"
v-model="contact.firstName"
required
/>
</div>
<div>
<label>Last Name*</label>
<input
type="text"
name="lastname"
v-model="contact.lastName"
required
/>
</div>
</div>
<div>
<div>
<label>Email Address *</label>
<input type="text" name="email" v-model="contact.email" required />
<small v-if="msg.email">{{msg.email}}</small>
</div>
</div>
<div>
<div>
<label>Phone*</label>
<input type="text" name="phone" v-model="contact.phone" required />
<small v-if="msg.phone">{{msg.phone}}</small>
</div>
</div>
<a href="#" target="_blank">
<button type="submit">
<strong>Download</strong>
</button></a
>
<div>
<small>
<em>* Denotes a required field.</em>
</small>
</div>
</form>
</div>

How can I generate access error and error message, leaving user on the page?

In laravel / jquery apps if I need to make checks if user is logged I make in controller:
$loggedUser = Auth::user();
if ( empty($loggedUser->id) ) {
return response()->json(['error_code'=> 1, 'message'=> "You must be logged!"],HTTP_RESPONSE_INTERNAL_SERVER_ERROR);
}
as I do not need to leave the user from the page, but only restrict some functionality
I show error message above using bootstrapGrowl library.
Now with laravel 7 /livewire 1.3 / turbolinks:5 / alpine#v2 I search how can I generate error and
show similar error message, leaving user on the page ?
UPDATED :
Let me explain it with detailed example :
In laravel / jquery apps I have in JS code :
var quiz_quality_radio= $('input[name=quiz_quality_radio]:checked').val()
var href = this_frontend_home_url + "/make-quiz-quality";
$.ajax( {
type: "POST",
dataType: "json",
url: href,
data: {"quiz_quality_id": quiz_quality_radio, "vote_id": this_vote_id, "_token": this_csrf_token},
success: function( response )
{
$('input[name=quiz_quality_radio]:checked').prop('checked', false);
frontendVote.showQuizQualityResults()
popupAlert("Thank you for rating ! Your rate was added!", 'success')
},
error: function( error )
{
$('input[name=quiz_quality_radio]:checked').prop('checked', false);
popupAlert(error.responseJSON.message, 'danger') // 'info', 'success'
}
});
and relative action in control :
public function make_quiz_quality(Request $request)
{
$requestData = $request->all();
$quiz_quality_id = ! empty($requestData['quiz_quality_id']) ? $requestData['quiz_quality_id'] : '';
$vote_id = ! empty($requestData['vote_id']) ? $requestData['vote_id'] : '';
if ( ! Auth::check()) {
return response()->json(['message' => "To rate you must login to the system !"], HTTP_RESPONSE_BAD_REQUEST);
}
if (empty($quiz_quality_id)) {
return response()->json([
'message' => "To rate you must select quiz quality !",
'quiz_quality_id' => $quiz_quality_id
], HTTP_RESPONSE_OK);
}
$vote = Vote::find($vote_id);
if ($vote === null) {
return response()->json([ 'message' => "Vote Item # " . $vote_id . " not found !"],HTTP_RESPONSE_NOT_FOUND);
}
$loggedUser = Auth::user();
$found_count = QuizQualityResult
::getByVoteIdAndUserId($vote_id, $loggedUser->id)
->count();
if ($found_count > 0) {
return response()->json(['message' => "You have already rated '" . $vote->name . "' # vote !", 'vote_id' => $vote_id],
HTTP_RESPONSE_BAD_REQUEST);
}
$newVoteItemUsersResult = new QuizQualityResult();
try {
$newVoteItemUsersResult->quiz_quality_id = $quiz_quality_id;
$newVoteItemUsersResult->vote_id = $vote_id;
$newVoteItemUsersResult->user_id = $loggedUser->id;
DB::beginTransaction();
$newVoteItemUsersResult->save();
DB::commit();
} catch (Exception $e) {
DB::rollBack();
return response()->json(['message' => $e->getMessage(), 'voteCategory' => null], HTTP_RESPONSE_INTERNAL_SERVER_ERROR);
}
return response()->json(['message' => '', 'id' => $newVoteItemUsersResult->id], HTTP_RESPONSE_OK_RESOURCE_CREATED);
} // public function make_quiz_quality(Request $request)
and in case of error generated in error block I show message with function popupAlert
(implemented with bootstrapGrowl), without leaving the page.
That is what I want to make in livewire / turbolinks / alpine app. How can I do it?
UPDATED # 2:
That is just listing of items user can vote for:
<div class="table-responsive">
<table class="table text-primary">
#foreach($quizQualityOptions as $key=>$next_quiz_quality_option)
<tr>
<td>
<input class="" type="radio" name="quiz_quality_radio" id="quiz_quality_radio_{{ $next_quiz_quality_option }}" value="{{ $key }}">
<label class="col-form-label" for="quiz_quality_radio_{{ $next_quiz_quality_option }}">{{ $next_quiz_quality_option }}</label>
</td>
</tr>
#endforeach
</table>
</div>
<div class="row p-3">
<a class="btn btn-primary a_link" onclick="javascript:frontendVote.MakeQuizQuality()">Rate !</a>
</div>
with 2 restrictions :
User must be logged
Any logged user can vote only once
these 2 errors were genarated at server.
UPDATED # 3:
I found decision with using of axios, like :
<button type="submit" class="btn btn-primary btn-sm m-2 ml-4 mr-4 action_link" #click.prevent="submitNewTodo()">
Submit
</button>
submitNewTodo() {
console.log('submitNewTodo::')
let is_insert= 1
let current_toto_id= 1
axios({
method: (is_insert ? 'post' : 'patch'),
url: '/api/todos' + (!is_insert ? "/" + current_toto_id : ''),
data: {
text : this.new_todo_text,
priority : this.new_todo_priority
},
}).then((response) => {
this.new_todo_text= ''
this.new_todo_priority= ''
this.loadTodosRows()
popupAlert( 'Todo ' + (is_insert ? 'added' : 'updated') + ' successfully !', 'success' )
}).catch((error) => {
var validationErrors= convertObjectToArray(error.response.data.errors.text)
this.validation_errors_text= ''
validationErrors.map((next_error, index) => {
if(next_error && typeof next_error[1] != "undefined" ) {
this.validation_errors_text += '<li>'+next_error[1]+'</li>'
}
})
popupErrorMessage(error.response.data.message)
});
},
With it I show message both on success and failure as I need but I see big disadvantage with it
as I use livewire and I would like to use livewire here, if that is possible...
Hope I explained what I want clearly...
Thanks!
With Alpine.js and axios you could do something like this, note that I'm not sure whether or not this_frontend_home_url, this_vote_id and this_csrf_token will be defined.
<div x-data="quiz()">
<div>
<div class="table-responsive">
<table class="table text-primary">
#foreach($quizQualityOptions as $key=>$next_quiz_quality_option)
<tr>
<td>
<input x-model="selected_quiz" class="" type="radio" name="quiz_quality_radio"
id="quiz_quality_radio_{{ $next_quiz_quality_option }}" value="{{ $key }}">
<label class="col-form-label"
for="quiz_quality_radio_{{ $next_quiz_quality_option }}">{{ $next_quiz_quality_option }}</label>
</td>
</tr>
#endforeach
</table>
</div>
<div class="row p-3">
<a class="btn btn-primary a_link" #click="submitQuizQuality()">Rate !</a>
</div>
</div>
</div>
<script>
function quiz() {
return {
selected_quiz: null,
submitQuizQuality() {
const url = this_frontend_home_url + "/make-quiz-quality";
axios.post(url, {
quiz_quality_id: this.selected_quiz,
vote_id: this_vote_id, // no idea where this is coming from,
_token: this_csrf_token // no idea where this is coming from
}).then(() => {
// reset "checked" state
this.selected_quiz = null;
frontendVote.showQuizQualityResults();
popupAlert("Thank you for rating ! Your rate was added!", 'success');
}).catch(error => {
// reset "checked" state
this.selected_quiz = null;
if (error && error.response) {
popupAlert(error.response.message, 'danger')
}
});
}
}
}
</script>

I'm using Angular 1.6 with django, if i refresh a page django serves the html file but controllers are not loading

I'm sharing my main.js, controller.js, home_page.html, index.html file below. Please some one help me fix the issue which is, when i was in the home page ('127.0.0.1:8000/home/') and try to refresh the page im getting only the html page and all the contents in the rootScope or scope disappears. But this is usual but i don't know how to again gain the data from the controller.
main.js
$stateProvider
.state("base", {
url : "/",
views : {
"main-content" : {
controller : "baseController",
template : "<div ui-view='content'></div>"
}
}
})
.state("base.login", {
url : "account/login/",
views : {
"content" : {
controller : "loginController",
templateUrl : "/account/login/"
}
}
})
.state("base.register", {
url : "account/register/",
views : {
"content" : {
controller : "registerController",
templateUrl : "/account/register/"
}
}
})
.state("base.home", {
url : "home/",
views : {
"content" : {
controller : "homeController",
templateUrl : "/home/"
}
}
})
.state("base.changepassword", {
url : "settings/",
views : {
"content" : {
controller : "changePasswordController",
templateUrl : "/settings/"
}
}
})
controller.js
myApp.controller('homeController', ['$http', '$scope', '$window', '$rootScope', function($http, $scope, $window, $rootScope) {
// debugger;
if (! $rootScope.currentUser.first_name) {
var search_payload = {
"email": $rootScope.currentUser.email
};
$http({
url: '/api/current/user/',
method: "POST",
data : search_payload,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
}).then(function(response) {
$rootScope.currentUser = response.data;
console.log("Current User - ", $rootScope.currentUser);
});
}
$scope.clear_cookie = function ($event) {
$event.preventDefault();
console.log("Clear Cookie");
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i];
var eqPos = cookie.indexOf("=");
var name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
document.cookie = name + "=;expires=Thu, 21 Jun 2018 12:00:00 GMT";
}
$http({
url: '/api/logout/',
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
}).then(function(response) {
console.log(response);
if (response.data.status == 'success') {
window.location = '/';
}
});
};
$scope.submit = function ($event, currentUser, isValid) {
$event.preventDefault();
if (isValid) {
var search_payload = {
"update": "true",
"username": currentUser.username,
"first_name": currentUser.first_name,
"last_name": currentUser.last_name,
"age": currentUser.age,
"email": currentUser.email,
"mobile": currentUser.mobile,
"address": currentUser.address
};
console.log(search_payload);
$http({
url: '/api/current/user/',
method: "POST",
data : search_payload,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
}).then(function(response) {
$rootScope.currentUser = response.data;
console.log("Current User - ", $rootScope.currentUser);
if(response.data.status == 'success') {
swal({
title: "Success",
text: "Successfully updated your profile.",
icon: "success",
button: "Okay!",
});
} else {
swal({
title: "Failed",
text: "Something went wrong sorry please try again after sometime.",
icon: "error",
button: "Okay!",
});
}
});
} else {
swal({
title: "Failed",
text: "Please correct the errors and submit the form.",
icon: "error",
button: "Okay!",
});
}
}
}]);
home_page.html
<div class="login_form_class" ng-if="currentUser.username">
<form name="updateForm">
{% csrf_token %}
<div name="login_form" class="login_form col-md-4">
<div class="header">PROFILE</div>
<hr/>
<label class="input_label col-md-5" for="username">USERNAME</label>
<input class="input_box col-md-7" type="text" id="username" name="username" ng-init="username='{$ currentUser.username $}'" ng-model="currentUser.username" placeholder="{$ currentUser.username $}" value="{$ currentUser.username $}" pattern="[a-zA-Z0-9]+"/>
<div role="alert">
<span class="error error_msg pull-right" ng-show="updateForm.username.$error.pattern">Must contain only alphanumeric Special characters are not allowed</span>
</div>
<br/>
<label class="input_label col-md-5" for="first_name">FIRST NAME</label>
<input class="input_box col-md-7" type="text" id="first_name" name="first_name" ng-init="first_name='{$ currentUser.first_name $}'" ng-model="currentUser.first_name" placeholder="{$ currentUser.first_name $}" value="{$ currentUser.first_name $}" pattern="[A-Za-z]+"/>
<div role="alert">
<span class="error error_msg pull-right" ng-show="updateForm.first_name.$error.pattern">Must contain only alphabets</span>
</div>
<br/>
<label class="input_label col-md-5" for="last_name">LAST NAME</label>
<input class="input_box col-md-7" type="text" id="last_name" name="last_name" ng-init="last_name='{$ currentUser.last_name $}'" ng-model="currentUser.last_name" placeholder="{$ currentUser.last_name $}" value="{$ currentUser.last_name $}" pattern="[a-zA-Z]+"/>
<div role="alert">
<span class="error error_msg pull-right" ng-show="updateForm.last_name.$error.pattern">Must contain only alphabets</span>
</div>
<br/>
<label class="input_label col-md-5" for="age">AGE</label>
<input class="input_box col-md-7" type="text" id="age" name="age" ng-init="age='{$ currentUser.age $}'" ng-model="currentUser.age" placeholder="{$ currentUser.age $}" value="{$ currentUser.age $}" ng-minlength="2" ng-maxlength="2" pattern="[0-9]{2}"/>
<div role="alert">
<span class="error error_msg pull-right" ng-show="updateForm.age.$error.minlength">Must have a number</span>
<span class="error error_msg pull-right" ng-show="updateForm.age.$error.maxlength">Must be two digit</span>
<span class="error error_msg pull-right" ng-show="updateForm.age.$error.pattern">Must be an integer</span>
</div>
<br/>
<label class="input_label col-md-5" for="email">EMAIL</label>
<input class="input_box col-md-7" type="email" id="email" ng-init="email='{$ currentUser.email $}'" ng-model="currentUser.email" placeholder="{$ currentUser.email $}" value="{$ currentUser.email $}" disabled/>
<br/>
<label class="input_label col-md-5" for="mobile">MOBILE</label>
<input class="input_box col-md-7" type="text" id="mobile" name="mobile" ng-init="mobile='{$ currentUser.mobile $}'" ng-model="currentUser.mobile" placeholder="{$ currentUser.mobile $}" value="{$ currentUser.mobile $}" ng-minlength="10" ng-maxlength="10" pattern="[0-9]{10}" />
<div role="alert">
<span class="error error_msg pull-right" ng-show="updateForm.mobile.$error.minlength">Must contain 10 digits</span>
<span class="error error_msg pull-right" ng-show="updateForm.mobile.$error.maxlength">Must contain only 10 digits</span>
<span class="error error_msg pull-right" ng-show="updateForm.mobile.$error.pattern">Must contain only numbers</span>
</div>
<br/>
<label class="input_label col-md-5" for="address">ADDRESS</label>
<textarea class="input_box col-md-7" id="address" ng-init="address='{$ currentUser.address $}'" ng-model="currentUser.address" placeholder="{$ currentUser.address $}">{$ currentUser.address $}</textarea>
<br/>
<button type="submit" class="btn btn-info update_btn" ng-click="submit($event, currentUser, updateForm.$valid)">SAVE CHANGES</button>
</div>
</form>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<script>
var cookie = document.cookie.split('=')[1];
var search_payload = {
"email": cookie.split(';')[0]
};
var promise = $.ajax({
url: '/api/current/user/',
method: 'POST',
data: search_payload
});
promise.done(function(response) {
// $rootScope.currentUser = response;
console.log(response);
});
</script>
</div>
index.html
<!DOCTYPE html>
<html ng-app="myApp">
<body>
<div class="content_panel" ui-view="main-content"></div>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular-animate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular-aria.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular-messages.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.1.6/angular-material.min.js"></script>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://cdn.jsdelivr.net/npm/js-cookie#2/src/js.cookie.min.js"></script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/1.0.18/angular-ui-router.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular-route.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular-resource.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular-sanitize.min.js"></script>
<script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
<script>
var csrftoken = Cookies.get('csrftoken');
var myApp = angular.module('myApp', ['ngRoute', 'ngResource', 'ngMaterial', 'ngMessages', 'ngSanitize', 'ui.router']);
</script>
<script src="{% static 'main.js' %}"></script>
<script src="{% static 'controllers.js' %}"></script>
</html>

Install Opencart store in subdirectory with a Login in the root folder

hope all is well. I am trying to install Opencart (2.0.3) into a subdirectory (Ie. www.website.com/shop/) I want the root URL www.website.com to go to a Login page, where once a user logs in. It will redirect them to the /shop/ portion of the site and allow them to continue their business. I was wondering what the easiest way was to accomplish this. Would I install everything in the root folder, and then modify the .htaccess file along with the config files? Then how would i Make the login files work in the root folder? I tried installing everything first into the subdirectory /shop/... but then I get issues trying to figure out how to get files in the root folder to work.
Thanks in advance!
Yes, need to work with ajax functionality as below. In the index.php insert the following code and replace URL_WITH_SHOP with your urlshop. Then I have taken "shop" as sub-folder installation if it is different then replace "shop" with your sub-folder name:
<script src="shop/catalog/view/javascript/jquery/jquery-2.1.1.min.js" type="text/javascript"></script>
<script src="shop/catalog/view/javascript/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<div class="container">
<div class="row">
<div id="content" class="col-sm-12 ">
<div class="row ">
<div class="col-sm-6" style="margin: 0px auto; float: none;">
<div class="well">
<h2>Returning Customer</h2>
<p><strong>I am a returning customer</strong></p>
<form method="post" enctype="multipart/form-data">
<div class="error"></div>
<div class="form-group">
<label class="control-label" for="input-email">E-Mail Address</label>
<input type="text" name="email" value="" placeholder="E-Mail Address" id="input-email" class="form-control" />
</div>
<div class="form-group">
<label class="control-label" for="input-password">Password</label>
<input type="password" name="password" value="" placeholder="Password" id="input-password" class="form-control" />
</div>
<button type="button" id="button-cart" data-loading-text="Checking login" class="btn btn-primary ">Login</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript"><!--
$('#button-cart').on('click', function() {
$.ajax({
url: 'shop/index.php?route=account/loginajax',
type: 'post',
data: $('input[type=\'text\'], input[type=\'password\']'),
dataType: 'json',
beforeSend: function() {
$('#button-cart').button('loading');
},
complete: function() {
$('#button-cart').button('reset');
},
success: function(json) {
$('.alert, .text-danger').remove();
$('.form-group').removeClass('has-error');
if (json['error']) {
$('.error').after('<div class="alert alert-danger has-error">' + json['error'] + '</div>');
}
if (json['success']) {
$('.error').after('<div class="alert alert-success">' + json['success'] + '</div>');
window.location = "URL_WITH_SHOP";
}
}
});
});
//--></script>
Above is the presentation layer, now let's make the logical layer, go to shop/catalog/controller/account and create loginajax.php and paste following code:
<?php
class ControllerAccountLoginAjax extends Controller
{
private $error = array();
public function index()
{
$this->load->model('account/customer');
$json = array();
// Login override for admin users
if (!empty($this->request->get['token'])) {
$this->event->trigger('pre.customer.login');
$this->customer->logout();
$this->cart->clear();
unset($this->session->data['wishlist']);
unset($this->session->data['payment_address']);
unset($this->session->data['payment_method']);
unset($this->session->data['payment_methods']);
unset($this->session->data['shipping_address']);
unset($this->session->data['shipping_method']);
unset($this->session->data['shipping_methods']);
unset($this->session->data['comment']);
unset($this->session->data['order_id']);
unset($this->session->data['coupon']);
unset($this->session->data['reward']);
unset($this->session->data['voucher']);
unset($this->session->data['vouchers']);
$customer_info = $this->model_account_customer->getCustomerByToken($this->request->get['token']);
if ($customer_info && $this->customer->login($customer_info['email'], '', true)) {
// Default Addresses
$this->load->model('account/address');
if ($this->config->get('config_tax_customer') == 'payment') {
$this->session->data['payment_address'] = $this->model_account_address->getAddress($this->customer->getAddressId());
}
if ($this->config->get('config_tax_customer') == 'shipping') {
$this->session->data['shipping_address'] = $this->model_account_address->getAddress($this->customer->getAddressId());
}
$this->event->trigger('post.customer.login');
$this->response->redirect($this->url->link('account/account', '', 'SSL'));
}
}
if ($this->customer->isLogged()) {
$this->response->redirect($this->url->link('account/account', '', 'SSL'));
}
if (!$json) {
$this->load->language('account/login');
$this->document->setTitle($this->language->get('heading_title'));
if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
$json['success'] = "Successfully logging in! ";
unset($this->session->data['guest']);
// Default Shipping Address
$this->load->model('account/address');
if ($this->config->get('config_tax_customer') == 'payment') {
$this->session->data['payment_address'] = $this->model_account_address->getAddress($this->customer->getAddressId());
}
if ($this->config->get('config_tax_customer') == 'shipping') {
$this->session->data['shipping_address'] = $this->model_account_address->getAddress($this->customer->getAddressId());
}
// Add to activity log
$this->load->model('account/activity');
$activity_data = array(
'customer_id' => $this->customer->getId(),
'name' => $this->customer->getFirstName() . ' ' . $this->customer->getLastName()
);
$this->model_account_activity->addActivity('login', $activity_data);
}
else{
$json['error'] = $this->language->get('error_login');
}
$data['breadcrumbs'] = array();
$data['breadcrumbs'][] = array(
'text' => $this->language->get('text_home'),
'href' => $this->url->link('common/home')
);
$data['breadcrumbs'][] = array(
'text' => $this->language->get('text_account'),
'href' => $this->url->link('account/account', '', 'SSL')
);
$data['breadcrumbs'][] = array(
'text' => $this->language->get('text_login'),
'href' => $this->url->link('account/login', '', 'SSL')
);
$data['heading_title'] = $this->language->get('heading_title');
$data['text_new_customer'] = $this->language->get('text_new_customer');
$data['text_register'] = $this->language->get('text_register');
$data['text_register_account'] = $this->language->get('text_register_account');
$data['text_returning_customer'] = $this->language->get('text_returning_customer');
$data['text_i_am_returning_customer'] = $this->language->get('text_i_am_returning_customer');
$data['text_forgotten'] = $this->language->get('text_forgotten');
$data['entry_email'] = $this->language->get('entry_email');
$data['entry_password'] = $this->language->get('entry_password');
$data['button_continue'] = $this->language->get('button_continue');
$data['button_login'] = $this->language->get('button_login');
if (isset($this->error['warning'])) {
$data['error_warning'] = $this->error['warning'];
} else {
$data['error_warning'] = '';
}
$data['action'] = $this->url->link('account/login', '', 'SSL');
$data['register'] = $this->url->link('account/register', '', 'SSL');
$data['forgotten'] = $this->url->link('account/forgotten', '', 'SSL');
if (isset($this->session->data['success'])) {
$data['success'] = $this->session->data['success'];
unset($this->session->data['success']);
} else {
$data['success'] = '';
}
if (isset($this->request->post['email'])) {
$data['email'] = $this->request->post['email'];
} else {
$data['email'] = '';
}
if (isset($this->request->post['password'])) {
$data['password'] = $this->request->post['password'];
} else {
$data['password'] = '';
}
} else {
$json['error'] = $this->language->get('error_login');
}
$this->response->addHeader('Content-Type: application/json');
$this->response->setOutput(json_encode($json));
}
protected function validate() {
$this->event->trigger('pre.customer.login');
// Check how many login attempts have been made.
$login_info = $this->model_account_customer->getLoginAttempts($this->request->post['email']);
if ($login_info && ($login_info['total'] >= $this->config->get('config_login_attempts')) && strtotime('-1 hour') < strtotime($login_info['date_modified'])) {
$this->error['warning'] = $this->language->get('error_attempts');
}
// Check if customer has been approved.
$customer_info = $this->model_account_customer->getCustomerByEmail($this->request->post['email']);
if ($customer_info && !$customer_info['approved']) {
$this->error['warning'] = $this->language->get('error_approved');
}
if (!$this->error) {
if (!$this->customer->login($this->request->post['email'], $this->request->post['password'])) {
$this->error['warning'] = $this->language->get('error_login');
$this->model_account_customer->addLoginAttempt($this->request->post['email']);
} else {
$this->model_account_customer->deleteLoginAttempts($this->request->post['email']);
$this->event->trigger('post.customer.login');
}
}
return !$this->error;
}
}
This will help you.
Download files and folders for above codes
Hope it also help you
If you just want the login page then here is the tricks, create index.php or index.html at root folder then paste the following code and change URL_WITH_SHOP in the code with your url with shop like "http://www.example.com/shop" :
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<div class="container">
<div class="row">
<div id="content" class="col-sm-12 ">
<div class="row ">
<div class="col-sm-6" style="margin: 0px auto; float: none;">
<div class="well">
<h2>Returning Customer</h2>
<p><strong>I am a returning customer</strong></p>
<form action="URL_WITH_SHOP/index.php?route=account/login" method="post" enctype="multipart/form-data">
<div class="form-group">
<label class="control-label" for="input-email">E-Mail Address</label>
<input type="text" name="email" value="" placeholder="E-Mail Address" id="input-email" class="form-control" />
</div>
<div class="form-group">
<label class="control-label" for="input-password">Password</label>
<input type="password" name="password" value="" placeholder="Password" id="input-password" class="form-control" />
</div>
<input type="submit" value="Login" class="btn btn-primary" />
</form>
</div>
</div>
</div>
</div>
</div>
</div>
The issue will be if the customer enters wrong username and password then it redirects to the actual login page.

Angular refresh list after post using Django-Rest-Framework

I've been looking on the forum how to update a list of items after the post using a server. The available examples show how to update the list but in memory.
This is my current code:
$http.get('job/').
success(function(data, status, headers, config) {
$scope.jobs = data;
}).
error(function(data, status, headers, config) {
console.log('Error', status);
});
$scope.save = function(job) {
$http.post('job/', job)
.success(function(data) {
$scope.jobs.push(data); // here i trying update the list in view
}).
error(function(data, status, headers, config) {
console.log('Error', status);
});
};
// HTML List
<tr ng-repeat="job in jobs">
{% verbatim %}
<td> {{ job.name }}</td>
<td>{{ job.description }}</td>
{% endverbatim %}
<td><a ng-click="edit(job.id)" class="btn btn-small btn-primary">edit</a></td>
<td><a ng-click="delete(job.id)" class="btn btn-small btn-danger">delete</a></td>
</tr>
// HTML form
<div class="span6" ng-controller="JobCtrl">
<h5>Create a New Job</h5>
<form class="form-inline">
<div class="form-group block-level">
<input type="text" class="form-control" name="name" ng-model="job.name" placeholder="Name">
</div>
<div class="form-group">
<input type="text" class="form-control" name="description" ng-model="job.description" placeholder="Description">
</div>
<div class="form-group">
<button class="btn btn-default" ng-click="save(job)">Add Job</button>
</div>
</form>
</div>
Thanks!
Solved using Observer.
myApp.service('JobService', function($http) {
var observerCallbacks = [];
// register the observer's callback in our callback list.
this.registerObserverCallback = function(callback){
observerCallbacks.push(callback);
};
// function to notify our observers and call the callback they registered with
var notifyObservers = function(){
angular.forEach(observerCallbacks, function(callback){
callback();
});
};
this.list = function() {
return $http.get('job/').
success(function(data) {
return data;
});
};
this.add = function(job) {
return $http.post('job/', job).success(function(data, status, headers, config) {
notifyObservers();
});
};
});
myApp.controller('JobCtrl', function ($scope, $http, $location, JobService) {
var getList = function() {
JobService.list().then(function(jobs){
$scope.jobs = jobs.data;
});
};
JobService.registerObserverCallback(getList);
getList();
$scope.save = function(job) {
JobService.add(job).success(function(data) {
$scope.job = '';
console.log('success!');
});
};
});
Here is the working solution (without BE communication). I think your code is fine, because it's working here, but I think you miss table tag around tr and td
http://jsfiddle.net/U3pVM/5031/
<table>
<tr ng-repeat="job in jobs">
<td> {{ job.name }}</td>
<td>{{ job.description }}</td>
<td><a ng-click="edit(job.id)" class="btn btn-small btn-primary">edit</a></td>
<td><a ng-click="delete(job.id)" class="btn btn-small btn-danger">delete</a></td>
</tr>
</table>
You don't have to do any $scope.$apply, because you are already working within angular scope (ng-click and $http backend request are that)