Share To Teams Error "Could not load content for https://local.teams.office.com/sourcemaps/../unhashed-assets/launcher.js.map" - microsoft-teams

We are trying to add "Share To Teams" button to our web application which is an angular app.
but while adding script, it gives an error as
Could not load content for
https://local.teams.office.com/sourcemaps/../unhashed-assets/launcher.js.map:
Connection error: net::ERR_CONNECTION_REFUSED
And the button does not display.
<a class="teams-share-button" data-href="someurl"></a>
Script rendering function in typescript
private appendShareToTeamsScript(): void{
//Add Share to Teams script to the page
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://teams.microsoft.com/share/launcher.js";
document.body.appendChild(script);
}

To load external JavaScript on component use ngOnInit() to append the libraries.
I have tried the below code snippet and worked for me.
Add below anchor tag in home.component.html
<a class="teams-share-button" data-href="yor url" data-icon-px-size="64"></a>
add below code snippet in home.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
})
export class HomeComponent implements OnInit {
title = 'app';
ngOnInit() {
this.loadScript('https://teams.microsoft.com/share/launcher.js');
}
public loadScript(url: string) {
const body = <HTMLDivElement> document.body;
var script = document.createElement('script');
script.src = url;
body.appendChild(script);
}
}

Related

Can't use React useEffect and also build failed using Gatsby

I am building a headless eCommerce website using Nacelle, Gatsby, and Shopify plus.
My problem is that I integrated Okendo API to fetch product reviews and can't build the project.
Actually, as you know, headless eCommerce is a new technology to us, but it is mostly close to Gatsby and SSR.
I tried to go 2 ways, one is to include the script to head using gatsby-react-helmet, and another one is to call window api inside useEffect or useLayoutEffect.
1. Including the script to head tag using gatsby-plugin-react-helmet.
ProductReview.js
import React, { useEffect } from 'react';
import { Helmet } from 'react-helmet';
import transformProductId from '../../utils/transformProductId';
import { PRODUCT_REVIEW_METAFIELD_KEY, OKENDO_SUBSCRIBER_ID } from '../../constants';
const ProductReview = ({
product
}) => {
const OkendoSettings = {
filtersEnabled: true,
omitMicrodata: true,
subscriberId: OKENDO_SUBSCRIBER_ID,
widgetTemplateId: "default"
}
return (
<>
<Helmet>
<script type="application/javascript" src="../plugins/okendo/index.js" />
<script type="application/json" id="oke-reviews-settings">
{JSON.stringify(OkendoSettings)}
</script>
<script type="application/javascript" src="../plugins/okendo/initAPI.js" />
</Helmet>
<div
data-oke-reviews-widget
data-oke-reviews-product-id={transformProductId(product.id)}
/>
</>
);
};
export default React.memo(ProductReview);
/plugin/okendo/index.js
(function () {
function asyncLoad() {
var urls = ['https:\/\/d3hw6dc1ow8pp2.cloudfront.net\/reviewsWidget.min.js?shop=example.myshopify.com'];
for (var i = 0; i < urls.length; i++) {
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = urls[i];
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
}
if (window.attachEvent) {
window.attachEvent('onload', asyncLoad);
} else {
window.addEventListener('load', asyncLoad, false);
}
})();
/plugin/okendo/initAPI.js
window.okeReviewsWidgetOnInit = function (okeInitApi) {};
If I include the Okendo scripts to head tag, it works all fine.
But when I try to build on vercel, it says "error Building static HTML failed for path /products/example-product-slug".
2. Calling window.init api inside useEffect.
ProductReview.js
import React, { useEffect } from 'react';
import { Helmet } from 'react-helmet';
import transformProductId from '../../utils/transformProductId';
import { PRODUCT_REVIEW_METAFIELD_KEY, OKENDO_SUBSCRIBER_ID } from '../../constants';
const ProductReview = ({
product
}) => {
const OkendoSettings = {
filtersEnabled: true,
omitMicrodata: true,
subscriberId: OKENDO_SUBSCRIBER_ID,
widgetTemplateId: "default"
}
useEffect(() => {
if (typeof window !== `undefined` && window.okendoInitApi) {
const reviewsWidget = window.document.querySelector('#oke-reviews-widget');
window.okendoInitApi.initReviewsWidget(reviewsWidget);
}
}, [product.id]);
return (
<>
<Helmet>
<script type="application/javascript" src="../plugins/okendo/index.js" />
<script type="application/json" id="oke-reviews-settings">
{JSON.stringify(OkendoSettings)}
</script>
{/* <script type="application/javascript" src="../plugins/okendo/initAPI.js" /> */}
</Helmet>
<div
id="oke-reviews-widget"
data-oke-reviews-widget
data-oke-reviews-product-id={transformProductId(product.id)}
/>
</>
);
};
export default React.memo(ProductReview);
While I am using useEffect to initialize Okendo api, it works only when the page refresh, not work if I open a page.
And if I try to build it, it says "error "window" is not available during server side rendering.".
I know useEffect doesn’t run unless it’s in the browser, but still I don't get what the solution is.
Hope to hear a good news.
Thank you.
UPDATE: The product id is generated from Shopify product graphql data named handle.
gatsby-node.js
exports.createPages = async ({ graphql, actions: { createPage } }) => {
// Fetch all products
const products = await graphql(`
{
allNacelleProduct (filter: { availableForSale: {eq: true} }) {
edges {
node {
handle
}
}
}
}
`);
products.data.allNacelleProduct.edges.forEach((product) =>
createPage({
// Build a Product Detail Page (PDP) for each product
path: `/products/${product.node.handle}`,
component: path.resolve('./src/templates/product-detail.js'),
context: {
handle: product.node.handle
}
})
);
...

NativeScript one-way databinding doesn't update view

This seems like a pretty simple case to me, but I'm obviously missing something. I have a Model to be bound to the View. I then load the Model with an Http call. Why doesn't the View update? I thought that was the whole point of one-way binding.
I have verified that I'm getting back the data I'm expecting from the http call.
Update
I added a button to the screen and databinding will actually update the screen with the http loaded data for both fields on button push, even though the button method only sets one of the values. So either there's a bug in NativeScript or I'm not doing something incorrectly.
Update 2 Just the act of clicking the button will trigger the binding to happen. I've modified the code to have an empty tap handler, and just clicking the button makes it bind.
typescript
import { Component, ChangeDetectionStrategy, OnInit } from "#angular/core";
import { Job } from "../../shared/customer/job";
import { Http, Headers, Response } from "#angular/http";
import { Observable } from "rxjs/Rx";
#Component({
selector: "my-app",
templateUrl: "pages/job-details/job-details.html",
changeDetection: ChangeDetectionStrategy.OnPush
})
export class JobDetailsComponent implements OnInit {
job: Job;
salesAssociateName: string = "x";
constructor(private http: Http) {
this.job = new Job();
}
ngOnInit() {
this.getJob(1234);
}
getJob(leadId: number) {
var url = "https://url-not-for-you/job?franchiseeid=48&leadid=" + leadId;
var headers = this.createRequestHeader();
this.http.get(url, { headers: headers }).map(response => response.json())
.do(data => this.setData(data[0]))
.subscribe(
() => this.success(),
(error) => this.error()
);
}
private createRequestHeader() {
let headers = new Headers();
headers.append("AuthKey","blah");
headers.append("AuthToken", "blee");
return headers;
}
setData(job) {
this.job.FullName = job["FullName"];
this.job.SalesAssociateName = job["SalesAssociateName"];
this.salesAssociateName = this.job.SalesAssociateName;
console.log("Found job for customer: " + job["FullName"]);
}
success() {
// nothing useful
}
error() {
alert("There was a problem retrieving your customer job.");
}
changeSA() {
}
}
html
<StackLayout>
<Label [text]="job.FullName"></Label>
<Label [text]="salesAssociateName"></Label>
<Button text="Push" (tap)="changeSA()"></Button>
</StackLayout>
Your code will work as expected with the default ChangeDetectionStrategy. however, you have changed the strategy to onPush
In order to make your binding work as expected in the default changeStrategy delete the following line
changeDetection: ChangeDetectionStrategy.OnPush
or change it to
changeDetection: ChangeDetectionStrategy.Default
More about the Angular-2 ChangeDetectionStrategy here and here
If you still want to use onPush instead of the default strategy then your properties should be declared as #input() and once the change is made (in your case in setData) marked with markForCheck()
The reason your binding is working when triggered from Button tap is because
application state change can be triggered by:
Events - tap, swipe,
XHR - Fetching data from a remote server
Timers - e.g. setTimeout()
For testing purposes and if someone is interested of how to implement the scenario with onPush here is a sample code:
import { Component, ChangeDetectionStrategy, ChangeDetectorRef, OnInit, NgZone, Input } from "#angular/core";
import { Http, Headers, Response } from "#angular/http";
import { Observable as RxObservable } from "rxjs/Rx";
import "rxjs/add/operator/map";
import "rxjs/add/operator/do";
#Component({
selector: "my-app",
templateUrl: "app.component.html",
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent implements OnInit {
#Input() public job: any = { salesAssociateName: "default job" };
#Input() public salesAssociateName: string = "default name";
constructor(private http: Http, private change:ChangeDetectorRef) { }
ngOnInit() {
this.getJob();
}
getJob() {
var url = "http://httpbin.org/get";
var headers = this.createRequestHeader();
this.http.get(url, { headers: headers })
.map(response => response.json())
.do(data => {
this.setData();
}).subscribe(
() => this.success(),
(error) => this.error()
);
}
private createRequestHeader() {
let headers = new Headers();
return headers;
}
setData() {
this.job.salesAssociateName = "NEW job SalesAssociateName";
this.salesAssociateName = "NEW job FullName";
this.change.markForCheck();
}
success() {
alert("success");
}
error() {
alert("There was a problem retrieving your customer job.");
}
}

Nativescript how to save image to file in custom component

I have created a custom component that access the device's camera to snap a picture, set it as source of an ImageView and then save it to a file.
Here is the Javascript code
CAMERA.JS
Here is the initialization of the imageView
export function cameraLoaded(args):void{
cameraPage = <Page> args.object;
imageView = <Image> cameraPage.getViewById("img_upload");...
}
Here I set the imageViews'source to the just taken picture
export function takePicture():void{
camera.takePicture(
{
})
.then(
function(picture){
imageView.imageSource = picture;
},function(error){
});
}
This works perfectly.
Now I try to save the picture to a file.
export function saveToFile():void{
try {
let saved = imageView.imageSource.saveToFile(path,enums.ImageFormat.png);
HalooseLogger.log(saved,"upload");
})
}catch (e){
...
}
}
Here I get an error cannot read property saveToFile of undefined
This is very unusual, in fact if I console.log(imageView) here is the output :
Image<img_upload>#file:///app/views/camera/camera.xml:4:5;
but if I console.log(imageView.imageSource) i see it is ´undefined`.
How is this possible? What am I doing wrong?
ADDITIONAL INFO
The previous code and relatex xml is included in another view as follows :
MAIN.XML
<Page
xmlns="http://schemas.nativescript.org/tns.xsd"
xmlns:cameraPage="views/camera"
loaded="loaded">
<StackLayout orientation="vertical">
<StackLayout id="mainContainer">
<!-- DYNAMIC CONTENT GOES HERE -->
</StackLayout>
</StackLayout>
</Page>
MAIN.JS
This is were the camera view gets loaded dynamically
export function loaded(args):void{
mainPage = <Page>args.object;
contentWrapper = mainPage.getViewById("mainContainer");
DynamicLoaderService.loadPage(mainPage,contentWrapper,mainViewModel.currentActive);
}
The loadPage method does the following :
public static loadPage(pageElement,parentElement,currentActive):void{
let component = Builder.load({
path : "views/camera",
name : "camera",
page : pageElement
});
parentElement.addChild(component);
}
The thing is that as of NativeScript 2.4.0 the Image created for Android will always return null for its property imageSource. Currently, optimisations are on the way to prevent Out of Memory related issues when working with multiple large images and that is why image-asset was presented in nativeScript 2.4.0.
Now I am not sure if you are using the latest nativescript-camera (highly recommended) but if so you should consider that the promise from takePicture() is returning imageAsset. Due to the memory optimization imageSource will always return undefined (for Android) unless you specifically create one. You can do that with fromAsset() method providing the ImageAsset returned from the camera callback.
Example:
import { EventData } from 'data/observable';
import { Page } from 'ui/page';
import { Image } from "ui/image";
import { ImageSource, fromAsset } from "image-source";
import { ImageAsset } from "image-asset";
import * as camera from "nativescript-camera";
import * as fs from "file-system";
var imageModule = require("ui/image");
var img;
var myImageSource: ImageSource;
// Event handler for Page "navigatingTo" event attached in main-page.xml
export function onLoaded(args: EventData) {
// Get the event sender
let page = <Page>args.object;
img = <Image>page.getViewById("img");
camera.requestPermissions();
}
export function takePhoto() {
camera.takePicture()
.then(imageAsset => {
console.log("Result is an image asset instance");
img.src = imageAsset;
fromAsset(imageAsset).then(res => {
myImageSource = res;
console.log(myImageSource);
})
}).catch(function (err) {
console.log("Error -> " + err.message);
});
}
export function saveToFile(): void {
var knownPath = fs.knownFolders.documents();
var folderPath = fs.path.join(knownPath.path, "CosmosDataBank");
var folder = fs.Folder.fromPath(folderPath);
var path = fs.path.join(folderPath, "Test.png");
var saved = myImageSource.saveToFile(path, "png");
console.log(saved);
}

angular2 display base64 image

I'm trying to display an image that comes from a server as a base64 string. The http and server details are not important to my question (basically, it works).
I have this code that does not work; i.e. I see all the data in the component but no image shows on the screen.
the service:
import { Injectable } from 'angular2/core'
import {Observable} from 'rxjs/Observable';
#Injectable()
export class ImageService {
constructor() {
}
public getImage(): Observable<string> {
return Observable.create(imageData);
}
}
const imageData: string = "iVBORw0KGgoAAAANSUhE...";
The component:
import { Component } from 'angular2/core';
import { ImageService } from './service'
#Component({
selector: 'my-app',
template: '<div><img [src]="data:image/PNG;base64,{{imageContent}}"> </div>',
providers: [ImageService]
})
export class AppComponent {
private imageContent: string = "";
constructor(imageService: ImageService) {
imageService.getImage().subscribe(response => {
this.imageContent = response;
});
}
}
As mentioned, the code does not work. Instead of the image on the screen, I receive: Quotes are not supported for evaluation!
I'll appreciate a working example for this simple problem.
The following example shows how to display base64 encoded images using ASP.NET Core and Angular 2:
PhotoController (server-side)
[HttpGet]
public async Task<IEnumerable<PhotoResource>> GetPhotos(int entityId)
{
// get photo information
// in my case only path to some server location is stored, so photos must be read from disk and base64 encoded
foreach (var photoR in photoResources)
{
var currPath = Path.Combine(Host.ContentRootPath, "upload", photoR.FileName);
byte[] bytes = System.IO.File.ReadAllBytes(currPath);
photoR.Content = Convert.ToBase64String(bytes);
}
return photoResources;
}
PhotoService (Angular service)
getPhotos(int entityId) {
return this.http.get(`${this.apiBasePath}vehicles/${vehicleId}/photos`)
.map(res => res.json());
}
entity-component.ts
Server already sends encoded content, so I am just preparing a property containing header and content. Html template is separate.
this.photoService.getPhotos(this.entityId)
.subscribe(photos => {
this.photos = photos;
for (let photo of this.photos) {
photo.imageData = 'data:image/png;base64,' + photo.content;
}
});
entity-component.html
<img *ngFor="let photo of photos" [src]="photo.imageData" class="img-thumbnail">

Handling select event from Google Charts in Angular v2

I am using Angular v2 (2.0.0-beta-1) and displaying a simple chart using Google Charts.
import {Component, View} from "angular2/core";
import {Http, HTTP_PROVIDERS} from "angular2/http";
import {OnInit, OnDestroy} from 'angular2/core';
declare let io: any;
declare let google: any;
#Component({
selector:'default',
viewProviders: [HTTP_PROVIDERS]
})
#View({
templateUrl: 'app/default/default.html'
})
export class DefaultPage implements OnInit, OnDestroy {
charttitle: string;
data: any;
options: any;
timerToken: any;
chart: any;
socket: any;
constructor(http: Http) {
}
ngOnInit() {
console.log("onInit");
this.charttitle = "Sample Graph using live data";
this.options = {
title: "My Daily Activities",
is3D: true
};
this.socket = io();
this.socket.on("data_updated", (msg) => {
this.data = new google.visualization.DataTable();
this.data.addColumn('string', 'Task');
this.data.addColumn('number', 'Hours per Day');
this.data.addRows(5);
let data = JSON.parse(msg).activityData;
for (let i = 0; i < data.length; i++) {
let act = data[i];
this.data.setCell(i, 0, act.act);
this.data.setCell(i, 1, act.value);
}
this.chart.draw(this.data, this.options);
});
this.chart = new google.visualization.PieChart(document.getElementById('chart_div'));
google.visualization.events.addListener(this.chart, 'select', this.mySelectHandler);
}
mySelectHandler() {
console.trace();
console.log("Chart: " + this);
//let selectedItem = this.chart.getSelection()[0];
/*if (selectedItem) {
let value = this.data.getValue(selectedItem.row, 0);
console.log("The user selected: " + value);
}*/
}
ngOnDestroy() {
console.log("onDestroy");
this.socket.disconnect();
}
}
The problem I have is the following line.
google.visualization.events.addListener(this.chart, 'select', this.mySelectHandler);
The event is registered is when an element on the pie chart is selected the actual event handler is fired. But all the Angular JS 2 scope variables referenced by this aren't in scope. It's as if the Google Chart visualization library is running in its own scope.
I know that Angular JS has the Angular-Charts directive but we cannot use that as the company wants to use Angular v2 only.
Is there a way I can get the Google Charts API to 'bubble' an event to the event handler running on the scope of the Angular component.
If you want that your mySelectHandler takes part within the Angular2 context / change detection, you could leverage NgZone, as described below. This way, the changes you make in this function will update the view accordingly.
import {NgZone} from 'angular2/core';
export class DefaultPage implements OnInit, OnDestroy {
constructor(private ngZone:NgZone) {
}
ngOnInit()
this.chart = new google.visualization.PieChart(
document.getElementById('chart_div'));
google.visualization.events.addListener(
this.chart, 'select', () => {
this.ngZone.run(() => {
this.mySelectHandler();
});
}
);
}
}
Hope that I correctly understood your question.
Thierry

Resources