How i can edit asset name? its doesnt work. Thanks
let assetService = $injector.get(self.ctx.servicesMap.get('assetService'));
let activeID = self.ctx.data[0].datasource.entityId
let tenantId = self.ctx.dashboard.authUser.tenantId
let asset = {
additionalInfo: null,
createdTime: 1599121131415, // временно
customerId: {
entityType: "CUSTOMER",
id: self.ctx.dashboard.authUser.customerId
},
id: {
entityType: "ASSET",
id: activeID
},
label: null,
name: "kuku", // временно
tenantId: {
entityType: "TENANT",
id: tenantId
},
type: "справочник"
}
assetService.saveAsset(asset)
Thingsboard is built using Angular 10 currently See releases. You correctly injected the Angular service 'assetService'. You need to follow the Angular method of subscribing to the observable from assetService.
Calling
assetService.saveAsset(asset)
without subscribing means nothing happens. From the Angular University Blog
The multiple versions of the Angular HTTP module all have an RxJS Observable-based API. This means that the multiple calls to the HTTP module will all return an observable, that we need to subscribe to one way or the other.
So here's the code to 'subscribe' to the observable described above
assetService.saveAsset(asset).subscribe(
(response) => {
console.log(
"saveAsset call Success:",
response);
},
response => {
console.log(
"saveAsset call Error:",
response);
},
() => {
console.log(
"saveAsset observable Complete"
);
});
Let me know if there's a mistake in the code above, I didn't test it. And thanks for your question Anzor - it led me to a solution to make a custom Thingsboard widget along with the Widgets Development Guide.
In manifest.json, I have following model definition:
{
"sap.ui5": {
"models": {
"SalesInvoices": {
"type": "sap.ui.model.odata.v2.ODataModel",
"settings": {
"defaultOperationMode": "Server",
"defaultCountMode": "Request"
},
"dataSource": "ZAM_SALES_STATISTICS_CDS",
"preload": true
}
}
}
}
As you can see, SalesInvoices is connected to the OData service.
Now on the onInit function in the controller, I am trying to get Metadata from OData as following:
{ // Controller
onInit: function() {
const oPayerModel = this.getView().getModel("SalesInvoices");
console.log(oPayerModel.getMetadata());
setTimeout(() => {
const oPayerModel = this.getView().getModel("SalesInvoices");
console.log(oPayerModel.getMetadata());
}, 600);
},
// ...
}
As you can see, I have to delay to get the OData instance.
setTimeout is not recommended to use in SAPUI5, how can I do it better?
You can avoid setTimeout, as mentioned in this answer, by using the v2.ODataModel API metadataLoaded instead which returns a promise. The promise is fulfilled once the service metadata is loaded successfully.
onInit: async function() {
const oPayerModel = this.getOwnerComponent().getModel("SalesInvoices");
try {
await oPayerModel.metadataLoaded(true);
const oServiceMetadata = oPayerModel.getServiceMetadata(); // NOT .getMetadata()
// ...
} catch (oError) {/* ... */}
},
About the model being undefined in onInit, here are answers with better explanations:
Nabi's answer
My other answer
I think you are running into the issue I reported some time ago: Component + default OData model: this.getView().getModel() returns undefined in onInit() of controllers:
don't use this.getView().getModel() directly in onInit()
instead use this.getOwnerComponent().getModel() in onInit()
anywhere else in the controller you can use this.getView().getModel()
In your case you should be fine changing the suggestion of #boghyon slightly:
onInit: function() {
const oPayerModel = this.getOwnerComponent().getModel("SalesInvoices");
oPayerModel.metadataLoaded().then(this.onMetadataLoaded.bind(this, oPayerModel));
},
onMetadataLoaded: function(myODataModel) {
const metadata = myODataModel.getServiceMetadata(); // NOT .getMetadata()
// ...
},
This way you can get rid of setTimeout(...).
I've been working with a great audio recorder which is recorder.js-master.
But I can't make the upload work with MVC Controller.
Here's my javascript function.
function AudioUpload(title, description) {
Recorder.upload({
method: "POST",
url: '#Url.Action("Upload", "Audio")',
audioParam: "Recording",
params: {
"Title": title,
"Description": description
},
success: function (responseText) {
alert(responseText.Success);
},
error: function () {
alert("error");
},
progress: NULL
});
}
Here's my AudioController
public JsonResult Upload(VoicePassage passage)
{
//upload Audio
return Json(new { Success = true });
}
I have a breakpoint in Upload just to know that it's going to my Controller but it doesn't.
Please help.
Here's the reference I follow. http://marc.codewisp.com/2013/05/07/in-browser-audio-recording-recorder-js-asp-net-mvc/?replytocom=12#respond
Regards,
Christian
I'm the author of the original post you linked to.
A few things to try out:
Add a breakpoint to the JavaScript function: if it doesn't hit, something's wrong with your JavaScript.
Check your browser's network activity: In Chrome, under Developer Tools, go to the Network tab and attempt the upload. See if it is hitting the right URL.
Make sure you're using '#Url.Action...' inside a Razor view. It won't work in external JavaScript files.
If you need #Url.Action, you can probably use it in your Razor view, assign it to a temporary global variable and use it in your external JavaScript file.
In your View, add the following before the reference to your external JS:
<script type="text/javascript">
var audioAction = '#Url.Action("Upload", "Audio")';
</script>
In your JavaScript file, change '#Url.Action("Upload", "Audio")' to audioAction, as in:
function AudioUpload(title, description) {
Recorder.upload({
method: "POST",
url: audioAction,
audioParam: "Recording",
params: {
"Title": title,
"Description": description
},
success: function (responseText) {
alert(responseText.Success);
},
error: function () {
alert("error");
},
progress: NULL
});
}
I have
def exception = request.exception.stackTraceLines
in Groovy controller. How can I get the value of the exception in the JavaScript.
If you are adding exception to your return like this.
flash.message = message(exception: 'Error: xxx');
you can get it like this
<div class="message" role="status"> ${flash.message} </div>
just use ${ your flash.your_var_name}
You have multiple options to fix this. When using javascript I usually use a wrapper which can hold the error message/stacktrace. It depends on where and how you want to do the error handling. Example:
def book = new Book(params)
book.validate()
render (contentType: "text/json") {[
"data": book,
"errors": book.hasErrors() ? book.errors : null
]}
Then you can check if "errors" has a value when getting back your JSON to determine if there are errors in the input for instance. (Elvis operator probably works too, book.errors ?: null) Other (uncatched) exceptions I handle in the error callback I usually define in my JavaScript. (jQuery mostly, (with malsup jquery.form.js in this case))
$(function() {
$("form").live("submit", function() {
$(this).ajaxSubmit({
error: function (msg) {
/* Catch hard errors here (500's) */
alert("Error occurred: " + msg);
},
success: function(wrapper) {
if (wrapper.errors != null) {
/* Handle model errors here */
} else {
/* Parse data here */
var book = wrapper.data;
}
}
});
return false;
});
You can also do this
render(template:"book",model:[book:theBook, exception:exception])
Or like this (didnt try, don't know if works)
render(template:"book",model:[book:theBook, exception:exception!=null?exception:""])
and then access from the GSP like this
${exception.getMessage()}
i have just shifted to unobtrusive ajax that ships with mvc-3 but it is breaking at one point.
Here is my link
<%:Ajax.ActionLink("Edit", "Home", "Edit", new{id = Model.SomeID}, new AjaxOptions{OnSuccess = "DoSomething"})%>
this is my js function that will be called on success
<script type="text/javascript">
function DoSomething(data)
{
var clickedLinkID = this.id; // this line breaks it used to work with microsoft ajax
//rest of code goes here
}
</script>
i found this article in which imran describes how to solve the problem. but it involves adding one line to jquery.unobtrusive-ajax.js. Does it have any side effects? should i be changing jquery files? if not how can i get id of the link that was clicked without changing jquery.unobtrusive-ajax.js file
Here is a complete copy of edited MS unobtrusive AJAX including a newly minified version. It contains the context (source element missing) fix plus another important fix whereby the documented "cancel" class was not honoured when applied to submit items (should prevent validation but doesn't).
jquery.unobtrusive-ajax
/*!
** Unobtrusive Ajax support library for jQuery
** Copyright (C) Microsoft Corporation. All rights reserved.
** Fixed version (see full comments for details)
*/
/*
Fix for "validation ignores cancel class" applied from
http://stackoverflow.com/questions/11561496/jquery-unobtrusive-validation-ignores-cancel-class-on-submit-button-if-used-in
Line 144 changed to:
// Fixed to pass class name (needed for other fixes and useful anyway)
$(form).data(data_click, name ? [{ name: name, value: evt.target.value, className: evt.target.className }] : []);
Line 154 changed to:
// Fixed for "cancel" class not honoured (so correct documented behavior of non-validating/cancel buttons are restored)
if (clickInfo.length > 0 && clickInfo[0].className.indexOf('cancel') < 0 && !validate(this)) {
*/
/*
Fix for "source element missing on post-back event handler" applied from
http://forums.asp.net/t/1663285.aspx?How+to+get+source+Element+when+using+Unobtrusive+Ajax+in+ASP+NET+MVC+3
Line 101 inserted:
// Fixed to pass source element in context (so source element can be discovered in handlers)
options.context = element;
*/
/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */
/*global window: false, jQuery: false */
(function ($) {
var data_click = "unobtrusiveAjaxClick",
data_validation = "unobtrusiveValidation";
function getFunction(code, argNames) {
var fn = window, parts = (code || "").split(".");
while (fn && parts.length) {
fn = fn[parts.shift()];
}
if (typeof (fn) === "function") {
return fn;
}
argNames.push(code);
return Function.constructor.apply(null, argNames);
}
function isMethodProxySafe(method) {
return method === "GET" || method === "POST";
}
function asyncOnBeforeSend(xhr, method) {
if (!isMethodProxySafe(method)) {
xhr.setRequestHeader("X-HTTP-Method-Override", method);
}
}
function asyncOnSuccess(element, data, contentType) {
var mode;
if (contentType.indexOf("application/x-javascript") !== -1) { // jQuery already executes JavaScript for us
return;
}
mode = (element.getAttribute("data-ajax-mode") || "").toUpperCase();
$(element.getAttribute("data-ajax-update")).each(function (i, update) {
var top;
switch (mode) {
case "BEFORE":
top = update.firstChild;
$("<div />").html(data).contents().each(function () {
update.insertBefore(this, top);
});
break;
case "AFTER":
$("<div />").html(data).contents().each(function () {
update.appendChild(this);
});
break;
default:
$(update).html(data);
break;
}
});
}
function asyncRequest(element, options) {
var confirm, loading, method, duration;
confirm = element.getAttribute("data-ajax-confirm");
if (confirm && !window.confirm(confirm)) {
return;
}
loading = $(element.getAttribute("data-ajax-loading"));
duration = element.getAttribute("data-ajax-loading-duration") || 0;
$.extend(options, {
type: element.getAttribute("data-ajax-method") || undefined,
url: element.getAttribute("data-ajax-url") || undefined,
beforeSend: function (xhr) {
var result;
asyncOnBeforeSend(xhr, method);
result = getFunction(element.getAttribute("data-ajax-begin"), ["xhr"]).apply(this, arguments);
if (result !== false) {
loading.show(duration);
}
return result;
},
complete: function () {
loading.hide(duration);
getFunction(element.getAttribute("data-ajax-complete"), ["xhr", "status"]).apply(this, arguments);
},
success: function (data, status, xhr) {
asyncOnSuccess(element, data, xhr.getResponseHeader("Content-Type") || "text/html");
getFunction(element.getAttribute("data-ajax-success"), ["data", "status", "xhr"]).apply(this, arguments);
},
error: getFunction(element.getAttribute("data-ajax-failure"), ["xhr", "status", "error"])
});
options.data.push({ name: "X-Requested-With", value: "XMLHttpRequest" });
// Fixed to pass source element in context (so source element can be discovered in handlers)
options.context = element;
method = options.type.toUpperCase();
if (!isMethodProxySafe(method)) {
options.type = "POST";
options.data.push({ name: "X-HTTP-Method-Override", value: method });
}
$.ajax(options);
}
function validate(form) {
var validationInfo = $(form).data(data_validation);
return !validationInfo || !validationInfo.validate || validationInfo.validate();
}
$(document).on("click", "a[data-ajax=true]", function (evt) {
evt.preventDefault();
asyncRequest(this, {
url: this.href,
type: "GET",
data: []
});
});
$(document).on("click", "form[data-ajax=true] input[type=image]", function (evt) {
var name = evt.target.name,
$target = $(evt.target),
form = $target.parents("form")[0],
offset = $target.offset();
$(form).data(data_click, [
{ name: name + ".x", value: Math.round(evt.pageX - offset.left) },
{ name: name + ".y", value: Math.round(evt.pageY - offset.top) }
]);
setTimeout(function () {
$(form).removeData(data_click);
}, 0);
});
$(document).on("click", "form[data-ajax=true] :submit", function (evt) {
var name = evt.target.name,
form = $(evt.target).parents("form")[0];
// Fixed to pass class name (needed for other fixes and useful anyway)
$(form).data(data_click, name ? [{ name: name, value: evt.target.value, className: evt.target.className }] : []);
setTimeout(function () {
$(form).removeData(data_click);
}, 0);
});
$(document).on("submit", "form[data-ajax=true]", function (evt) {
var clickInfo = $(this).data(data_click) || [];
evt.preventDefault();
// Fixed for "cancel" class not honoured (so correct documented behavior of non-validating/cancel buttons are restored)
if (clickInfo.length > 0 && clickInfo[0].className.indexOf('cancel') < 0 && !validate(this)) {
return;
}
asyncRequest(this, {
url: this.action,
type: this.method || "GET",
data: clickInfo.concat($(this).serializeArray())
});
});
}(jQuery));
jquery.unobtrusive-ajax-fixed.min.js
/*
** Unobtrusive Ajax support library for jQuery
** Copyright (C) Microsoft Corporation. All rights reserved.
** Fixed version (see full comments for details)
*/
(function(a){var b="unobtrusiveAjaxClick",g="unobtrusiveValidation";function c(d,b){var a=window,c=(d||"").split(".");while(a&&c.length)a=a[c.shift()];if(typeof a==="function")return a;b.push(d);return Function.constructor.apply(null,b)}function d(a){return a==="GET"||a==="POST"}function f(b,a){!d(a)&&b.setRequestHeader("X-HTTP-Method-Override",a)}function h(c,b,e){var d;if(e.indexOf("application/x-javascript")!==-1)return;d=(c.getAttribute("data-ajax-mode")||"").toUpperCase();a(c.getAttribute("data-ajax-update")).each(function(f,c){var e;switch(d){case"BEFORE":e=c.firstChild;a("<div />").html(b).contents().each(function(){c.insertBefore(this,e)});break;case"AFTER":a("<div />").html(b).contents().each(function(){c.appendChild(this)});break;default:a(c).html(b)}})}function e(b,e){var j,k,g,i;j=b.getAttribute("data-ajax-confirm");if(j&&!window.confirm(j))return;k=a(b.getAttribute("data-ajax-loading"));i=b.getAttribute("data-ajax-loading-duration")||0;a.extend(e,{type:b.getAttribute("data-ajax-method")||undefined,url:b.getAttribute("data-ajax-url")||undefined,beforeSend:function(d){var a;f(d,g);a=c(b.getAttribute("data-ajax-begin"),["xhr"]).apply(this,arguments);a!==false&&k.show(i);return a},complete:function(){k.hide(i);c(b.getAttribute("data-ajax-complete"),["xhr","status"]).apply(this,arguments)},success:function(a,e,d){h(b,a,d.getResponseHeader("Content-Type")||"text/html");c(b.getAttribute("data-ajax-success"),["data","status","xhr"]).apply(this,arguments)},error:c(b.getAttribute("data-ajax-failure"),["xhr","status","error"])});e.data.push({name:"X-Requested-With",value:"XMLHttpRequest"});e.context=b;g=e.type.toUpperCase();if(!d(g)){e.type="POST";e.data.push({name:"X-HTTP-Method-Override",value:g})}a.ajax(e)}function i(c){var b=a(c).data(g);return!b||!b.validate||b.validate()}a(document).on("click","a[data-ajax=true]",function(a){a.preventDefault();e(this,{url:this.href,type:"GET",data:[]})});a(document).on("click","form[data-ajax=true] input[type=image]",function(c){var g=c.target.name,d=a(c.target),f=d.parents("form")[0],e=d.offset();a(f).data(b,[{name:g+".x",value:Math.round(c.pageX-e.left)},{name:g+".y",value:Math.round(c.pageY-e.top)}]);setTimeout(function(){a(f).removeData(b)},0)});a(document).on("click","form[data-ajax=true] :submit",function(c){var e=c.target.name,d=a(c.target).parents("form")[0];a(d).data(b,e?[{name:e,value:c.target.value,className:c.target.className}]:[]);setTimeout(function(){a(d).removeData(b)},0)});a(document).on("submit","form[data-ajax=true]",function(d){var c=a(this).data(b)||[];d.preventDefault();if(c.length>0&&c[0].className.indexOf("cancel")<0&&!i(this))return;e(this,{url:this.action,type:this.method||"GET",data:c.concat(a(this).serializeArray())})})})(jQuery)
I am disappointed that these critical normal behavioural fixes have not been corrected in such a long time (especially since there are just three simple edits). Even the latest September 2013 release of the AJAX toolkit is still using the now legacy "Sys" Microsoft AJAX Library whilst the rest of the world already migrated to jQuery UI. Maybe this will all be clarified with the RTM of Visual Studio 2013 specifically MVC 5?
As you are now using jQuery you need to wrap it in the $ to get the context. So the code will be something like
$(this).addClass("someclass");
You can find more from the api docs here.