How to customize the OpenERP 6.1 WebClient HTML DOM? - javascript

The page of OpenERP web client can get really wide with many columns in a list view. On short screens, this is a trouble as the centered menu runs out of reach to the right with the content. I decided to find a quick fix for this: Make the menu align to left. With normal websites, this would be a piece of cake with standard JQuery, but this OpenERP web thing is pretty much completely generated in JS!
The generated HTML has the following structure for the menu:
<div class="menu" id="oe_menu">
<table align="left">
<tbody>
<tr>
<td>
<a href="#" data-menu="3">
Settings
</a>
</td>
<!--other menus...-->
</tr>
</tbody>
</table>
</div>
The way to go with JQuery is (tested in JS console):
$('div.menu table[align=center]').attr('align','left');
Though the usual $(document).ready() will fail because the time the DOM is loaded is only the initialization of the OpenERP web client.
My requirement is that this needs to be managed from a module. Simahawk got his answer for a similar topic - hooking into the logout event which pointed me in the right direction, but did not fix my task.

I found and modified a piece of code from the web_livechat module which finally worked. My module is called camara and that is important, because the new method of the openerp object must be called after your module - static/src/js/tweaks.js:
// openerp.camara(openerp) is executed when the module is loaded - UI is not rendered yet!
openerp.camara = function(openerp) {
// so we hook into (override) the Header do_update() call
// which is executed upon session validation by the WebClient
// we have to call the overriden parent method
// then we hook onto the update_promise
// which executes our code after the update is done.
openerp.web.Header.include({
do_update: function() {
var self = this;
this._super();
this.update_promise.then(function() {
// change menu alignment to the left
// because with wide views, it runs out of reach
$('div.menu table[align=center]').attr('align','left')
});
}
});
}
The js file needs to be included in the __openerp__.py:
'js': ["static/src/js/tweaks.js"]
Questions remaining:
Do you like this and find it an appropriate approach?
If not, please offer other solutions.
I myself find this rather clumsy, that's why I ask. I thought of using CSS but did not manage to override the table's align attribute.

Related

Multiple Partial view and javascript

I am trying to create a paginated report with multiple KPI in a table using MVC, C#,and JavaScript. In order to accomplish this, I created a partial view, with a view model to represent the KPI component. I have a small JavaScript file with a document ready function to set the color of the KPI component. I load them like this.
<td style="width:30px">
#Html.Partial("Element", new ElementReadings((decimal)item.NLow,
(decimal)item.NHigh, (decimal)item.N_PCT))
</td>
<td style="width:30px">
#Html.Partial("Element", new ElementReadings((decimal)item.NALow,
(decimal)item.NAHigh, (decimal)item.Na_PCT))
</td>
My problem is the java script only runs on the first row of table. I have tried including the java script file in the partial view (which loads the same file every time according to viewing the page source) but it does not seem to fire except on the first row. I even tried to include the java script directly in the cshtml file, again no love.
I am not well versed in web based programming, and looking for any advice. My next step in a windows app would be looping through all the controls on the form, getting their name or some property, then run a function against what I found. Not sure if that is possible with java script and the DOM (?). Guess I will find out.
cheers
bob
Edit here is the java script code.
$(document).ready(function () {
var high = $('#high').val();
var low = $('#low').val();
var actualValue = $('#actualValue').val();
if (high > actualValue) {
$('#HighTextBox').addClass("redBackgroud");
}
else {
if (low < actualValue) {
$('#LowTextbox').addClass("redBackgroud");
}
else {
$('#MediumTextbox').addClass("greenBackground");
}
}
})
I think Stephen is correct, and I do not fully understand how html is rendered. I solved the problem on the server side when assigning values to the text boxes. Thanks for everyone's help and comments.

Angular in a injected cshtml file, $compile not working

TL;TR; is below
I'm dealing with a huge application where everything is made super-generic to easily expend. One of the components is the dialog. Now before your answer is, use ngInclude or angular templates, let me explain how these work and why we would like to stick to them.
The flow of creating this dialog:
From somewhere in javascript a javascript function is called.
That constructs the container for the dialog. Position, widths, heights, gray background, etc.
Once that is present, a loading indicator will show up, while a GET request takes place to the back-end.
At the back-end Action, a view name is provided and a model.
This view (a .cshtml file) gets loaded into a string builder. To give you an idea of what happens, here is a piece of code where the view gets loaded.
var sb = new StringBuilder(1);
using (StringWriter sw = new StringWriter(sb))
{
.....
var helper = new HtmlHelper(viewContext, viewDataContainer);
using (helper.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
helper.RenderPartial(viewName, model);
}
return sb.ToString();
}
Then the string is returned, in javascript it is eventually set as html: diag.html(jsonResponse.data);
Now the view that I load, contains angular elements, like ng-controller and brackets to show something from that controller. Here is the .cshtml:
#model int
<div ng-controller="dialogGridColumnSelectionController as dgc" ng-init="dgc.init(#Model)">
<table>
<thead>
...
<tbody>
<tr ng-repeat="col in dgc.tableColumns">
<td>
<input type="checkbox" name="some" ng-value="dgc.hide"/>
</td>
<td>{{col.headerName}}</td>
<td>{{col.Description}}</td>
</tr>
</tbody>
</table>
</div>
A few statements:
ng-app was tried later, it should not be needed here because the <body> element already has this.
The dialog is within the <body> element.
No errors, no angular warnings.
{{col.headerName}} shows exactly as how it is shown, so angular is not working.
The controller and all the other required javascript files are already loaded on the page from where this dialog is opened. Also tried to add them here, no difference. I also tried to load angular there again, that does give me a warning that I tried to load it more than once. So the scripts are there.
Now I kind of doubted that this would work from the beginning, but I just want to make sure before we start a major rework of the dialogs.
So my question is, is it possible? Can a view that is initiated from .html() (in javascript) have angular? How do I "start" Angular? Or why is it not working with generated html?
TL;TR and EDIT:
After some digging, I eventually did this:
dialog.html(jsonResponse.data);//dialog is a created jquery element, jsonResponse.data contains the `.cshtml` content
if (options.angularCompile && options.angularScope) {//I've set them in options
options.angularCompile(dialog)(options.angularScope);//= $compile(dialog)($scope) or $compile(dialog.contents())($scope)
}
From the html above, it does fire the init function. That function correctly loads data (from other injected factories).
var self = this;
this.tablePageID = 0;
this.tableColumns = [];
$scope.hello = "hii";
this.init = function (tablePageID) {
self.tablePageID = tablePageID;
self.tableColumns = gridTableFactory.getTableColumns(tablePageID);
}
But once completed and the dialog is shown, it still has no angular working.
Worth noting is that the ng-repeat has done it job at the beginning, there are no items. Looks like it compiles and forgets.
Try executing $scope.$apply(); after using the $compile. You may need to inform angular to update its bindings. Also try to avoid using jQuery directly and use angular.element("#....") instead.

Configure a specific JavaScript function to reload on a page

I am trying to configure a JavaScript function to sort of 'reload' on a particular web page. The site has an overarching JS and CSS framework, but I just want this particular function to load a bit differently.
The JS function in the JS framework sets the height of a grid element dynamically. For example, if I have three elements that have a height of 155px, 34px, and 100px, it takes the element with the highest value and applies that height to all of the grid elements, making them all 155px.
For the rest of the pages in the site, the code is fine because the JS script executes on page load. However, the page I'm using the function on doesn't seem to adhere to the JS script. It still retains differing heights when it is supposed to have the same height.
What's different about this particular page is the application coded in it. There is a filtering application in the page that features a Search text box and a checkbox similar to Amazon's application filters for categories like Men's or Women's clothing. However, unlike Amazon, checking/unchecking the checkboxes or typing something else in the Search box simply loads/reloads the 'content' but the page doesn't actually reload and the content are simply links. The links are loaded from a separate XML file.
The links in this case are the grid elements. They are set up to look like interactive buttons.
Here is the code for the JS function:
(function ($, window, document, undefined ) {
'use strict';
// add initialisation
this.addInitalisation('equal-heights', function() {
this.debug('Module: Equal Heights');
var font = new FontFaceObserver('BentonSansRegular');
font.load().then(function () {
// console.log('Font is available');
$(".grid > .grid-item").matchHeight();
});
});
// Register UI module
this.UIModule({
module: 'equal-heights',
init: function() {
this.initialize('equal-heights');
}
});
})( jQuery, window, window.document );
I didn't really upload a whole lot of the code because I am seeking to understand on how to solve this problem conceptually (provided that I was clear in my explanation of the issue).
To iterate my goal, I want to load the grid items in this particular web page to be the same height even after clicking/typing through the filter application.
Please let me know if I can clarify anything else.
This answer has been solved. What I did (what worked) was take this piece of code: $(".grid > .grid-item").matchHeight(); and place it in the JavaScript file that the particular web page used. This code is what sets the element height dynamically.

Angular issue inheriting scripts?

This may seem like an obscure question, but I am having issues with my angular inheriting scripts via an invoke script.
The generalization for this would be as follows:
We have a custom browser that will create a button when a custom "property" is added to an input object on our webpage(where the angular resides). When that property is added (similar to class and ID), it will put a button next to the Input object.
The issue I am having is that it will only work on objects outside of the ng-view.
The code works fine if the input object is located in my main index html file (before ng-view is called).
Is there a way to make it so that the custom scripts can be inherited in, what I believe, is the controllers?
Any tips are greatly appreciated.
EDIT: Here are some code snips that are currently being used to make this functionality possible.
The Browser Side:
string invokeScript = "(function(){function i(n,t){t.parentNode.insertBefore(n,t.nextSibling)}function r(n,t){var o={scannableInputId:n.id,scannerType:t},u=document.createElement(\"span\"),f=document.createAttribute(\"class\");f.value=\"input-group-btn\";u.setAttributeNode(f);var r=document.createElement(\"button\"),s=document.createTextNode(\"scan\"),e=document.createAttribute(\"class\");e.value=\"btn btn-primary\";r.appendChild(s);r.setAttributeNode(e);r.addEventListener(\"click\",function(){window.external.notify(JSON.stringify(o))});u.appendChild(r);i(u,n)}var n=document.querySelectorAll(\"[data-barcode-scan]\");for(var t in n)n.hasOwnProperty(t)&&r(n[t],n[t].getAttribute(\"data-barcode-scan\").toLowerCase())})();";
if (Web != null)
{
await Web.InvokeScriptAsync("eval",
new[]
{
invokeScript
});
}
The Website Side:
<h2>
Item (Demo): <input id="scannedValueDemoTextBox"
name="scannedValueDemoTextBox"
ng-model="scannedValueDemo"
type="text"
data-barcode-scan="single"
style="width: 200px"/>
The "data-barcode-scan="single" is what should trigger the button to appear, which works outside the ng-view/controllers.

User control with a client side API

Maybe I've picked a totally inappropriate/bad example.
What I have is a user control that contains a bunch of dynamically created Telerik RadGrids.
My user control is added to a couple of Telerik RadPageViews that are part of a RadMultiPage that is used alongside a RadTabStrip.
What I need to do is call a Javascript function in my usercontrol to update it's display whenever it's parent RadPageView is selected.
So basically I have the following Javascript code:
function OnClientTabSelected(sender, args)
{
// Get the MyControl that is on this tab
var myControl = $find("whatever");
// Call a method that updates the display
myControl.doSomething();
}
Thanks,
David
You can add a wrapper div in your User Control and then extend that div using jQuery to add your desired methods and properties. The trick is to set the div's id='<%=this.ID%>' - that way the div has the same ID as the User Control (which is fine because the User Control doesn't actually render anything - only its contents).
Then back on your containing page, you can just reference your UserControl's ID using $get('whatever') - and you'll actually select your extended div.. which will have all your methods and properties on it.
The nice thing about this approach is that all of your methods and properties and neatly scoped and nothing is in the global namespace.
I have a full demo solution and details here if you want more info:
http://programmerramblings.blogspot.com/2011/07/clientside-api-for-aspnet-user-controls.html
Just make a call to javascript method in input button if you are sure about the name of that function.
<input type="button" value="test" onclick="doSomething()" />
If you place any javascript code in the control that will be spit on the page and it will be available for calling provided both of them are in the same form.
for example your code will look like this if you look into the source of that page.
<script type="text/javascript">
function doSomething()
{
alert(new Date());
}
</script>
<div>
<span id="MyControl1_Label1">Dummy label</span>
</div>
<hr />
<input type="button" value="test" onclick="doSomething()" />
Edit: This is not a good way to access these methods in my opinion. When you are putting some javascript code inside a control then it should be used in that control only (There is no rule as such, its just a design suggestion). If you are trying to access javascript code of a control from outside that control then you need to revisit your design.
If you can give us more details on why you want to access that method, may be we can suggest some better way to do that.
Update: (As you have modified your question): Bit tricky to answer this as I dont have hands on experience with Rad controls. I guess there should be some feature which will help you to update the controls in that page without using javascript, may be have a look at clientside-API provided for Rad.
May be somebody who knows about RAD controls will help you.
You should make the control method public
public void doSomething
and call this from the page
myControl1.doSomething();

Categories

Resources