Background: There's a table from which I can choose employees. I want to filter these employees by name.(I know name is not a good way to filter, this is just an example)
Basically I have a drop down from which I choose one of the filters.
My declaration: $scope.filters = null;.
I also have this deceleration to choose my filter $scope.chosenFilter= null;.
I use the following to retrieve the different filters I have $scope.filters = retrieveFilters(info.Names);
retrieveFilters looks like the following:
var retrieveFilters = function(rows) {
var filterWrapper = document.querySelector(".filterWrapper");
var datasetOptions = [];
$scope.predicate = 'Name';
if (rows) {
//Add Default option
datasetOptions.push({
name: $scope.data.Fnames.defaultFilterOptionLabel,
value: ''
});
$scope.chosenFilter = datasetOptions[0];
_.forEach(rows, function(ele) {
datasetOptions.push({
name: ele,
value: ele
});
});
} else {
filterWrapper.style.display = "none";
}
return datasetOptions;
};
I use the following to choose my filter:
$scope.$watch('chosenFilter', function() {
var filterSearchInput = document.querySelector(".filterWrapper input");
ng.element(filterSearchInput).triggerHandler('input');
});
Everything is fine and the display works on first load as I have set the default with
//Add Default option
datasetOptions.push({
name: $scope.data.Fnames.defaultFilterOptionLabel,
value: ''
});
From the default table whenever I click on an employees name hes details are displayed. However whenever I filter and click on the employees name, nothing is displayed. Whenever I click on a specific employees name at the default table and then filter in the same name the information also shows up, as I cache it each time.
I assume that you're displaying this data somewhere in your GUI using ng-repeat. Angular has a lot of great built-in features for this. Check out the answer here: AngularJS custom search data by writing custom filter for a way to approach this more from an Angular direction. You also might want to check out this question and answer: "Thinking in AngularJS" if I have a jQuery background?.
I'm working on a GUI website that can use several languages. The original HTML-files I got to work with were totally static. So if translation was needed I had to parse through alle files, note where some words or terms were, collect them all hand them to the translation department and enter those translations in the new language files.
Since those files were totally static it meant having to translate whole sections several times. Not very effictient.
So now I am working on some kind of dictionary in Javascript, to just exchange the terms in those websites. Mostly it works this way:
var dicEnglish = {
term 1: "This is the English text"
Ref: "Another English text"
}
var dicFrench = {
term 1: "This is the French text"
Ref: "Another French text"
}
Which contains all the possible content that needs to be changed. Every candidate in the HTML-code gets a class="dicRef" id="l_dicTag_#"as identifier, which I slice down to the dictionary tag and exchange with the following code:
var imgSrc = "en";
var ActiveDic;
var langSel;
if(window.name){
langSel=window.name;
}
else{langSel="English";
}
function LangChange(){
langClass = document.getElementsByClassName("dicRef");
var i = langClass.length;
var Start, Stop, idSrc, idDic;
var navText;
switch(langSel){
case "French":
langSel="French";
imgSrc = "en";
navText="Anglais";
break;
case "English":
case "Anglais":
default:
langSel="English";
imgSrc = "fr";
navText="French";
break;
}
ActiveDic="dic"+langSel;
window.name=langSel;
while(i--){
idSrc = langClass[i].id;
Start=idSrc.indexOf("_")+1;
Stop=idSrc.lastIndexOf("_");
idDic=idSrc.slice(Start,Stop);
if(window[ActiveDic][idDic]){
document.getElementById(idSrc).innerHTML=window[ActiveDic][idDic];}
else{
document.getElementById(idSrc).innerHTML="N/A";
}
}
if(document.getElementById("imgSel")){
document.getElementById("imgSel").src="../../img/"+imgSrc+".gif";
}
if (document.getElementById("l_SelLang1_1")){
document.getElementById("l_SelLang1_1").innerHTML=navText;
}
}
The problem lies in the uniqueness of the id-tag. Since some terms can occur more than once and some are generated the counter is needed. I'd prefer to ommit the counter, but can't find any other identifier to sort out all target terms and change their content.
Since I want to be safe for the future I'd prefer a solution that makes it possible to handle a possible third language. Working with the inner HTML would need to tag the same term several times, once for each language.
So is there any way to target all terms to be exchanged more efficently and easily, or a better way to do it? I can only work with client-side solutions, so no PHP and so on.
No offense to the other answerers but storing the text in JavaScript or in data attributes is not good for search engines or disabled site visitors and offers no benefits while added unnecessarily complicated code. The best and most simple solution in my opinion is to make use of HTML lang attribute and use JavaScript to show and hide the desired language. This solution also gracefully degrades so if a site visitor has their JavaScript disabled it will still display the content. Here is my solution:
HTML
<button id="switch-lang">Switch Language</button>
<h1><span lang="en">Hello</span> <span lang="es">Hola</span></h1>
<p lang="en">I really enjoy coding.</p>
<p lang="es">Me gusta mucho la codificación.</p>
jQuery
$('[lang="es"]').hide();
$('#switch-lang').click(function() {
$('[lang="es"]').toggle();
$('[lang="en"]').toggle();
});
Then I would recommend adding HTML5 Geolocation to determine which language to show initially based on the users location in the world. I would also use Fontawesome language icon to show users they can switch languages in a way that is understandable by anyone: http://fontawesome.io/icon/language/
Here is the working code example at CodePen: https://codepen.io/codepajamas/pen/ZejaQz?editors=1010
Here is an additional example on code pen using a select menu to change between 3
(or more) languages: https://codepen.io/codepajamas/pen/NjGOMV
Updated Full Example with Geolocation and Cookies
I kept working on this and created an updated example switching between two languages Chinese and English (if you need more than two languages you would have to hide all languages and show only the one selected instead of using toggle the way I am). This code also detects if an existing cookie is already set for the language using jQuery Cookie. It also checks their geolocation if their browser supports it automatically setting the language to Chinese if they are in either Taiwan or China and defaults to English in all other countries. The code below is commented so you can see what each step is doing and hopefully be able to modify it to suit your needs. Here it is:
HTML
<button id="switch-lang">Switch Language Icon Here</button>
<h1><span lang="en">Hello</span> <span lang="zh">你好</span></h1>
<p lang="en">I really enjoy coding.</p>
<p lang="zh">我真的很喜歡編碼。</p>
jQuery
Note: this requires linking to not only jQuery but also jQuery Cookie
$(function () {
///// Language Switching (2 languages: English and Chinese). /////
// Initially disable language switching button.
$('#switch-lang').css({'pointer-events':'none',
'cursor':'default'}).attr('disabled','disabled');
function langButtonListen() {
$('#switch-lang').click(function (event) {
event.preventDefault();
$('[lang="zh"]').toggle();
$('[lang="en"]').toggle();
// Switch cookie stored language.
if ($.cookie('lang') === 'en') {
$.cookie('lang', 'zh', { expires: 7 });
} else {
$.cookie('lang', 'en', { expires: 7 });
}
});
// Enable lang switching button.
$('#switch-lang').css({'pointer-events':'auto',
'cursor':'pointer'}).removeAttr('disabled');
}
// Check if language cookie already exists.
if ($.cookie('lang')) {
var lang = $.cookie('lang');
if (lang === 'en') {
$('[lang="zh"]').hide();
langButtonListen();
} else {
$('[lang="en"]').hide();
langButtonListen();
}
} else {
// no cookie set, so detect language based on location.
if ("geolocation" in navigator) {
// geolocation is available
navigator.geolocation.getCurrentPosition(function (position) {
// accepted geolocation so figure out which country
var lat = position.coords.latitude,
lng = position.coords.longitude;
$.getJSON('http://maps.googleapis.com/maps/api/geocode/json?latlng='+lat+','+lng+'&sensor=true', null, function (response) {
var country = response.results[response.results.length-1].formatted_address;
if (country === 'Taiwan' || country === 'China') {
$('[lang="en"]').hide();
$.cookie('lang', 'zh', { expires: 7 });
langButtonListen();
} else {
$('[lang="zh"]').hide();
$.cookie('lang', 'en', { expires: 7 });
langButtonListen();
}
}).fail(function (err) {
console.log('error: '+err);
$('[lang="zh"]').hide();
$.cookie('lang', 'en', { expires: 7 });
langButtonListen();
});
},
function (error) {
if (error.code == error.PERMISSION_DENIED) {
// denied geolocation
$('[lang="zh"]').hide();
$.cookie('lang', 'en', { expires: 7 });
langButtonListen();
} else {
console.log('Unknown error. Defaulting to English!');
$('[lang="zh"]').hide();
$.cookie('lang', 'en', { expires: 7 });
langButtonListen();
}
});
} else {
// geolocation IS NOT available
$('[lang="zh"]').hide();
$.cookie('lang', 'en', { expires: 7 });
langButtonListen());
}
}
});
You can use data attributes: the fact that "HTML5 attributes are not supported in IE6 and IE7" means that you don't get the getAttribute() method or the dataset property for retrieving/accessing them. But you can still retrieve them as explained in this post.
<div id="geoff" data-geoff="geoff">
var geoff = document.getElementById("geoff");
alert(geoff.getAttribute("data-geoff"));
Even better, you can use jQuery .data() to support previous versions of IE.
Something along these lines should work:
<div data-translate="translation_key"></div>
$("[data-translate]").each(function(){
var key = $(this).data('translate');
$(this).html(dictionary[key][current_lang] || "N/A");
});
Working example: https://jsfiddle.net/x93oLad8/4/
One of the ways around this might be to use some sort of client-side templating system for your interface. That way you don't need to unnecessarily load your HTML with a bunch of data attributes detailing the language requirements, but just describe it once in the JavaScript and use a couple of functions to assist with the translation. I've coded up quick example below to show you what I mean.
Here's the dictionary object. It contains all the translations by country code. This means you don't need separate dictionaries for each country. This is important because it means we can use this single object structure very easily in out translation function as you'll see in a moment. It also means you can add as many languages and translations as you like.
var dict = {
en: {
'Hallo': 'Hallo',
'Goodbye': 'Goodbye',
'castle': 'castle'
},
fr: {
'Hallo': 'Bonjour',
'Goodbye': 'Au revoir',
'castle': 'chateau'
},
de: {
'Hallo': 'Hallo',
'Goodbye': 'Auf Wiedersehen',
'castle': 'schloss'
}
}
This is our country code and it relates directly to the country code key in our dictionary object:
var lang = 'fr';
The first of our two functions. This takes a template and a language and performs the translation, returning whatever's left (usually some sort of HTML as in our example).
function applyTemplate(tmpl, lang) {
// find all words within {{word}} a double set of curly braces
// (this format is similar to the handlebars templating engine)
var regex = /\{\{([a-zA-Z])\w+\}\}/g
// for each found word perform the translation and
// remove the curly braces
return tmpl.replace(regex, function (word) {
return translate(dict, lang, word.replace(/[\{\}]/g, ''));
});
}
The translate function takes the dictionary, the language, and a word and returns the translated word. Note that this is much easier with one object containing all the country translations.
function translate(dict, lang, word) {
return dict[lang][word];
}
Some HTML. Here is our template (display: none) and the output element. Note the words in the curly braces are the ones to be translated.
<div class="template"><div>{{Goodbye}}, {{castle}}</div></div>
<div id="translation"></div>
Finally, putting it all together:
// grab the template
var tmpl = document.querySelector('.template').textContent;
var translation = document.querySelector('#translation');
// grab our translated html and add it to the output element
var html = applyTemplate(tmpl, lang);
translation.insertAdjacentHTML('afterbegin', html);
DEMO
Now, obviously you don't have to use this method (there are dozens of JS templating engines out there), but templating is particularly useful for sites that need to use multiple languages. Many do this on the back end but, as you can see, it can be easily done client-side too.
Hope this was useful and given you a couple of different ideas on how you might approach your solution.
<script type="text/javascript">
// Load the Google Transliteration API
google.load("elements", "1", {
packages: "transliteration"
});
var transliterationControl;
function onLoad() {
var options = {
sourceLanguage: 'en',
destinationLanguage: ['hi','or','bn','ta','te'],
transliterationEnabled: true,
shortcutKey: 'ctrl+g'
};
// Create an instance on TransliterationControl with the required
// options.
transliterationControl =
new google.elements.transliteration.TransliterationControl(options);
// Enable transliteration in the textfields with the given ids.
var ids = [ "transl1", "transl2" ];
transliterationControl.makeTransliteratable(ids);
// Add the STATE_CHANGED event handler to correcly maintain the state
// of the checkbox.
transliterationControl.addEventListener(
google.elements.transliteration.TransliterationControl.EventType.STATE_CHANGED,
transliterateStateChangeHandler);
// Add the SERVER_UNREACHABLE event handler to display an error message
// if unable to reach the server.
transliterationControl.addEventListener(
google.elements.transliteration.TransliterationControl.EventType.SERVER_UNREACHABLE,
serverUnreachableHandler);
// Add the SERVER_REACHABLE event handler to remove the error message
// once the server becomes reachable.
transliterationControl.addEventListener(
google.elements.transliteration.TransliterationControl.EventType.SERVER_REACHABLE,
serverReachableHandler);
// Set the checkbox to the correct state.
document.getElementById('checkboxId').checked =
transliterationControl.isTransliterationEnabled();
// Populate the language dropdown
var destinationLanguage =
transliterationControl.getLanguagePair().destinationLanguage;
var languageSelect = document.getElementById('languageDropDown');
var supportedDestinationLanguages =
google.elements.transliteration.getDestinationLanguages(
google.elements.transliteration.LanguageCode.ENGLISH);
for (var lang in supportedDestinationLanguages) {
var opt = document.createElement('option');
opt.text = lang;
if (lang=="TAMIL" || lang=="TELUGU" || lang=="HINDI" || lang=="ORIYA" || lang=="BENGALI"){
opt.value = supportedDestinationLanguages[lang];
if (destinationLanguage == opt.value) {
opt.selected = true;
}
try {
languageSelect.add(opt, null);
} catch (ex) {
languageSelect.add(opt);
}
}//End of if
}
}
// Handler for STATE_CHANGED event which makes sure checkbox status
// reflects the transliteration enabled or disabled status.
function transliterateStateChangeHandler(e) {
document.getElementById('checkboxId').checked = e.transliterationEnabled;
}
// Handler for checkbox's click event. Calls toggleTransliteration to toggle
// the transliteration state.
function checkboxClickHandler() {
transliterationControl.toggleTransliteration();
}
// Handler for dropdown option change event. Calls setLanguagePair to
// set the new language.
function languageChangeHandler() {
var dropdown = document.getElementById('languageDropDown');
transliterationControl.setLanguagePair(
google.elements.transliteration.LanguageCode.ENGLISH,
dropdown.options[dropdown.selectedIndex].value);
}
// SERVER_UNREACHABLE event handler which displays the error message.
function serverUnreachableHandler(e) {
document.getElementById("errorDiv").innerHTML =
"Transliteration Server unreachable";
}
// SERVER_UNREACHABLE event handler which clears the error message.
function serverReachableHandler(e) {
document.getElementById("errorDiv").innerHTML = "";
}
google.setOnLoadCallback(onLoad);
I have a KoGrid and want to be able to save visible columns when a user revisits the page. I would save data in json to a cookie or on the database, but how can I get notified when a column's visible attribute is changed, and load visibility on initialization?
Its easy achieved with some creative overrides of the kg.grid constructor
http://jsfiddle.net/A29GA/2/
var savedState = { age: false, name: true };
var org = kg.Grid;
kg.Grid = function (options) {
var grid = new org(options);
ko.utils.arrayForEach(grid.columns(), function(col) {
//load state from cookie
col.visible(savedState[col.field]);
});
grid.visibleColumns.subscribe(function() {
console.log("Here you get notified when visible columns change save to cookie");
});
return grid;
};
Here is a example that uses actual cookies, but I wouldnt rely on the cookie code, its quick and dirty
http://jsfiddle.net/A29GA/3/
I'm looking for tutorial or example on how to implement a simple input text for searching
in the grid.
My attempt (but ng-keyup require angularjs > 1.1.3 and I've got
1.0.7)
<input type="text" ng-keyup="mySearch()" ng-model="searchText">
$scope.getPagedDataAsync = function (pageSize, page, searchText) {
setTimeout(function () {
var data;
if (searchText) {
var ft = searchText.toLowerCase();
$http.get('largeLoad.json?q='+encodeURIComponent(ft)).success(function (largeLoad) {
$scope.setPagingData(largeLoad,page,pageSize);
});
} else {
$http.get('largeLoad.json').success(function (largeLoad) {
$scope.setPagingData(largeLoad,page,pageSize);
});
}
}, 100);
};
$scope.mySearch = function(){
console.log($scope.searchText);
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage,$scope.searchText);
}
Bye
NB its a fake request against a json file just to make the example.
Update: I'm using ng-grid-1.3.2
Basically to solve this problem I think you can use a solution similar to what I've done below where I'm just watching the property of the model for changes and firing a function to do the filtering on the data set at that point.
The HTML for the text input
<input type="text" placeholder="Type to filter" ng-model="gardenModel.externalFilterText"/>
The JavaScript that filters the data set (also included the part I had a watch on a service to update the data in the first place too or if the data is refreshed to reapply the filter).
//This function is called every time the data is updated from the service or the filter text changes
$scope.filterGardens = function(filterText) {
//Basically everything in this function is custom filtering specific
//to the data set I was looking at if you want something closer to the
//real implementation you'll probably have to dig through the source (I believe they separated the search filter code into it's own file in the original project)
//Creating a temporary array so changes don't cause a bunch of firing of watchers
var tempToShow = [];
//doing case insensitive search so lower case the filter text
filterText = filterText.toLowerCase();
//If the filter text is blank just use the whole data set
if(!filterText || filterText == "")
{
$scope.gardenModel.shownGardens = $scope.gardenModel.gardens;
return;
}
//step through each entry in the main list and add any gardens that match
for (var i = 0; i < $scope.gardenModel.gardens.length; i++) {
var curEntry = $scope.gardenModel.gardens[i];
var curGarden = curEntry.curGarden;
if(curGarden["Garden Name"] && curGarden["Garden Name"].answer.toString().toLowerCase().indexOf(filterText)!=-1)
tempToShow.push(curEntry);
else if(curGarden["Address"] && curGarden["Address"].answer.toString().toLowerCase().indexOf(filterText)!=-1)
tempToShow.push(curEntry);
else if(curGarden["Ownership"] && curGarden["Ownership"].answer.toString().toLowerCase().indexOf(filterText)!=-1)
tempToShow.push(curEntry);
else if(curGarden.gardenId && curGarden.gardenId == filterText)
tempToShow.push(curEntry);
};
$scope.gardenModel.shownGardens = tempToShow;
}
//Watch for any changes to the filter text (this is bound to the input in the HTML)
$scope.$watch('gardenModel.externalFilterText', function(value) {
$scope.filterGardens(value);
});
//Watch for any changes on the service (this way if addition/edit are made and
//refresh happens in the service things stay up to date in this view, and the filter stays)
$scope.$watch( function () { return gardenService.gardens; }, function ( gardens ) {
$scope.gardenModel.gardens = gardens;
$scope.filterGardens($scope.gardenModel.externalFilterText);
});
Edit Cleaned up the code formatting a bit and added some comments.
So I have datatable setup using the YUI 2.0. And for one of my column definitions, I've set it up so when you click on a cell, a set of radio button options pops so you can modify that cell content.
I want whatever changes are made to be reflected in the database. So I subscribe to a radioClickEvent. Here is my code for that:
Ex.myDataTable.subscribe("radioClickEvent", function(oArgs){
// hold the change for now
YAHOO.util.Event.preventDefault(oArgs.event);
// block the user from doing anything
this.disable();
// Read all we need
var elCheckbox = oArgs.target,
newValue = elCheckbox.checked,
record = this.getRecord(elCheckbox),
column = this.getColumn(elCheckbox),
oldValue = record.getData(column.key),
recordIndex = this.getRecordIndex(record),
session_code = record.getData(\'session_code\');
alert(newValue);
// check against server
YAHOO.util.Connect.asyncRequest(
"GET",
"inlineeddit.php?sesscode=session_code&",
{
success:function(o) {
alert("good");
var r = YAHOO.lang.JSON.parse(o.responseText);
if (r.replyCode == 200) {
// If Ok, do the change
var data = record.getData();
data[column.key] = newValue;
this.updateRow(recordIndex,data);
} else {
alert(r.replyText);
}
// unblock the interface
this.undisable();
},
failure:function(o) {
alert("bad");
//alert(o.statusText);
this.undisable();
},
scope:this
}
);
});
Ex.myDataTable.subscribe("cellClickEvent", Ex.myDataTable.onEventShowCellEditor);
But when I run my code and I click on a cell and I click a radio button, nothing happens ever. I've been looking at this for some time and I have no idea what I'm doing wrong. I know you can also use asyncsubmitter within my column definition I believe and I tried that, but that also wasn't working for me.
Any ideas would be greatly appreciated.