Multi language support in Hot towel(Durandal framework) - durandal

I'm in the process of creating a application using Hot Towel, which supports multiple language (eg. english, french)
I have the referred the following links
Translating Views
Durandal localization example
And my question is how can i render views of application based on user language. If user selects english, the complete application should display contents in english. How to achieve this
I have tried something like in the second link.
define({
'root': {
welcome: 'Welcome!',
flickr: 'Flickr'
},
'fr-fr': true,
'es-es': true,
});
Is this i have to create seperate views for languages or i have to create seperate App folder for contents
Suggest me some best practices.

I don't recommend using separate views or separate folders if your project is a big one.
You can just create a file of the labels and if you use lib like knockout just data-bind these labels once (text: xxxx). In addition you can use i18n to manage labels.
With selected language just load the specific language file to your viewmodel.
EDIT1:
I'd never encountered a complete sample nor tutorial. So how I do is to :
use tools like i18n to get the key-paired dictionary file for labels in html and in some javascript code like messages.
then manually I indexed these labels by augmenting knockout viewmodels for views and variables for modules.
This is my last option in waiting for better solution. Hope this can help!

you can do some thing like this . YOu can change the APP folder if you are need do lot of locale changes you can use the following method
#{
string strAcceptLanguage;
strAcceptLanguage = System.Configuration.ConfigurationManager.AppSettings["Locale"].ToString();
if (strAcceptLanguage == "en-us")
{
#Scripts.Render("~/Scripts/vendor.js")
<script type="text/javascript" src="~/Scripts/require.js" data-main="en-US/main"></script>
}
else if (strAcceptLanguage == "es-es")
{
#Scripts.Render("~/Scripts/vendor.js")
<script type="text/javascript" src="~/Scripts/require.js" data-main="en-UK/main"></script>
}
else if (strAcceptLanguage == "fr-fr")
{
#Scripts.Render("~/Scripts/vendor.js")
<script type="text/javascript" src="~/Scripts/require.js" data-main="AUZ/main"></script>
}
}
in the Index.cshtml you can use the above code and for the you need to have value in Webconfig file
<add key="Locale" value="en-us" />
and in the SPA page each time when the user try to change the locale by pressing button or any kind of option you have to trigger a event to call a AJAX post to assess a REST api to update the Given Locale Value in the webconfig file
changeLocale: function (val) {
var name = JSON.stringify({
locale: val
});
$.ajax({
cache: false,
url: "http://localhost:49589/api/Locale",
type: "POST",
dataType: "json",
data: name,
contentType: "application/json; charset=utf-8",
processData: false,
success: function (json) {
alert(json);
location.reload();
},
error: function (json) {
alert("error" + JSON.stringify(json));
}
});
you have to write the above code in the shell.js and the shell.html has the following code
<div class="btn-group" data-toggle="buttons-radio">
<button type="button" class="btn btn-primary" data-bind="click: changeLocale.bind($data, 'en-us')">English</button>
<button type="button" class="btn btn-primary" data-bind="click: changeLocale.bind($data, 'es-es')">French</button>
<button type="button" class="btn btn-primary" data-bind="click: changeLocale.bind($data, 'fr-fr')">Japanese</button>
</div>
And the rest api like this
[HttpPost]
public string ChangeLocale(Locale l)
{
ConfigurationManager.AppSettings["Locale"] = l.locale;
return "success";
}
hope this will help

Related

Custom widget js doesn't recognize template from qweb

I try to test custom widget from js reference and I get error in debugger:
Error: QWeb2: Template 'some.template' not found
qweb.xml was properly set in manifest, because when I extend ListController and use another template, it works correctly.
Here is template definition, which I use in qweb.xml:
<?xml version="1.0" encoding="UTF-8"?>
<template>
<div t-name="some.template">
<span class="val"><t t-esc="widget.count"/></span>
<button>Increment</button>
</div>
</template>
I tried to change <template> -> <templates>, totally removed tag "template" but still get the same error message.
JS:
odoo.define('working.test', function (require) {
var Widget = require('web.Widget');
var Counter = Widget.extend({
template: 'some.template',
events: {
'click button': '_onClick',
},
init: function (parent, value) {
this._super(parent);
this.count = value;
},
_onClick: function () {
this.count++;
this.$('.val').text(this.count);
},
});
// Create the instance
var counter = new Counter(this, 4);
// Render and insert into DOM
counter.appendTo(".o_nocontent_help");
})
Manifest:
# -*- coding: utf-8 -*-
{
'name': "testwidget",
'summary': """
Short (1 phrase/line) summary of the module's purpose, used as
subtitle on modules listing or apps.openerp.com""",
'description': """
Long description of module's purpose
""",
'author': "My Company",
'website': "http://www.yourcompany.com",
# Categories can be used to filter modules in modules listing
# Check https://github.com/odoo/odoo/blob/12.0/odoo/addons/base/data/ir_module_category_data.xml
# for the full list
'category': 'Uncategorized',
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['base'],
'qweb': ['static/qweb.xml'],
# always loaded
'data': [
# 'security/ir.model.access.csv',
'views/views.xml',
'views/web_asset.xml',
],
# only loaded in demonstration mode
'demo': [
'demo/demo.xml',
],
}
Any idea how I need to modify this template to make the widget working correctly and in which table in db odoo stores these templates?
I was running into this same issue and needed to put my QWeb code into static/src/xml/base.xml in order for Odoo to recognize it.
You can check to see if Odoo is loading the QWeb by going to this URL on your Odoo instance:
<odoo_instance>/web/webclient/qweb?mods=<my_module_name>
Such as:
localhost:8069/web/webclient/qweb?mods=test
For comparison, you can see a successful output by using mods=web to load the QWeb assets for the web module.
You can try changing
'qweb': ['static/qweb.xml'],
to
'qweb': ['static/*.xml'],
It happens with me sometimes, by specifying static xml file name, it does not render that template. But by just loading all .xml files by using *, templates are loaded.
To solve this issue I used as workaround Widget.xmlDependencies:
xmlDependencies: ['/test/static/qweb.xml']
but the main reason I think was cache in PyCharm which I didn't invalidate.
After having done some code reading, IMO, I realized the official documentation might not have pointed out clearly how to use templates in frontend.
To summarize my understanding:
The 'qweb' field in manifest is mainly designed for webclient (i.e. the backoffice), not the website. When entering webclient, a request to /web/webclient/qweb is made to retrieve all the templates of installed modules.
In order to use templates in website (i.e. frontend), synchronous and asynchronous ways both exist.
Synchronous way: Use qweb.add_template. When parameter is template content itself or a DOM node, template is loaded in a synchronous way. (While param is a URL, then it fires up an ajax request to server to fetch content.)
qweb.add_template is mentioned in https://www.odoo.com/documentation/13.0/reference/qweb.html
Asynchronous way:
Use ajax.loadXML which you can use anywhere you want to start loading template from a URL.
Use xmlDependencies which you specify in widget definition. And if you dig into the code in widget.js, you can see ajax.loadXML is being used in willStart.
There are discussions regarding qweb.add_template vs ajax.loadXML
See https://github.com/OCA/pylint-odoo/issues/186 and https://github.com/odoo/odoo/issues/20821
FYI.
I guess you may need to make sure that the js definition refers to the module name correctly
odoo.define('MODULE TECHNICAL NAME SHOULD BE HERE.test', function (require) {});
you should also register your js function with something like:
core.action_registry.add("module_name.name", Widget_Extend);
for more info https://www.odoo.com/documentation/11.0/reference/javascript_reference.html#registries
In Odoo 14 make sure
dashboard.js
odoo.define('library_managment.dashboard', function(require) {
"use strict";
// alert("hello odoo...............")
console.log("Hello My Module........!!")
var widgetRegistry = require('web.widget_registry');
var Widget = require('web.Widget');
var Counter = Widget.extend({
template: 'library_managment.template',
xmlDependencies: ['/library_managment/static/src/xml/template.xml'],
events: {
'click button': '_onClick',
},
init: function (parent, value) {
this._super(parent);
this.count = 4*9+5;
console.log("parent is", parent)
console.log("counter is..", this.count)
},
_onClick: function () {
this.count++;
this.$('.val').text(this.count);
},
});
widgetRegistry.add('library_counter', Counter);
return Counter;
});
template.xml
add this
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<div t-name="library_managment.template">
<span class="val">
<t t-esc="widget.count"/>
</span>
<button class="bg-danger">Increment</button>
</div>
</odoo>
then add js file in assets.xml inside youe views
<odoo>
<template id="assets_backend" name="Library assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/library_managment/static/src/js/dashboard.js"></script>
</xpath>
</template>
</odoo>
then add in manifest like this:
'js': ['/static/src/js/dashboard.js'],
'qweb': ['/static/src/xml/template.xml']
then inside form view add this line
<widget="library_counter"/>
I had the same problem but with "hr_org_chart" template idk why everything works fine in another computer but in mine it returned this problem, I solved it by installing this module hr-org-chart

#Ajax.ActionLink in ASP.NET CORE [duplicate]

Earlier in MVC I used #Ajax.ActionLink for Ajax call and replaced container in my layout.
Now in .Net Core there is anything like AjaxHelper back then.
How can I form Ajax call without writing jquery script for every menu item in my dashboard.
I tried #Url.Action with anonymous Ajax parameters but that won't work.
#Url.Action("Index", "User", new
{
data_ajax = "true",
data_ajax_method = "GET",
data_ajax_mode = "replace",
data_ajax_update = "#replace"
}))"
No. Honestly, you never needed it anyways. All it did was create a regular link and then add a trivial bit of JavaScript you can easily add yourself. Long and short, let it go:
<a class="menu-item" asp-action="Index" asp-controller="User">Click Me</a>
Then:
<script>
$('.menu-item').on('click', function (e) {
e.preventDefault();
$.get(this.href, function (html) {
$('#replace').html(html);
});
});
</script>
By binding to the class, you only need this one piece of JS for any link with a menu-item class.

Google script HTML api can´t process file upload field

I am trying to create a simple web app in google scripts with the HTML api.
code.gs
function doGet() {
return HtmlService.createHtmlOutputFromFile('index');
}
function processForm(formObject) {
var formBlob = formObject.myFile;
var driveFile = DriveApp.createFile(formBlob);
return driveFile.getUrl();
}
index.html
<script>
function updateUrl(url) {
var div = document.getElementById('output');
div.innerHTML = 'Got it!';
}
</script>
<form id="myForm">
<input name="myFile" type="file" />
<input type="button" value="Submit"
onclick="google.script.run
.withSuccessHandler(updateUrl)
.processForm(this.parentNode)" />
</form>
<div id="output"></div>
The form fails to submit. I´m using google chrome Versión 30.0.1599.101 m
This appears in the console: Uncaught NetworkError: Form submission failed.
Here is the app: https://script.google.com/d/1yrgM20n1ZI99bChN2qtQWgGck36OccLN3A16Gn7tCPvsJw0EcK_ql7C5/edit?usp=sharing
Maybe you should add encoding="multipart/form-data" attribute to form tag.
If not solved already – did you try changing the input type from "button" to "submit"? On top of that I'd also try giving it another value than "Submit" since that might interfere with the actual submit parameter.

how to pass file from client to controller by jquery?

i want to create very simple sample to uploading a file by client side in mvc4 by jquery and java script.
i Google it and found many samples and many plugins on internet but i prefer to do not have any dependency on any extra plugin or library like "uploadify"
for this i create a simple mvc4 application and in my view i attach my script file that contains method bellow until user click a button on this view start to uploading.
i do not know how to change bellow method to pass file to controller(in client side) ?
function uploadimage() {
$.ajax({
url: "/Uploader/FileUpload",
type: 'POST',
dataType: 'json',
data:null,
contentType: 'application/json; charset=utf-8',
success: function (msg) {
},
error: function (xhr) {
}
});
}
in my view
<input type="file" id="fileToUpload" name="file" />
<input type="button" value ="Upload" onclick="uploadimage()"/>
my controller
public ActionResult FileUpload(HttpPostedFileBase file)
{
//do somethings with file
}
file upload is not possible through ajax. You can upload file, without refreshing page by using IFrame.

How can I use cshtml files with Durandal?

I got the DurandalJS StarterKit template on VS2012... All works great...
But in some views I need to do something like that:
#if (Roles.IsUserInRole("Administrators"))
{
<p>Test</p>
}
However with durandal all my views are '.html' files... Is that possible to use '.cshtml' files to access some information like that?
Or is there any other way to do that with durandal?
Junior
I am doing it like this:
Create a generic controller for Durandal views:
public class DurandalViewController : Controller
{
//
// GET: /App/views/{viewName}.html
[HttpGet]
public ActionResult Get(string viewName)
{
return View("~/App/views/" + viewName + ".cshtml");
}
}
Register a route:
routes.MapRoute(
name: "Durandal App Views",
url: "App/views/{viewName}.html",
defaults: new { controller = "DurandalView", action = "Get" }
);
Copy Views/web.config to /App/views/web.config (so Razor views work in this location).
This lets me use the normal Durandal conventions (even the html extension for views), and put durandal views as cshtml files in their normal location without adding any more server code.
If you also have static html views, you can also place the cshtml views in a subfolder or use the normal MVC /Views folder.
I wouldn't recommend using ASP.NET MVC with Durandal.
What you are probably looking to do is use the Razor view engine (to get the benefits of a compiler, strong typing etc.) which exists independently from ASP.NET MVC. Just WebAPI for data I/O is more than enough to very efficiently create a Durandal.js application.
If you are interested in using Razor/CSHTML with Durandal and Knockout there is an open source option out there called FluentKnockoutHelpers that may be exactly what you are looking for. It offers much of the 'nice' parts of ASP.NET MVC allowing you to use the awesome abilities of Durandal and Knockout with almost no downfalls.
Source
Live demo using Durandal.js
In a nutshell it provides a bunch of features which makes doing Durandal/Knockout development just as easy as ASP.NET MVC. (You simply provide a C# type that your JavaScript model is based off of for most of the features.) You only have to write JavaScript and un-compiled markup for complicated cases which is unavoidable and no different than MVC! (Except in MVC your code would also likely end up would also be a big jQuery mess which is why you are using Durandal/Knockout in the first place!)
Features:
Painlessly generate Knockout syntax with strongly typed, fluent, lambda expression helpers similar to ASP.NET MVC
Rich intellisense and compiler support for syntax generation
Fluent syntax makes it a breeze to create custom helpers or extend whats built in
OSS alternative to ASP.NET MVC helpers: feel free to add optional features that everyone in the community can use
Painlessly provides validation based on .NET types and DataAnnotations in a few lines of code for all current/future application types and changes
Client side JavaScript object factory (based on C# types) to create new items in for example, a list, with zero headaches or server traffic
Example without FluentKnockoutHelpers
<div class="control-group">
<label for="FirstName" class="control-label">
First Name
</label>
<div class="controls">
<input type="text" data-bind="value: person.FirstName" id="FirstName" />
</div>
</div>
<div class="control-group">
<label for="LastName" class="control-label">
Last Name
</label>
<div class="controls">
<input type="text" data-bind="value: person.LastName" id="LastName" />
</div>
</div>
<h2>
Hello,
<!-- ko text: person.FirstName --><!-- /ko -->
<!-- ko text: person.LastName --><!-- /ko -->
</h2>
Provide FluentKnockoutHelpers with a .NET type and you can do this in style with Intellisense and a compiler in Razor / CSHTML
#{
var person = this.KnockoutHelperForType<Person>("person", true);
}
<div class="control-group">
#person.LabelFor(x => x.FirstName).Class("control-label")
<div class="controls">
#person.BoundTextBoxFor(x => x.FirstName)
</div>
</div>
<div class="control-group">
#person.LabelFor(x => x.LastName).Class("control-label")
<div class="controls">
#person.BoundTextBoxFor(x => x.LastName)
</div>
</div>
<h2>
Hello,
#person.BoundTextFor(x => x.FirstName)
#person.BoundTextFor(x => x.LastName)
</h2>
Take a look at the Source or Live Demo for an exhaustive overview of FluentKnockoutHelper's features in a non-trivial Durandal.js application.
Yes, you can absolutely use cshtml files with Durandal and take advantage of Razor on the server. I assume that also means you want MVC, so you can do that too and use its routing.
If you don;t want the routing then you can set the webpages.Enabled in the web.config, as the other comments suggest.
<add key="webpages:Enabled" value="true" />
I don't recommend that you use .cshtml files as views directly. You're better off placing the .cshtml files behind a controller.
For example, take the HotTowel sample, edit /App/main.js, and replace the function definition with the following:
define(['durandal/app',
'durandal/viewLocator',
'durandal/system',
'durandal/plugins/router',
'durandal/viewEngine',
'services/logger'],
function (app, viewLocator, system, router, viewEngine, logger) {
Note that we added a reference to the Durandal viewEngine. Then we need to replace
viewLocator.useConvention();
with
viewLocator.useConvention('viewmodels', '../../dynamic');
viewEngine.viewExtension = '/';
The first argument to viewLocation.useConvention sets the /Apps/viewmodels/ directory as the location for the view models js files, but for the view location, uses the URL http://example.com/dynamic/, with an extension of '/'. So that if Durandal is looking for the view named 'shell', it will reference http://example.com/dynamic/shell/ (this is because the view directory is mapped relative to the viewmodels directory, hence /App/viewmodels/../../dynamic will give you simply /dynamic).
By convention, this previous URL (http://example.com/dynamic/shell/) will be mapped to the controller DynamicController, and the action "Shell".
After this, you simply add a controller - DynamicController.cs, like this:
// will render dynamic views for Durandal
public class DynamicController : Controller
{
public ActionResult Shell()
{
return View();
}
public ActionResult Home()
{
return View();
}
public ActionResult Nav()
{
return View();
}
public ActionResult Details()
{
return View();
}
public ActionResult Sessions()
{
return View();
}
public ActionResult Footer()
{
return View();
}
}
Create .cshtml files for each of the above actions. This way you get to use controllers, server side IoC et al to generate dynamic views for your SPA.
DurandaljS is a client framework which forms mainly a solid base for single-page apps (SPA).
I assume you are using asp.net web API as your server technology. In that case, you can determine the user's role inside your API controller and based on that return data to the client. On the client you can use Knockout "if" binding in order to show / hide certain areas of your page.
What you perhaps can do is placing this code in the Index.cshtml.
Following link shows how to customize moduleid to viewid mapping
http://durandaljs.com/documentation/View-Location/
by convention durandal tries to find view url in following steps
1) Checke whether object has getView() function which returns either dom or a string ( url for the view)
2) If object does not have getView function then checks whether object has viewUrl property
3) If above two steps fails to produce url or a DOM view drundal falls to default convention
which maps moduleid xyz.js to view xyz.html using view url ( path of Views folder ) defined in main.js
so for moduleid xyz.js path of the view will be views/xyz.html
you can overwrite this default mapping behavior by overwriting convertModuleIdToViewId function.
So there are many ways you can customize your view url for specific model (.js object)
I made an extension to Durandal which gives you the ability to place an applicationContent div in your cshtml file together with the applicationHost div. In applicationContent you can now use both ASP .Net MVC syntax together with knockout bindings.
Only thing I did was put some extra code in the viewLocator.js file which looks for an applicationContent div:
locateViewForObject: function(obj, area, elementsToSearch) {
var view;
if (obj.getView) {
view = obj.getView();
if (view) {
return this.locateView(view, area, elementsToSearch);
}
}
if (obj.viewUrl) {
return this.locateView(obj.viewUrl, area, elementsToSearch);
}
view = document.getElementById('applicationContent');
if (view) {
return this.locateView(view, area, elementsToSearch);
}
var id = system.getModuleId(obj);
if (id) {
return this.locateView(this.convertModuleIdToViewId(id), area, elementsToSearch);
}
return this.locateView(this.determineFallbackViewId(obj), area, elementsToSearch);
},
Your original cshtml file can now do something like this:
<div class="row underheader" id="applicationContent">
<div class="small-5 columns">
<div class="contentbox">
#using (Html.BeginForm("Generate", "Barcode", FormMethod.Post, Attributes.Create()
.With("data-bind", "submit: generateBarcodes")))
{
<div class="row formrow">
<label for="aantalBijlagen">#Translations.Label_AantalBijlagen</label>
</div>
<div class="row">
<select name="aantalBijlagen" class="small-6 columns">
<option>0</option>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
</select>
</div>
<div class="row">
<button class="button right" type="submit" id="loginbutton"><span class="glyphicon glyphicon-cog"></span> #Translations.Action_Generate</button>
</div>
}
</div>
</div>
<div class="small-7 columns" data-bind="if: hasPdfUrl">
<div class="contentbox lastcontent">
<iframe data-bind="attr: {src: pdf_url}"></iframe>
</div>
</div>
You can find my fork of the durandal project here and a small blogpost of what and how I did this here.
I'm not very familiar with DurandalJS but because it's a client-side system, it should make no difference what technology is used on the server to generate the HTML markup. So if you use Razor CSHTML files to generate the HTML on the server, DurandalJS should work just fine with it.
If you're getting a particular error then please share that error, but I can't think of any reason why it wouldn't work.