AngularJs - Bind to function - javascript

I've had a simple setup displaying a variable using ng-bind-html. But now requisites have changed and I part to display comes from a collection and needs to display a pager.
So I thought about using a functionI , thinking that it would be easy, but failing miserably at it.
The expected workflow:
Via AJAX I load the content of a select field (working)
When the user selects one of the options, the function should get called to paint the content from the collection that is assigned to the selected option, including a pager to navigate within the collection (not working).
What I have so far:
{% extends 'BDAMainBundle::layout.html.twig' %}
{% block title %}Kurs-Auswahl{% endblock %}
{% block body %}
<div data-ng-controller="SingleCourseDownloadCtrl">
<div class="row">
<div class="col-xs-8">
{{ textSnippet('snippet.solo_download_selection')|raw }}
<div data-ng-show="loading">
<span class="inline-ajax-indicator"></span>
</div>
<div data-ng-hide="loading || courses.length == 0" class="input-group">
<select
class="form-control"
data-ng-change="tcAccepted = false"
data-ng-model="course"
data-ng-options="c|courseSelectionCourseLabel for c in courses"
><option value="">Bitte wählen:</option></select>
....
<div data-ng-show="course">
<div>
{% verbatim %}
{{ paged_weekly_exercises }}
{% endverbatim %}
</div>
</div>
</div>
....
{% endblock %}
and:
app.controller('SingleCourseDownloadCtrl', ['$scope', '$window', 'CourseManager', 'RemoteRouteConfig', function ($scope, $window, CourseManager, RemoteRouteConfig) {
$scope.courses = [];
$scope.loading = true;
$scope.paged_weekly_exercise;
$scope.course = [];
// load
CourseManager.getAvailableForSoloDownload().then(function (response) {
$scope.courses = response;
$scope.loading = false;
$scope.$watch('course', function(){
if(!course.length){
return false;
}
$scope.paged_weekly_exercise = $scope.course.weekly_exercises[0].content;
});
});
// starts the given course for single download
$scope.startCourse = function startCourse(course) {
$window.location = RemoteRouteConfig.course.startSoloDownload({
course: course.id
});
};
}]);
But I feel I went a totally wrong path. How should I go to accomplish my goal?
I have minimal experience with Angular, but am quite decent at Javascipt and JQuery.

Instead of adding a watcher in your controller, just use the ng-change:
<select
class="form-control"
data-ng-change="courseChanged()"
data-ng-model="course"
data-ng-options="c|courseSelectionCourseLabel for c in courses"
In your controller:
$scope.courseChanged = function() {
$scope.tcAccepted = false;
var course = $scope.course; // the used ng-model
if(!course.length){
return;
}
$scope.paged_weekly_exercise = course.weekly_exercises[0].content;
}

Related

JS always loads default view

I'm taking the CS50 course "Web Programming with Python and Javascript".
In one of the projects that need to be done, one have to implement a social network platform where users can post some texts, like and dislike them and follow other users.
There are 3 routes a user can go:
All posts,
Following and
users posts.
I've been trying to solve this with js front end. I've a function "view_posts", that takes an argument that specifies what posts should be fetched and rendered to the html page. Consider the following part of my js file:
document.addEventListener('DOMContentLoaded', function() {
username = document.getElementById('username').innerText
document.querySelector('#posts').addEventListener('click', () => view_posts('all'));
document.querySelector('#username').addEventListener('click', () => view_posts(username));
document.querySelector('#following').addEventListener('click', () => view_posts('followers'));
document.querySelector('#post-form').style.display = "none";
view_posts('all')
});
Fetching the data is basically working, but js is always displaying the default view "view_posts('all')". Meaning, even if I click on the element whose id is #post, calling the very same function including the same argument, the function is called without displaying anything, before it calls the default view again (this time it will display the data correct).
function view_posts(selector) {
// document.querySelector('#post-form').style.display = "none";
if (selector === 'all') {
document.querySelector('#header').innerText = "All Posts";
document.querySelector('#post-form').style.display = "block";
} else if (selector == 'following') {
document.querySelector('#header').innerText = "Following";
} else {
document.querySelector('#header').innerText = selector;
}
fetch(`/network/${selector}`)
// .then(response => response.text())
// .then(posts => console.log(posts))
.then(response => response.json())
.then(posts => {
posts.forEach((element) => {
var item = document.createElement('div');
item.className = "card";
item.innerHTML = `<div class="card-body" id="item-${element.id}">
${element.author} | ${element.timestamp}
<br>
${element.body}
</div>`;
document.querySelector('#post-view').append(item);
// document.getElementById('item-${element.id}').addEventListener('click', () => view_posts(element.author))
});
});
// .catch(console.log.bind(console));
};
EDIT: here is my index.html
{% extends "network/layout.html" %}
{% load static %}
{% block body %}
<div id="post-view">
<h2 id="header" style="margin:10px"></h2>
<div id=post-form>
<h4>New Post</h4>
<form>
<textarea class="form-control" id="compose-body" placeholder="Type here..."></textarea>
<input onclick="create_post()" type="button" class="btn btn-primary" value="Post"/>
</form>
</div>
</div>
{% endblock %}
{% block script %}
<script src="{% static 'network/index.js' %}"></script>
{% endblock %}
To not append
document.getElementById('post-view').innerHTML = posts.map(element => (
`<div class="card">
<div class="card-body" id="item-${element.id}">
${element.author} | ${element.timestamp}<br>${element.body}
</div>
</div>`).join("");

How to create Flask-Stripe Checkout and Charge for the multiple items

I am building an integration with Stripe by following the examples in its documentation, but I can't understand the part of creating a Charge for more than one product.
I was looking all over Stripe's docs and was searching for any articles / forums about the similar issue but was unable to find anything. I'd be very grateful to either some links to the articles on this matter or any tips to help me to understand how to solve it.
Here's a server side code:
```python
#app.route("/checkout", methods=["GET", "POST"])
def checkout():
if request.method == "POST":
# Process a JSON string with a checkout information:
# { item_id: item_quantity, ... }
# Build the SQL query based on it
items = {}
shopping_cart = request.form["cart_checkout"]
shopping_cart = shopping_cart.lstrip("{")
shopping_cart = shopping_cart.rstrip("}")
shopping_cart = shopping_cart.split(",")
sqlQuery = "SELECT * FROM Items WHERE item_id IN ("
for KeyValPair in shopping_cart:
Key = KeyValPair.split(":")[0]
Key = Key.strip('"')
sqlQuery = sqlQuery + Key + ","
Value = KeyValPair.split(":")[1]
items[Key] = Value
sqlQuery = sqlQuery.rstrip(",")
sqlQuery = sqlQuery + ") ORDER BY item_id ASC"
cart_items = sql_select(sqlQuery)
# Add a column about the quantity of items
for item in cart_items:
item["quantity"] = items[item["item_id"]]
# Build a Stripe checkout list
line_items_list = []
for item in cart_items:
line_item = {}
line_item["name"] = item["item_name"]
line_item["description"] = item["item_description"]
line_item["amount"] = item["price"]
line_item["currency"] = "usd"
line_item["quantity"] = item["quantity"]
line_items_list.append(dict(line_item))
stripe_session = stripe.checkout.Session.create(
submit_type="pay",
payment_method_types=["card"],
line_items=line_items_list,
success_url='https://example.com/success',
cancel_url='https://example.com/cancel',
)
return render_template("checkout.html",
stripe_id=stripe_session.id,
stripe_pk=stripe_keys["PUBLIC_KEY"])
return redirect("/")
```
And here's a part of HTML template:
```html
<form action="/checkout" method="post" id="form_checkout" onsubmit="return cart_info()"
...
<input type="hidden" name="cart_checkout" id="checkout_info" value="{{ cart_checkout }}">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="{{ stripe_pk }}"
data-name="Company Name"
data-image="https://stripe.com/img/documentation/checkout/marketplace.png"
data-description="A description of the product or service being purchased"
data-amount="999"
data-shipping-address="true"
data-zip-code="true"
data-allow-remember-me="true"
data-panel-label="Pay"
data-label="Checkout"
data-locale="auto">
</script>
</form>
```
If I do a simple example of Charge from Stripe docs, like this:
```python
#app.route('/charge', methods=['POST'])
def charge():
# Amount in cents
amount = 500
customer = stripe.Customer.create(
email='customer#example.com',
source=request.form['stripeToken']
)
charge = stripe.Charge.create(
customer=customer.id,
amount=amount,
currency='usd',
description='Flask Charge'
)
return render_template('charge.html', amount=amount)
```
Then I can create without any issues a successful test Charge, it displays with the success label in my Stripe's dashboard. If I use stripe.checkout.Session.create, Stripe dashboard properly creates an incomplete record about my Checkout session with the selected list of items, but I've no idea how to proceed from there to finalise the Charge for them.
As often happens, when I start asking questions, I eventually find the answers on my own, lol. I've had a "checkout.html" template but it didn't work, and no errors were displaying, so I assumed that I was missing some more code required for it all to work.
As it happened, all I was missing, was "" in a line of code. Here's a working Checkout session with the addition of a bit of JavaScript:
{% extends "layout.html" %}
{% block title %}Checkout{% endblock %}
{% block head %}
<script src="https://js.stripe.com/v3/"></script>
{% endblock %}
{% block main %}
<!-- Main content -->
<div class="wrapper main-content">
{% with messages = get_flashed_messages(with_categories=true) %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endwith %}
<h2 class="section-header">Checkout</h2>
<p id="result_msg"></p>
<button onclick="checkout()">Checkout</button>
</div>
<script type="text/javascript">
function checkout() {
var stripe = Stripe("{{ stripe_pk }}");
stripe.redirectToCheckout({
// Make the id field from the Checkout Session creation API response
// available to this file, so you can provide it as parameter here
// instead of the {{CHECKOUT_SESSION_ID}} placeholder.
sessionId: "{{CHECKOUT_SESSION_ID}}"
}).then(function (result) {
// If `redirectToCheckout` fails due to a browser or network
// error, display the localized error message to your customer
// using `result.error.message`.
document.getElementById("result_msg").innerHTML = result.error.message;
});
}
</script>
{% endblock %}

Append / add, text and function to a div's content

I'm currently working on a AngularJS based application. I'm using the 'pascalprecht.translate' library to create a multi-language application. For more information on that please see this link. While creating my application i've created a dynamic switch of page titles. this switch reads the route name from the $routeProvider and adds a corresponding title to the <header>. See the example below:
// Pages configuration
myApp.run(['$rootScope', '$route', function($rootScope, $route) {
$rootScope.$on('$routeChangeSuccess', function() {
document.title = $route.current.title;
var title = document.getElementsByTagName("title")[0].innerHTML;
var leftSvg = document.getElementById('leftsvg');
var rightSvg = document.getElementById('rightsvg');
// start responsive elements
$("footer").removeClass("hidden-xl-down");
$("#header").removeClass("hidden-xl-down");
$("#leftheaderblock").removeAttr('class');
$("#rightheaderblock").removeAttr('class');
$(".activefooter").removeAttr('class');
$("#homepageback").removeClass("hidden-xl-down");
$("#rightsvg").addClass("hidden-xl-down");
$("#leftsvg").addClass("hidden-xl-down");
$("body").removeAttr('class');
$("body").addClass(title);
if (title === 'login') {
$("footer").addClass("hidden-xl-down");
$("#header").addClass("hidden-xl-down")
}
else if (title === 'productpage') {
$("#homepageback").addClass("hidden-xl-down");
$("#homesvg").addClass("activefooter");
$("#leftheaderblock").addClass("col-55");
$("#rightheaderblock").addClass("col-45");
$('#leftheadertext').text('Alle producten');
$('#rightheadertext').text('Bestelling');
$("#rightsvg").removeClass("hidden-xl-down");
$("#leftsvg").removeClass("hidden-xl-down")
}
});
}]);
The html
The <html> is some simple div's in an index.html file. Below you'll see the example of the rightheaderblock.
<div id="rightheaderblock">
<div class="bc-f3f3f3 justify-content-center toptext d-flex align-items-center headerblock">
<span id="rightheadertext"></span>
</div>
</div>
The question
Using 'pascalprecht.translate' gives access to the possibility of creating a multiple language application with your own language library's in JSON format. See the example below:
var myApp = angular.module('myApp',['ngRoute','pascalprecht.translate','ngSanitize']);
var mypagetitle = document.getElementsByTagName("title")[0];
myApp.config(function ($routeProvider, $locationProvider, $translateProvider) {
$locationProvider.hashPrefix('');
$routeProvider
.when('/', {
templateUrl: 'views/login_view.html',
title: 'login'
})
.when('/productpage', {
templateUrl: 'views/productpage_view.html',
title: 'productpage'
})
.when('/payorder', {
templateUrl: 'views/payorder_view.html',
title: 'payorder'
});
$translateProvider
.translations('en', {
'Opslaan': 'Save',
'Alle producten': 'All products',
'Bestelling': 'Order',
})
.translations('nl', {
'Opslaan': 'Opslaan',
'Alle producten': 'Alle producten',
'Bestelling': 'Bestelling',
});
$translateProvider.useSanitizeValueStrategy('sanitize');
$translateProvider.preferredLanguage('nl'); // standaard taal bij openen
// configures staticFilesLoader
// configures staticFilesLoader
});
myApp.controller('mainCtrl', function($http, $scope) {
$scope.text = "hi";
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="main" ng-controller="mainCtrl">
<div id="rightheaderblock">
<div class="bc-f3f3f3 justify-content-center toptext d-flex align-items-center headerblock">
<span id="rightheadertext">
{{text}}
</span>
</div>
</div>
<div class="bodyTests">
<p> {{ "Bestelling" | translate }}
</div>
Above you're able to look at a simple example of how the 'tranlate' function works. by adding | translate to a string and defining that string to a .translations() section i'm able to create the translations.
Looking back at the // Pages configuration above you're able to see that i'm using if/else statements to check for the page title and add .text() and classes to a div by current page title.
The issue i'm having is passing the | translate section of e.g. {{"bestelling" | translate}} from the if/else statement to the view. So passing the following line:
$('#rightheadertext').text('Bestelling');
To the page isn't a problem. But adding
$('#rightheadertext').text('{{ Bestelling | translate }}');
gives me the full string in the view:
{{ Bestelling | translate }}
i've tried multiple different functions like .val() and append(). But neither seems to work. The result i would like is to add the following structure to the view:
{{ "string" | translate // call }}
If you have any questions or remarks on my question please let them know in the comments below.
As always, thanks in advance!
How about this...
<p> {{ $root.text1 | translate }}</p>
Then in your code, in the if/else...
if (...) {
$rootScope.text1 = 'Bestelling';
} else {
$rootScope.text1 = 'Opslaan';
}

Scope not updating changes in the model

I have an expandable form that generates an object with two attributes, a title and description. This object successfully submits to my database as a json object. I'm currently using an Angular (1.3.2) front end that interacts with Tastypie as the interface layer with my Django (1.7) backend. The problem is that I never observe updates to my home page after adding a new object to the db. I need to refresh the page for the object to appear which is not ideal.
home.html
<div class="protocol-list-container">
<div ng-app="protocolApp"
id="protocol-list">
<div class="new-protocol-container" ng-controller="protoCtrl">
<h4>Add New Protocol</h4>
<button type="button"
ng-click="toggle()"
id="id_new">
<span class="glyphicon glyphicon-plus"></span>
</button>
<div ng-hide="visible" class="protocol-new">
<form name="newProtocolForm" novalidate>
<input type="text"
id="id_new_title"
placeholder="Title"
ng-model="protocol.title"
required /><br>
<input type="text"
id="id_new_desc"
placeholder="Description"
ng-model="protocol.description"
required /><br><br>
<input type="submit"
id="id_submit_new_protocol"
value="New Protocol"
ng-click="submit(protocol)"
ng-disabled="newProtocolForm.$invalid">
</form>
{% verbatim %}
<pre>form = {{ protocol | json}}</pre>
{% endverbatim %}
</div>
<div class="protocol">
<h4>My Protocols</h4>
<li ng-repeat="protocol in protocols">
{% verbatim %}
<div><span ng-bind="protocol.title"></span></div>
{% endverbatim %}
<div> - <span ng-bind="protocol.description"></span>
</li>
<br>
</div>
</div>
</div>
app.js
angular.module('protocolApp', [])
.factory('protocolFactory', ['$http', function($http) {
var urlBase = '/api/v1/protocol/';
var protocolFactory = {};
protocolFactory.getProtocols = function() {
console.log('getProtocols called');
return $http.get(urlBase);
};
protocolFactory.addProtocol = function(protocol) {
console.log('addProtocol called');
return $http.post(urlBase, protocol);
};
return protocolFactory;
}])
.controller('protoCtrl', ['$scope', 'protocolFactory',
function ($scope, protocolFactory) {
$scope.visible = true;
var self = this;
getProtocols();
function getProtocols() {
protocolFactory.getProtocols()
.success(function(data) {
$scope.protocols = data;
})
.error(function(error) {
console.log('error retrieving protocols');
});
}
$scope.toggle = function() {
$scope.visible = !$scope.visible;
var self = this;
var protocol = {};
self.submit = function() {
var protocol = {title: self.title, description: self.description};
console.log('clicked submit with ', self.protocol);
protocolFactory.addProtocol(self.protocol)
.success(function(response) {
console.log('protocol added');
$scope.protocol = null;
})
.error(function(error) {
console.log('post to api failed');
});
// gives the behavior I want, but ultimately crashes chrome
// $scope.$watch('protocols', function(newVal, oldVal) {
// protocolFactory.getProtocols()
// .success(function(data) {
// $scope.protocols = data;
// console.log('watcher data', data);
// });
// }, true);
};
};
}]);
I've done some testing with a $scope.$watch function (commented out), but this either shows the new object and never stops (true removed) or does not update (but tells me that there is an extra object in the data based on the console statement) (true present).
Any help would be appreciated.
When the database gets updated, how does the front end know that it should get the latest data unless we tell it to ? You don't have some kind of sockets between the server and front end, looking for events and making the front end to get the latest data...
So, When you post the data to backend and database got updated, make a call to getProtocols(), in the success callback of submit.
In your case of using $watch(), you are repeatedly getting the protocols from backend, which updated the scope variable, which again fired the callback repeatedly and browser crashed.

How can I take one large object, use a sub-object of that as the data for multiple instances of the same controller, and keep them both in sync?

Apologies for the ambiguity of the question :-P
I have a single JavaScript object which contains all my data. And I have a controller which I will use multiple times throughout the application. So the controllers are all working on the same data, the data is added to the application using a service.
To support a read-only/edit mode interaction, I make two copies of the original data source in the service. When the user manipulates data, they are manipulating the edit mode data source. They can then press a button to save the data to the edit mode data source to the read-only mode data source (using angular.copy).
I would also like to have the instances of the controller work on just part of the data source rather than the whole thing.
The behavior I am seeing is angularjs is able to update the parts, keeping them both in sync; but when I press the button to perform the angular.copy, it seems to reassign the variable rather than adjust the value of where it was pointing.
Code below and here's a jsfiddle http://jsfiddle.net/q5ca5quq/1/
<html ng-app='app'>
<body>
<div ng-controller='a_controller as ctrl_1'>
read_mode_inner = {{ ctrl_1.read_mode_inner }}<br>
edit_mode_inner = {{ ctrl_1.edit_mode_inner }}<br>
<br>
<input ng-model='ctrl_1.edit_mode_inner[0].a'>
</div>
<br><br><br>
<div ng-controller='a_controller as ctrl_2'>
read_mode_inner = {{ ctrl_2.read_mode_inner }}<br>
edit_mode_inner = {{ ctrl_2.edit_mode_inner }}<br>
<br>
Change this and press button below <input ng-model='ctrl_2.edit_mode_inner[0].a'> <br>
<button ng-click='ctrl_2.change()'>Copy edit_mode_inner into read_mode_inner</button>
</div>
<script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.2.21/angular.min.js'></script>
<script type='text/javascript'>
angular.module('app',[])
.factory('DataService', [function() {
data = {
xx : [{'a':1}, {'b':2}, {'c':3}],
yy : [{'a':1}, {'b':2}, {'c':3}]
}
return {
read_mode : data,
edit_mode : angular.copy(data)
}
}])
.controller('a_controller', ['DataService', function(DataService) {
var self = this;
window.s = self; // For debugging
self.read_mode = DataService.read_mode;
self.edit_mode = DataService.edit_mode;
self.read_mode_inner = self.read_mode.xx;
self.edit_mode_inner = self.edit_mode.xx;
self.change = function(){
self.read_mode_inner = angular.copy(self.edit_mode_inner);
}
}]);
</script>
</body>
</html>
http://jsfiddle.net/q5ca5quq/2/
You can share data between controllers using a service, but if you want all controller instances to update every time the value in service is changed, you'll need something like $watch inside your controller:
$scope.change = function(){
DataService.read_mode.xx = angular.copy($scope.edit_mode_inner);
}
$scope.$watch(function(){return DataService}, function(dataService){
$scope.read_mode_inner = dataService.read_mode.xx;
}, true);
For this to work you need to use angular $scope instead of this/self reference. Notice how using $scope also simplified angular notation in HTML. You don't need to name controllers separately:
instead of:
<div ng-controller='a_controller as ctrl_1'>
read_mode_inner = {{ ctrl_1.read_mode_inner }}<br>
edit_mode_inner = {{ ctrl_1.edit_mode_inner }}<br>
</div>
You only have to:
<div ng-controller='a_controller'>
read_mode_inner = {{ read_mode_inner }}<br>
edit_mode_inner = {{ edit_mode_inner }}<br>
</div>
because $scope takes care of the rest.

Categories

Resources