Hope someone can help me. I have embedded a Power BI solution, and it work so fine, but when i try to hide the filter pane or apply a filter, it dosen't work. My javascript file is here:
$(function () {
var models = window["powerbi-client"].models;
var reportContainer = $("#report-container").get(0);
// Initialize iframe for embedding report
powerbi.bootstrap(reportContainer, { type: "report" });
$.ajax({
type: "GET",
url: "/embedinfo/getembedinfo",
success: function (data) {
embedData = $.parseJSON(data);
reportLoadConfig = {
type: "report",
tokenType: models.TokenType.Embed,
accessToken: embedData.EmbedToken.Token,
// You can embed different reports as per your need
embedUrl: embedData.EmbedReport[0].EmbedUrl,
permissions: permissions,
settings: {
panes: {
filters: {
visible: false
},
pageNavigation: {
visible: false
}
}
}
// Enable this setting to remove gray shoulders from embedded report
// settings: {
// background: models.BackgroundType.Transparent
// }
};
and it looks still:
What's wrong with my code?
I find and use this, and it works:
// ---- Embed Code ----------------------------------------------------
$(function () {
var models = window["powerbi-client"].models;
var reportContainer = $("#report-container").get(0);
// Initialize iframe for embedding report
powerbi.bootstrap(reportContainer, { type: "report" });
$.ajax({
type: "GET",
url: "/embedinfo/getembedinfo",
success: function (data) {
embedData = $.parseJSON(data);
var models = window["powerbi-client"].models;
// Read embed application token from textbox
var txtAccessToken = embedData.EmbedToken.Token;
// Read embed URL from textbox
var txtEmbedUrl = embedData.EmbedReport[0].EmbedUrl;
// Read embed type from radio
var tokenType = models.TokenType.Embed;
// Get models. models contains enums that can be used.
var models = window["powerbi-client"].models;
// We give All permissions to demonstrate switching between View and Edit mode and saving report.
var permissions = models.Permissions.All;
// Embed configuration used to describe the what and how to embed.
// This object is used when calling powerbi.embed.
// This also includes settings and options such as filters.
// You can find more information at https://github.com/Microsoft/PowerBI-JavaScript/wiki/Embed-Configuration-Details.
const myFilter = {
$schema: "http://powerbi.com/product/schema#advanced",
target: {
table: "myTable",
column: "myColumn",
},
operator: "In",
values: ["1"],
};
var config = {
type: "report",
tokenType: tokenType,
accessToken: txtAccessToken,
embedUrl: txtEmbedUrl,
permissions: permissions,
filters: [myFilter],
settings: {
panes: {
filters: {
visible: true,
},
pageNavigation: {
visible: true,
},
},
},
};
// Get a reference to the embedded report HTML element
var reportContainer = $("#report-container").get(0);
// Embed the report and display it within the div container.
var report = powerbi.embed(reportContainer, config);
// Report.off removes a given event handler if it exists.
report.off("loaded");
// Report.on will add an event handler which prints to Log window.
report.on("loaded", function () {
Log.logText("Loaded");
});
// Report.off removes a given event handler if it exists.
report.off("rendered");
// Report.on will add an event handler which prints to Log window.
report.on("rendered", function () {
Log.logText("Rendered");
});
report.on("error", function (event) {
Log.log(event.detail);
report.off("error");
});
report.off("saved");
report.on("saved", function (event) {
Log.log(event.detail);
if (event.detail.saveAs) {
Log.logText(
"In order to interact with the new report, create a new token and load the new report"
);
}
});
},
});
});
Related
For the website I develop, I use Newsletter module to create a mailing list. It's quite enough for basic needs. When you insert an e-mail and click to subscribe button, it shows (replace) "Thanks" message and hide the "Subscribe" button. It also shows a toast message: "Thanks for subscribing!" on the top right side of the page.
I don't want to show toast messages for newsletter subscriptions. Unfortunately, there is no option to enable/disable it.
If I disable/remove that part below from website_mass_mailing.js file it doesn't show the toast message.
self.displayNotification({
type: toastType,
title: toastType === 'success' ? _t('Success') : _t('Error'),
message: result.toast_content,
sticky: true,
});
I don't want to touch this file (website_mass_mailing.js) but instead, inherit it and remove that part but I couldn't succeed. Any suggestion on how to do it?
You should create a new module which depends on website_mass_mailing and extends mass_mailing.website_integration via a dedicated javascript module.
For example:
odoo.define('my_module.mass_mailing_website_integration', function (require) {
var website_integration = require('mass_mailing.website_integration');
website_integration.extend({
// Your Logic Here
});
}
Find mass_mailing method who's calling displayNotification and override it.
Unfortunately i see no alternative to copy-pasting it entirely from source and then removing desired behaviours.
Do not forget to include your javascript in web_assets template.
After suggestions of #icra I've tried to figure it out and here is the code that worked for me.
Thanks to Cybrosys Techno Solutions Pvt.Ltd as well to achieve the solution.
Here is the code:
odoo.define('your_module.name', function (require){
var publicWidget = require('web.public.widget');
var _t = core._t;
publicWidget.registry.subscribe.include({
_onSubscribeClick: async function () {
var self = this;
var $email = this.$(".js_subscribe_email:visible");
if ($email.length && !$email.val().match(/.+#.+/)) {
this.$target.addClass('o_has_error').find('.form-control').addClass('is-invalid');
return false;
}
this.$target.removeClass('o_has_error').find('.form-control').removeClass('is-invalid');
let tokenObj = null;
if (this._recaptcha) {
tokenObj = await this._recaptcha.getToken('website_mass_mailing_subscribe');
if (tokenObj.error) {
self.displayNotification({
type: 'danger',
title: _t("Error"),
message: tokenObj.error,
sticky: true,
});
return false;
}
}
const params = {
'list_id': this.$target.data('list-id'),
'email': $email.length ? $email.val() : false,
};
if (this._recaptcha) {
params['recaptcha_token_response'] = tokenObj.token;
}
this._rpc({
route: '/website_mass_mailing/subscribe',
params: params,
}).then(function (result) {
let toastType = result.toast_type;
if (toastType === 'success') {
self.$(".js_subscribe_btn").addClass('d-none');
self.$(".js_subscribed_btn").removeClass('d-none');
self.$('input.js_subscribe_email').prop('disabled', !!result);
if (self.$popup.length) {
self.$popup.modal('hide');
}
}
// make the changes you need accordingly or comment out the below code.
self.displayNotification({
type: toastType,
title: toastType === 'success' ? _t('Success') : _t('Error'),
message: result.toast_content,
sticky: true,
});
});
},
})
})
I have a AngularJS web application, I'm trying to upload a file to a server and while the upload is complete, I have to update ng-grid with the last uploaded file's entry. The following is my grid html,
<div class="gridholder" data-ng-grid="viewmodel.gridOptions">
</div>
The following is my controller logic.
vm.gridOptions = {
data: 'gridData',
enableColumnResize: true,
enablePaging: true,
columnDefs: [
{ field: 'FileName', displayName: 'File Name', width: 250 }
{ field: 'UploadedDate', displayName: 'Uploaded Date'}
],
multiSelect: false,
enableSorting: true,
showFooter: true,
};
The requirement is that I show the progress of file upload and the entire application to be responsive when upload is in progress, I have achieved this but my ng-grid not is updating in a particular scenario.
If I remain in the same page until the file is uploaded and the response comes, the grid is refreshing but when I move to another page of my application and come back to the file upload page, and the response comes after, my grid is not getting refreshed.
This is my file upload js code,
var data = new FormData();
data.append('file', file);
var xhrRequest = Factory.uploadFileRequest('UploadFile');
xhrRequest.upload.addEventListener("progress", progressHandler, false);
xhrRequest.onreadystatechange = function (e) {
};
xhrRequest.onload = function (e) {
if (JSON.parse(e.currentTarget.responseText).Success == true) {
$timeout(function () {
$scope.LoadGrid();
//showing success message here
}, 2000);
}
else
{
//showing error message here
}
};
xhrRequest.onerror = function (e) {
//showing error message here
};
xhrRequest.send(data);
$scope.LoadGrid = function () {
Factory.callGet("Files").then(function (d) {
$scope.gridData = d.data;
}
$scope.totalItems = $scope.gridData.length;
}, function error(err) {
//Error Message
});
}
gridData is my data-ng-grid value. I'm calling my LoadGrid method inside a $timeout already but still the grid is not refreshing with latest data. Any help would be much appreciated.
Possible Problem
You implemented upload logic inside the controller. When you switch to another view, angularjs destroys your controller and therefore no one listens on file upload response.
Possible solution:
1) Use a service (or Factory) kind of singleton to manage upload process there.
For example MyService.upload(data).then(function (response) {/**/});
2) By default MyService.upload(data) returns a promise on a regular basis but also stores the result inside the Service, for example, upload_results:
app.service('MyService',['$q',function ($q) {
var self = this;
var upload_results = [];
self.upload = function (_data) {
return // <YOUR_PROMISE>
.then(function (response) {
upload_results.push({
id: new Date().getTime(),
data: response.data
})
}
, function (error) {
console.error(error);
return $q.reject(error);
});
};
self.getResults() = function(){
return upload_results;
}
self.resetResults() = function(){
upload_results = [];
}
}
When you initialize the controller on start or go back to the previous controller, you ask the service if it has something for you:
var results = MyService.getResults();
if(results.length > 0){
$scope.gridData = results[0].data; // or use timestamp to manage it
MyService.resetResults();
}
Hope it will give you some insight,
I want to submit the values in my form but each time I press the submit button I get the following error:
Uncaught TypeError: login is not a function
at Object.submit [as fn] (eval at Yr (vue.min.js:7), <anonymous>:2:369)
at HTMLFormElement.<anonymous> (vue.min.js:6)
In my HTML code I have my form declared as such: <form v-on:submit.prevent="login">
In the JS it looks like this:
// register
Vue.component("login-form",
{
template: // THE HTML FORM
,
data: function () {
return data;
},
ready: function () {
this.isAuthenticated = this.checkIfAuthenticated();
this.userName = localStorage.getItem("id_name");
},
methods: {
checkIfAuthenticated: function () {
const users = [
{
id: 1,
name: "tom"
},
{
id: 2,
name: "brian"
},
{
id: 3,
name: "sam"
}
];
this.$set("users", users);
},
login: function () {
const headers = { "Content-Type": "application/x-www-form-urlencoded" };
$.ajax({
url: "/token",
type: "post",
data: `username=${this.login.username}&password=${this.login.password}`,
headers: headers,
success: function (data) {
console.info(data);
},
error: function() {
this.isValid = false;
}
});
}
}
});
// create a root instance
var vue = new Vue({
el: "#loginForm"
});
As you see the login function is in my methods, so I don't see why vue is throwing the error
Edit: inluced a JSFiddle. The problem happens when you submit the form (after you click on login)
Looks like your instance has a conflict. There is a login property in a component and there is a login method in Vue instance. Try to use different names. Issue at vuejs github:
Both data properties and methods will be set on the instance, so the
short answered is: don't use conflicting names :)
I tried to sync database return html string append to each .buttonContainer , use action.js execute other file loadButton module use parameter point specific element.
the result should be use different .buttonContainer and with their attr id connect db get result append to each .buttonContainer
My question is if I set ajax option async default true, then both return html string will append to the last .buttonContainer.
I can't understand I already set each module/function have their attr el from each execute parameter.
Why and how to solve it?
I tried to change async to false then it work but slowly the page, so I'm trying to find other solution.
<div class="buttonContainer" data-user-id="0"></div>
<div class="buttonContainer" data-user-id="1"></div>
action.js
define(['DomReady!', 'jquery', 'loadButton'],function (DomReady, $, loadButton) {
return {
init: function() {
// loop each element as parameter and execute
$('.buttonContainer').each(function(index, el) {
var config = {};
config.el = $(this);
loadButton.init(config);
});
},
}
});
loadButton.js
define(['DomReady!', 'jquery'],function (DomReady, $) {
return {
init: function(config) {
this.config = {};
this.config.el = config.el; // set each execute have their own private attribute
this.onLoadAction();
},
onLoadAction: function() {
this.onLoadController();
},
onLoadController: function() {
var userId = this.config.el.attr('data-user-id');
var mthis = this;
this.onLoadRequestDB('load/'+userId).done(function(response) {
console.log(mthis.config.el);
var response = JSON.parse(response);
mthis.config.el.append(response.button);
});
},
onLoadRequestDB: function(url) {
return $.ajax({
url: url,
type: 'GET',
processData: false,
contentType: false,
// async: false
});
},
}
});
Edit:
https://stackoverflow.com/a/22121078/1775888 I found some solution here, so I edit loadButton.js like this
..
init: function(config) {
this.config = {};
this.config.el = config.el; // set each execute have their own private attribute
this.onLoadAction(this.config);
},
onLoadAction: function(config) {
this.onLoadController(config);
},
onLoadController: function(config) {
..
pass parameter, then it work.
But I still want to know why I set the loadButton.js init this, in each loop but still can be cover after ajax. makes all response append to element from last execute loadButton.js config parameter
I am using HTML5 History API (on Chrome), at the following link:
http://jsbin.com/zuqijofole/1
You can see a simple application which shows/hides div (Views).
Script works fine using browser backward and forward buttons but if I type directly in the browser the following address (in order to see View 2)
http://jsbin.com/zuqijofole/2
the document is not found. I need instead the second View to be shown.
I would like to know:
Should I implement on server side some logic which map URL? Which
coul be a solution using latest PHP?
Or am I missing some implementation in my JS?
Notes: solution should work in a SPA application, so all data is rendered by JS app.
window.app = {
data: {
views: [
{ id: 0, isActive: false },
{ id: 1, isActive: false },
{ id: 2, isActive: false },
]
},
start: function () {
this.listeners();
// default entry
var activeView = this.wm.activeView;
history.replaceState({ activeView: activeView }, document.title, document.location.href);
window.app.wm.hideViews();
window.app.wm.showView();
},
listeners: function () {
window.addEventListener('popstate', function (event) {
// fires when backing/forwarding in history
console.log(event);
console.log(window.history.state);
this.wm.showHideBl(event.state);
}.bind(this));
var elm = document.getElementById('btn-prev');
elm.addEventListener('click', function () {
window.app.wm.snowPrevView();
});
elm = document.getElementById('btn-next');
elm.addEventListener('click', function () {
window.app.wm.snowNextView();
});
},
wm: {
activeView: 0, // default
showView: function () {
var elm = document.getElementById('view-' + this.activeView);
elm.style.display = '';
},
showHideBl: function (data) {
this.hideView();
this.activeView = data.activeView;
this.showView();
},
snowNextView: function () {
// bl
if (this.activeView < window.app.data.views.length - 1) {
this.hideView();
this.activeView++;
this.showView();
history.pushState({ activeView: this.activeView }, '', this.activeView);
}
},
snowPrevView: function () {
// bl
if (this.activeView > 0) {
this.hideView();
this.activeView--;
this.showView();
history.pushState({ activeView: this.activeView }, '', this.activeView);
}
},
hideView: function () {
var elm = document.getElementById('view-' + this.activeView);
elm.style.display = 'none';
},
hideViews: function () {
window.app.data.views.forEach(function (item, index, array) {
var elm = document.getElementById('view-' + item.id);
elm.style.display = 'none';
}.bind(this));
}
}
};
Yes, for a completely seamless experience, you want that all URLs created by the History API to be mapped to actual URLs that the server can use.
For example, if you have a table that you can sort client side, you can use the history API to save the sorting state into the URL. The server should be able to read that URL and serve a table already sorted when the page is refreshed.
The best way to make sure everything works as intended is to disable JavaScript and make sure you can still navigate and use the site correctly (even though the page refreshes all the time).