I am trying to integrate Braintree payment gateway to Vaadin 14 which is using Polymer for its frontend.
Basically we have a custom Vaadin front-end view to load script https://js.braintreegateway.com/web/dropin/1.9.4/js/dropin.min.js :
And we call its method dropin.create as below:
import{PolymerElement}from'#polymer/polymer/polymer-element.js';
import'#polymer/iron-icon/iron-icon.js';
import{html}from'#polymer/polymer/lib/utils/html-tag.js';
import '#webcomponents/webcomponentsjs/webcomponents-loader.js';
import'#polymer/polymer/polymer-legacy.js';
import'#polymer/iron-flex-layout/iron-flex-layout.js';
import{mixinBehaviors}from'#polymer/polymer/lib/legacy/class.js';
import{Polymer}from'#polymer/polymer/lib/legacy/polymer-fn.js';
import{setTouchAction}from'#polymer/polymer/lib/utils/gestures.js';
import{afterNextRender}from'#polymer/polymer/lib/utils/render-status.js';
import'#vaadin/vaadin-text-field/vaadin-text-field.js';
import { sharedStyles } from './drop-in.js';
let dropin = require('braintree-web-drop-in');
class BrainTreeVaadin extends PolymerElement {
<vaadin-vertical-layout class="main-div-layout-boder padding5">
<form id="paymentForm" method="post" action="/checkout" class="main-screen-vert-layout-row">
<div id="containPayment" class="main-screen-vert-layout-row">
<div id="btDropin" class="main-screen-vert-layout-row"></div>
</div>
<vaadin-text-field id="nonce" value={{valueNonce}} hidden></vaadin-text-field>
<vaadin-button id="butPayment" theme="theme-button-02" class="button-row">Payment</vaadin-button>
</form>
</vaadin-vertical-layout>
createFormPayment(){
let form = this.$.paymentForm;
let butPayment = this.$.butPayment;
let btDropin = this.$.btDropin;
let textNonce = this.$.nonce;
dropin.create({
authorization: this.clientToken,
container: btDropin,
card: {
cardholderName: {
required: true
}
},
paypal: {
flow: 'vault',
currency: 'USD'
},
paypalCredit: {
flow: 'vault',
currency: 'USD'
}
}
}
}
However we get error as below image:
Reason that internally, the main script dropin.min.js includes other script https://www.paypalobjects.com/api/checkout.min.js and called other methods from this new JS.
Accessing methods in checkout.min.js got error because checkout.min.js can’t get id of html elements (here is buttons) using javascript reference style "#...".
Braintree uses JS style #element_id to pass a html div element as argument to method: braintree.dropin.create(..., container: '#bt-dropin').
Below is Braintree example code (take note on method "braintree.dropin.create", it takes '#bt-dropin' as input):
<div class="bt-drop-in-wrapper">
<div id="bt-dropin"></div>
</div>
<div th:include="fragments/homefooter :: footer"></div>
<script src="https://js.braintreegateway.com/web/dropin/1.9.4/js/dropin.min.js"></script>
<script th:inline="javascript">
/*<![CDATA[*/
var form = document.querySelector('#payment-form');
var client_token = [[${clientToken}]];
braintree.dropin.create({
authorization: client_token,
container: '#bt-dropin',
paypal: {
flow: 'vault'
}
}, function (createErr, instance) {
form.addEventListener('submit', function (event) {
event.preventDefault();
$('#errorDiv').hide();
$('#serverSideErrorDiv').hide();
instance.requestPaymentMethod(function (err, payload) {
if (err) {
console.log('Error', err);
showError(err);
return;
}
// Add the nonce to the form and submit
document.querySelector('#nonce').value = payload.nonce;
form.submit();
});
});
});
And problem that Vaadin form (view) doesn’t understand javascript style: "#bt-dropin" to reference to a div element.
How to make Vaadin view understand JS style: "#element_id" ?
Update:
this is polymer problem, not Vaadin flow problem.
Update 2:
this is braintree problem, not polymer problem :)).
This is a issue of braintree due to lacking of supporting web components.
Below is workaround solution.
Braintree Git Issue
Workaround (remove space .io on URL):
https://codepen .io/braintree/pen/VrYXYW
Related
I'm trying to hide this message but without success in vue js.
Currently im using js-cookie library
What I am actually trying to do:
html
<div v-show="closeBox()">
<span><a id="closeButton" #click="closeBox()" href="#">Close</a></span>
<p>som info message</p>
</div>
javascript:
closeBox(){
if(#CloseButton.clicked()){ // if the button is clicked (not working in vue js)
Cookies.set("cookie", "false");
var cookie = Cookies.get("cookie");
}
else
{
cookie = true
}
return cookie;
I see you are trying to hide/close the div when the span is clicked.
You need to register the event method in the same vue component.
Also, it is better to use reactive state (showBox below) to show/hide the div
watch this example code...
<div v-show="showBox">
<span><a id="closeButton" #click.prevent="closeBox" href="#">Close</a></span>
<p>som info message</p>
</div>
data() {
return: {
showBox: true
}
},
methods: {
closeBox() {
this.showBox = false;
Cookies.set("cookie", "false");
var cookie = Cookies.get("cookie");
}
else
{
cookie = true
}
return cookie;
...
You can do this with vanilla js, css and HTML.
in you'r HTMl:
<div v-show="closeBox()">
<span><a id="closeButton" #click="closeBox()" href="#">Close</a></span>
<p id="message">som info message</p>
</div>
You'r js:
closeBox(){
let closeButton = document.getElementById("closeButton")
let message = document.getElementById("message")
if(closeButton.clicked()){ // pseudo code
message.className += "hide"
// sets the cookie
document.cookie = 'cookie=test; expires=Sun, 1 Jan 2023 00:00:00 UTC; path=/'
}
}
and finally in you'r css:
.hide {
display: none;
}
Update
set cookie added in answer.
For more reference on working with cookies using vanilla js follow this link.
Here is the answer to my question. Where you can add a cookie to a close/hide button so it won't show again, unless the user deletes their browser data.
The following is achieved by using js-cookie library
<template>
<div v-show="isBoxShowing">
<a #click="closeBox">Close</a>
</div>
</template>
<script>
// here you import the cookie lib
export default {
data () {
return {
isBoxShowing: Cookies.get('cookie') !== 'false'
}
},
methods: {
closeBox () {
Cookies.set('cookie', 'false')
this.isBoxShowing = false
}
}
}
</script>
Here is my simplified template:
<div>
<img ng-click="addSubPlots($event)"
data-status="add"
src="abc.svg" />
</div>
And here is a directive that loads that template:
a.directive("trendChartToolbar", function ($templateRequest, $compile) {
return {
link: function (scope, elem, attrs) {
$templateRequest('template.html').then(function (html) {
var template = angular.element(html);
$(elem).append(template);
template = $compile(template)(scope);
});
}
};
});
My expectation was that I would be able to access template's dataset and modify the 'status' key. But when I check it the dataset is empty. I know that Angular removes 'data-' but I also dont see any 'status' anywhere. Any idea how to make something like this working? The reason I am trying this is when I click on a button I need to get button's status. Here is code I am using in the controller:
$scope.addSubPlots = function(event){
console.log(event.target.dataset.status);
}
Please share some ideas.
Thanks
I have written the following piece of code to display some contents in angular material dialog box. it works fine when i add plain text to textContent . when i add HTML its displays HTML as text. how do i bind HTML to textContent
This Works
Sample Link
$scope.Modal = function () {
$mdDialog.show(
$mdDialog.alert()
.parent(angular.element(document.querySelector('body')))
.clickOutsideToClose(true)
.textContent('sample text')
.ok('Ok')
);
}
This Doesn't Works
Sample Link
$scope.Modal = function () {
$mdDialog.show(
$mdDialog.alert()
.parent(angular.element(document.querySelector('body')))
.clickOutsideToClose(true)
.textContent('<div class="test"><p>Sample text</p></div>')
.ok('Ok')
);
}
Thanks in advance
You need to append to the template,
$mdDialog.show({
parent: angular.element(document.body),
clickOutsideToClose: true,
template: '<md-dialog md-theme="mytheme">' +
' <md-dialog-content>' +
'<div class="test"><p>Sample text</p></div>' +
' <md-button ng-click="closeDialog();">Close</md-button>' +
' </md-dialog-content>' +
'</md-dialog>',
locals: {
},
controller: DialogController
});
DEMO
You can add html in template and just add variable in displayOption. This will work.
Template Code
<script type="text/ng-template" id="confirm-dialog-answer.html">
<md-dialog aria-label="confirm-dialog">
<form>
<md-dialog-content>
<div>
<h2 class="md-title">{{displayOption.title}}</h2>
<p>{{displayOption.content}} <img src="{{displayOption.fruitimg}}"/></p>
<p>{{displayOption.comment}}</p>
</div>
</md-dialog-content>
<div class="md-actions" layout="row">
<a class="md-primary-color dialog-action-btn" ng-click="cancel()">
{{displayOption.cancel}}
</a>
<a class="md-primary-color dialog-action-btn" ng-click="ok()">
{{displayOption.ok}}
</a>
</div>
</form>
</md-dialog>
</script>
Controller Code
$mdDialog.show({
controller: 'DialogController',
templateUrl: 'confirm-dialog-answer.html',
locals: {
displayOption: {
title: "OOPS !!",
content: "You have given correct answer. You earned "+$scope.lastattemptEarnCount,
comment : "Note:- "+$scope.comment,
fruitimg : "img/fruit/"+$scope.fruitname+".png",
ok: "Ok"
}
}
}).then(function () {
alert('Ok clicked');
});
Use template instead of textContent, textContent is used for show plan text in a model. It does not render HTML code
$mdDialog.show({
controller: function ($scope) {
$scope.msg = msg ? msg : 'Loading...';
},
template: 'div class="test"><p>{{msg}}</p></div>',
parent: angular.element(document.body),
clickOutsideToClose: false,
fullscreen: false
});
You can use htmlContent instead of textContent to render HTML. Heres an excerpt from the documentation available at https://material.angularjs.org/latest/#mddialog-alert
$mdDialogPreset#htmlContent(string) - Sets the alert message as HTML.
Requires ngSanitize module to be loaded. HTML is not run through
Angular's compiler.
It seems a bit counter intuitive to use a template when you only need to inject one or two things in. To avoid using a template, you need to include 'ngSanitize' for it to work.
angular.module('myApp',['ngMaterial', 'ngSanitize'])
.controller('btnTest',function($mdDialog,$scope){
var someHTML = "<font>This is a test</font>";
$scope.showConfirm = function(ev) {
// Appending dialog to document.body to cover sidenav in docs app
var confirm = $mdDialog.confirm()
.title('Please confirm the following')
.htmlContent(someHTML)
.ariaLabel('Lucky day')
.targetEvent(ev)
.ok('Please do it!')
.cancel('Sounds like a scam');
//Switch between .htmlContent and .textContent. You will see htmlContent doesn't display dialogbox, textContent does.
$mdDialog.show(confirm).then(function() {
$scope.status = 'Saving Data';
},
function() {
$scope.status = 'You decided to keep your debt.';
});
};
})
Notice the injected HTML:
var someHTML = "<font>This is a test</font>";
I found this example here.
The latest version of Angular Material Design API has predefined function for add HTML content to the alert dialog:
an $mdDialogPreset with the chainable configuration methods:
$mdDialogPreset#title(string) - Sets the alert title.
$mdDialogPreset#textContent(string) - Sets the alert message.
$mdDialogPreset#htmlContent(string) - Sets the alert message as HTML. Requires ngSanitize module to be loaded. HTML is not run through Angular's compiler.
$mdDialogPreset#ok(string) - Sets the alert "Okay" button text.
$mdDialogPreset#theme(string) - Sets the theme of the alert dialog.
$mdDialogPreset#targetEvent(DOMClickEvent=) - A click's event object. When passed in as an option, the location of the click will be used as the starting point for the opening animation of the the dialog.
The link to the documentation: Angular MD API
I'm working with the Kendo UI MVVM and I'm trying to bind it to a self executing anonymous modular function. Long story short, it's only kind of working. The module is being updated if I inspect the page but the UI isn't. All I'm using is a short HTML file with a script tag to wire up the MVVM and an external JavaScript file to bring the module in.
HTML and JS on page
<!-- Adding information -->
<input data-bind="value: DemoField" />
<!-- Update Button -->
<button data-bind="events: { click: updateModule }">Update</button>
<!-- Trying to update this field -->
<input data-bind="value: module.Model.Demo.DemoField" />
<!-- Observable -->
<script type="text/javascript">
var model = kendo.observable(
{
DemoField: "",
updateModule: function () {
module.updateInformation({
demoField: this.get("DemoField")
)};
}
},
module
);
kendo.bind($("#form"), invoiceModel);
</script>
Module JS file
var module = (function () {
// private information
var _demo = (function () {
var _demoObject = {},
_demoField = null;
Object.defineProperty(_demoObject, "DemoField", {
get: function () { return _demoField; }
});
_demoObject.updateInformation = function (updatedObject) {
if (updatedObject.demoField) {
_demoField = updatedObject.demoField;
}
};
return _demoObject;
}());
var _model = { Demo: _demo };
// public information
return {
Model: _model
updateInformation: _demo.updateInformation
}
}());
If I enter "module.Model.Fields.FieldName" in the inspector, I see the information I'm expecting, but the UI just isn't playing nice. I've been to many pages on Telerik's website and I've consulted Google, but typically my searches yield little to no results and the results I do get are less than useful.
My thoughts are that kendo won't observe a module like it would a regular property, but then again I haven't worked with any kind of JS module before and I'm very new to MVVM.
Your thoughts are correct. Kendo will not observe a nested property, even if it is not nested you always have to use "get" and "set" words, for reference in Angular you don't need to do that.
So your code should look something like that:
<input data-bind="value: DemoField" />
<!-- Update Button -->
<button data-bind="events: { click: updateModule }">Update</button>
<!-- Trying to update this field -->
<input data-bind="value: updatedValue" />
And the view Model:
var model = kendo.observable({
DemoField: "",
updateModule: function () {
module.updateInformation({
demoField: this.get("DemoField")
});
this.set("updatedValue", module.Model.Demo.DemoField);
},
updatedValue: "",
});
kendo.bind($("#form"), model);
Here is a link to dojo with working example:
http://dojo.telerik.com/UzUhi
The Situation
Nested within our Angular app is a directive called Page, backed by a controller, which contains a div with an ng-bind-html-unsafe attribute. This is assigned to a $scope var called 'pageContent'. This var gets assigned dynamically generated HTML from a database. When the user flips to the next page, a called to the DB is made, and the pageContent var is set to this new HTML, which gets rendered onscreen through ng-bind-html-unsafe. Here's the code:
Page directive
angular.module('myApp.directives')
.directive('myPage', function ($compile) {
return {
templateUrl: 'page.html',
restrict: 'E',
compile: function compile(element, attrs, transclude) {
// does nothing currently
return {
pre: function preLink(scope, element, attrs, controller) {
// does nothing currently
},
post: function postLink(scope, element, attrs, controller) {
// does nothing currently
}
}
}
};
});
Page directive's template ("page.html" from the templateUrl property above)
<div ng-controller="PageCtrl" >
...
<!-- dynamic page content written into the div below -->
<div ng-bind-html-unsafe="pageContent" >
...
</div>
Page controller
angular.module('myApp')
.controller('PageCtrl', function ($scope) {
$scope.pageContent = '';
$scope.$on( "receivedPageContent", function(event, args) {
console.log( 'new page content received after DB call' );
$scope.pageContent = args.htmlStrFromDB;
});
});
That works. We see the page's HTML from the DB rendered nicely in the browser. When the user flips to the next page, we see the next page's content, and so on. So far so good.
The Problem
The problem here is that we want to have interactive content inside of a page's content. For instance, the HTML may contain a thumbnail image where, when the user clicks on it, Angular should do something awesome, such as displaying a pop-up modal window. I've placed Angular method calls (ng-click) in the HTML strings in our database, but of course Angular isn't going to recognize either method calls or directives unless it somehow parses the HTML string, recognizes them and compiles them.
In our DB
Content for Page 1:
<p>Here's a cool pic of a lion. <img src="lion.png" ng-click="doSomethingAwesone('lion', 'showImage')" > Click on him to see a large image.</p>
Content for Page 2:
<p>Here's a snake. <img src="snake.png" ng-click="doSomethingAwesone('snake', 'playSound')" >Click to make him hiss.</p>
Back in the Page controller, we then add the corresponding $scope function:
Page controller
$scope.doSomethingAwesome = function( id, action ) {
console.log( "Going to do " + action + " with "+ id );
}
I can't figure out how to call that 'doSomethingAwesome' method from within the HTML string from the DB. I realize Angular has to parse the HTML string somehow, but how? I've read vague mumblings about the $compile service, and copied and pasted some examples, but nothing works. Also, most examples show dynamic content only getting set during the linking phase of the directive. We would want Page to stay alive throughout the life of the app. It constantly receives, compiles and displays new content as the user flips through pages.
In an abstract sense, I guess you could say we are trying to dynamically nest chunks of Angular within an Angular app, and need to be able to swap them in and out.
I've read various bits of Angular documentation multiple times, as well as all sorts of blog posts, and JS Fiddled with people's code. I don't know whether I'm completely misunderstanding Angular, or just missing something simple, or maybe I'm slow. In any case, I could use some advice.
ng-bind-html-unsafe only renders the content as HTML. It doesn't bind Angular scope to the resulted DOM. You have to use $compile service for that purpose. I created this plunker to demonstrate how to use $compile to create a directive rendering dynamic HTML entered by users and binding to the controller's scope. The source is posted below.
demo.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.0.7" data-semver="1.0.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js"></script>
<script src="script.js"></script>
</head>
<body>
<h1>Compile dynamic HTML</h1>
<div ng-controller="MyController">
<textarea ng-model="html"></textarea>
<div dynamic="html"></div>
</div>
</body>
</html>
script.js
var app = angular.module('app', []);
app.directive('dynamic', function ($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.dynamic, function(html) {
ele.html(html);
$compile(ele.contents())(scope);
});
}
};
});
function MyController($scope) {
$scope.click = function(arg) {
alert('Clicked ' + arg);
}
$scope.html = '<a ng-click="click(1)" href="#">Click me</a>';
}
In angular 1.2.10 the line scope.$watch(attrs.dynamic, function(html) { was returning an invalid character error because it was trying to watch the value of attrs.dynamic which was html text.
I fixed that by fetching the attribute from the scope property
scope: { dynamic: '=dynamic'},
My example
angular.module('app')
.directive('dynamic', function ($compile) {
return {
restrict: 'A',
replace: true,
scope: { dynamic: '=dynamic'},
link: function postLink(scope, element, attrs) {
scope.$watch( 'dynamic' , function(html){
element.html(html);
$compile(element.contents())(scope);
});
}
};
});
Found in a google discussion group. Works for me.
var $injector = angular.injector(['ng', 'myApp']);
$injector.invoke(function($rootScope, $compile) {
$compile(element)($rootScope);
});
You can use
ng-bind-html https://docs.angularjs.org/api/ng/service/$sce
directive to bind html dynamically.
However you have to get the data via $sce service.
Please see the live demo at http://plnkr.co/edit/k4s3Bx
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope,$sce) {
$scope.getHtml=function(){
return $sce.trustAsHtml("<b>Hi Rupesh hi <u>dfdfdfdf</u>!</b>sdafsdfsdf<button>dfdfasdf</button>");
}
});
<body ng-controller="MainCtrl">
<span ng-bind-html="getHtml()"></span>
</body>
Try this below code for binding html through attr
.directive('dynamic', function ($compile) {
return {
restrict: 'A',
replace: true,
scope: { dynamic: '=dynamic'},
link: function postLink(scope, element, attrs) {
scope.$watch( 'attrs.dynamic' , function(html){
element.html(scope.dynamic);
$compile(element.contents())(scope);
});
}
};
});
Try this element.html(scope.dynamic);
than element.html(attr.dynamic);