I'm trying to do a widget to attach to the sysTrayMenu, I need to know on the on_click event, the current model of the view. I know that I can get it from the current browser url, but I wanted to know if is there a cleaner way to get it from the odoo js api.
For example if the user is in New quotation menu, I need to get sale.order
odoo.define('xx.systray', function (require) {
"use strict";
var config = require('web.config');
var SystrayMenu = require('web.SystrayMenu');
var Widget = require('web.Widget');
var ajax = require('web.ajax');
var xxMenu = Widget.extend({
template:'solvo-support.helpMenu',
events: {
"click": "on_click",
},
on_click: function () {
//HERE I NEED TO GET THE CURRENT MODEL
},
});
SystrayMenu.Items.push(xxMenu);
});
As I remember you can access the the model of the widget like this:
this.model // or self.model if you defined self (self = this)
all widget have this attribute it's string type contains the name of the model.
Related
I'm writing an Odoo v9 widget, which renders a URL, based on concatenation of a setting in the database, and the actual form fields.
The setting in the database I figure should live in ir_config_parameter. I'm inserting a default value with my module.
What's the best way to get this value when rendering the widget? Doing an async ajax call using
new Model("ir.config_parameter")
seems a little heavy handed. Is there a better way to be doing this?
Thanks.
Widget code:
var UrlWidget2 = form_common.FormWidget.extend({
start: function() {
this._super();
this.field_manager.on("field_changed:ref", this, this.display_result);
this.display_result();
},
display_result: function() {
var ref = this.field_manager.get_field_value("ref");
if (!ref) return;
var baseUrl = 'https://example.com'; //this is the value I want to get from the setting in the database.
var url = baseUrl + '/foo/' + ref;
this.$el.html('View Externally<br /><br/>');
}
});
You can use RPC for this. This is example which work for me:
var Model = require('web.DataModel');
var UrlWidget2 = form_common.FormWidget.extend({
// just example how to get parameter from backend
display_result: function() {
var parameter = new Model('ir.config_parameter');
// get fields value, key
parameter.query(['value', 'key'])
// criteria of search - record with id = 1
.filter([['id', '=', 1]])
// only one record
.limit(1)
.all()
.then(function (parameter) {
// here data from server
console.log(parameter);
});
// ...
}
});
Hope this helps you.
I want to have in foreach loop list of a href labels with links to MyAction.
In Edit.cshtml (http://localhost/mysite/Order/Edit/4) in foreach loop (knockout) I have a href for each item:
Eddit
Method is implemented in ViewModel.js:
self.EditForStore = function (item) {
console.log(item.IdStore());
var idStore = item.IdStore();
//var urlMeta = '#Url.Action("MyAction", "MyController", new { pStoreId = ' + idStore.toString() + ' })';
var urlMeta = '#Url.Action("MyAction", "MyController", new { pStoreId = 4 })';
//var myURL = '#Url.Action("MyAction", "MyController")';
window.location.href = urlMeta;
};
I cannot redirect to MyAction.
When I set location.href , it redirect to http address:
http://localhost/mysite/Order/Edit/4/MyAction/MyController?pStoreId=4
I cannot change the location.href, I only appends text to my current address.
A technique I have used for getting server side routes into knockout is to generate the route with a known parameter value and store it as a data attribute. Then knockout then pushes the data value into the href.
You can take advantage of the $element context variable to supply the current element into a function declared on the parent viewModel in a foreach binding, the use the attr binding to update the href attribute.
For example...
HTML
<div data-bind="foreach: stores">
</div>
Javascript
var ViewModel = (function() {
var ctor = function ViewModel() {
this.stores = ko.observableArray( [{ id: 1 }, {id:2}, {id:3}, {id:4} ]);
};
ctor.prototype.buildUrl = function(element, data) {
return element.getAttribute('data-url').replace('pStoreId=0', 'pStoreId=' + data);
};
return ctor;
})();
ko.applyBindings( new ViewModel() );
See http://jsfiddle.net/9Lz4493d/ for a sample (without the razor syntax and a hard coded data-url attribute, but you get the idea.)
The Url builder function could easily be enhanced to supply the parameter name, etc.
i have several singleton views in my SPA, each of these view contain the same widget.
When the view is activated i take some parameters from the activate callback and pass it to the widget and it works fine.
But if i navigate the second time into the view (with different parameters into the activate callback)
the activate method of the widgets is rightly not raised.
How can i pass the fresh data to the widgets ?
I tried to make the parameter observable and subscribe it into the widget (settings.params.subscribe) and it works, but i don't think it's a good solution.
This should be pretty simple assuming you are returning a constructor from your widget -
View model -
var thisWidget = new widget(someArbitraryData)
function createWidget() {
dialog.show(thisWidget);
}
// later
function updateWidget() {
thisWidget.refreshData(newArbitraryData);
}
Widget module -
define([], function () {
var ctor = function () {
var self = this;
self.data = ko.observable();
};
ctor.prototype.refreshData = function (newData) {
var self = this;
self.data(newData);
};
ctor.prototype.activate = function (activationData) {
var self = this;
self.data(activationData);
};
});
Is there a way in Durandal to get the path of the current module? I'm building a dashboard inside of a SPA and would like to organize my widgets in the same way that durandal does with "FolderWidgetName" and the folder would contain a controller.js and view.html file. I tried using the getView() method in my controller.js file but could never get it to look in the current folder for the view.
getView(){
return "view"; // looks in the "App" folder
return "./view"; // looks in the "App/durandal" folder
return "/view"; // looks in the root of the website
return "dashboard/widgets/htmlviewer/view" //don't want to hard code the path
}
I don't want to hardcode the path inside of the controller
I don't want to override the viewlocator because the rest of the app still functions as a regular durandal spa that uses standard conventions.
You could use define(['module'], function(module) { ... in order to get a hold on the current module. getView() would than allow you to set a specific view or, like in the example below, dynamically switch between multiple views.
define(['module'], function(module) {
var roles = ['default', 'role1', 'role2'];
var role = ko.observable('default');
var modulePath = module.id.substr(0, module.id.lastIndexOf('/') +1);
var getView = ko.computed(function(){
var roleViewMap = {
'default': modulePath + 'index.html',
role1: modulePath + 'role1.html',
role2: modulePath + 'role2.html'
};
this.role = (role() || 'default');
return roleViewMap[this.role];
});
return {
showCodeUrl: true,
roles: roles,
role: role,
getView: getView,
propertyOne: 'This is a databound property from the root context.',
propertyTwo: 'This property demonstrates that binding contexts flow through composed views.',
moduleJSON: ko.toJSON(module)
};
});
Here's a live example http://dfiddle.github.io/dFiddle-1.2/#/view-composition/getView
You can simply bind your setup view to router.activeRoute.name or .url and that should do what you are looking for. If you are trying to write back to the setup viewmodels property when loading you can do that like below.
If you are using the revealing module you need to define the functions and create a module definition list and return it. Example :
define(['durandal/plugins/router', 'view models/setup'],
function(router, setup) {
var myObservable = ko.observable();
function activate() {
setup.currentViewName = router.activeRoute.name;
return refreshData();
}
var refreshData = function () {
myDataService.getSomeData(myObservable);
};
var viewModel = {
activate: activate,
deactivate: deactivate,
canDeactivate: canDeactivate
};
return viewModel;
});
You can also reveal literals, observables and even functions directly while revealing them -
title: ko.observable(true),
desc: "hey!",
canDeactivate: function() { if (title) ? true : false,
Check out durandal's router page for more info on what is available. Also, heads up Durandal 2.0 is switching up the router.
http://durandaljs.com/documentation/Router/
Add an activate function to your viewmodel as follows:
define([],
function() {
var vm = {
//#region Initialization
activate: activate,
//#endregion
};
return vm;
//#region Internal methods
function activate(context) {
var moduleId = context.routeInfo.moduleId;
var hash = context.routeInfo.hash;
}
//#endregion
});
I'm trying to call a widget function from success node of an ajax call, however, I'm having no success with it.
My app allows users to add some markers on googlemaps with some description. It is using JQuery-addresspicker widget and Rails. I added a function responsible for adding a marker, it displays a form with a description textarea and a button to submit the information. So, after user submits, the app calls an Ajax function to store user's data and if successfully, I want to call another widget function, just for close InfoWindow, for example.
The problem is, I don't know how to call another widget function from success Ajax node.
JQuery-addresspicker.js
.
.
.
_addFormListener: function(map, marker) {
var form = $(".add-form").clone().show();
var infoWindowContent = form[0];
var infoWindow = new google.maps.InfoWindow({
content: infoWindowContent
});
google.maps.event.addListener(marker, "click", function() {
infoWindow.open(map, this);
});
form.submit(function (event){
event.preventDefault();
var description = $("textarea[name=description]", this).val();
var latitude = marker.getPosition().lat();
var longitude = marker.getPosition().lng();
var data = {
description : description,
latitude : latitude,
longitude : longitude
};
$.ajax({
type : "POST",
url : "/places",
data: {place: data},
beforeSend: function(x) {
x.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
},
success: function(x) {
this._closeFormFields(); // Not working!
}
});
});
},
_cleanFormFields: function() {
console.log("testing");
}
.
.
PlacesController
def create
#place = Place.new(params[:place])
if #place.save
redirect_to places_path
else
render :status => 422
end
end
The browser's console raises "Uncaught TypeError: Object [object Window] has no method '_cleanFormFields'
Any ideas? Thanks!
The problem here is the scope of this - the ajax call overwrites this to reference the ajax call object and you loose the widget reference.
To fix this, just add a variable to store the reference to the widget like
form.submit(function (event){
event.preventDefault();
var description = $("textarea[name=description]", this).val();
var latitude = marker.getPosition().lat();
var longitude = marker.getPosition().lng();
var data = {
description : description,
latitude : latitude,
longitude : longitude
};
//new widget reference var (this here still refers to the widget)
var widget = this;
$.ajax({
type : "POST",
url : "/places",
data: {place: data},
beforeSend: function(x) {
x.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
},
success: function(x) {
//now use the var widget here
widget._closeFormFields(); // Works!
}
});
});