In my Meteor code. Can I define a method "or a function" on the server and call it on collectionName.findOne({id: 'someId'}).methodName; on the client?
Being new to Meteor, I don't know if this is possible and if so, what would the syntax look like? Thanks
This code is just a brain dump.
//server
Meteor.method({
doWork1: function (args) {
//do work
return something;
}
});
MyCol = new Mongo.Collection('myCol');
MyCol.insert({item: "envelopes", qty : 100, myFunc: doWork1});
//client
Meteor.call(MyCol.findOne({item: 'envelops'}).myFunc;
Edited:
Blaze Sahlzen comments made me think and add the following.
The reasons why I thought to give a mongo collection a try is this:
Stage one: the user fills up a form and click a button, the input values need to be used as arguments for a method which when successfully returns, the form gets modified to show different input fields for the user to fill up again and click the SAME button.
Stage two: same as stage one but with different input fields again.
Stage n: same as stage n-1 but with different input fields again.
I need to store all input values and group them by their stage identifier.
Each method may add/remove different kind of input controls for the next stage.
Since the SAME button will be used thus I don't have different buttons for different methods, so I came up with my original question "brain dump code". You are welcome to reinvent or change it and offer yours. :) Thanks again.
edited 2
A practical example could look like this:
var stageOne = {};
//for each DOM input
stageOne[inputName][i]= [inputValue][i];
myCol.insert({'stageOne': stageOne});
//then an observer for stageOne
But I just can't get my head around how to "link" each stage with the correct method to call without having to use a long if or switch conditional statement.
Alright, if I understand what you mean, I don't think you need observeChanges. The following solution might be a bit extensive, so bear with me.
Firstly you need a session variable to control on the client side which form values need to be shown. You could introduce this variable in your Template.name.onRendered.
Session.set('stage',1);
Then you have your input fields
<form>
<label id="label1">{{label1}}</label>
<input id="field1" type="text"/>
<label id="label1">{{label2}}</label>
<input id="field2" type="text"/>
<label id="label1">{{label3}}</label>
<input id="field3" type="text"/>
<button id="form-submit" type="submit"/>
</form>
I can imagine that you want to switch up the names of their labels to reflect the change in forms as you go to different stages. As a result you can write helpers as such:
'label1': function(){
var myStage = Session.get('stage');
if (myStage == 1){return '<label-title-for-stage-1>';
} else if (myStage == 2){return '<label-title-for-stage-2>';}
} else if .... etc.
}
Any changes to the session variable 'stage' will force the helper to reload, making it ideal to update form titles as you go through your stages.
You can then write an event for the button click event as such:
'submit #form-submit': function(){
var options = {
stage: Session.get('stage'),
values: [
{ value: $('#field1').val(), name:$("#label1").text() },
{ value: $('#field2').val(), name:$("#label1").text() },
{ value: $('#field3').val(), name:$("#label3").text() }]
}
Meteor.call('storeValues', options, function(error, result) {
if (error) {
console.log(error);
}
Session.set('stage',result);
});
}
This will essentially combine the filled fields into one object and call upon a server method, waiting for a callback from the server that tells the client which stage to move to.
Now, on the server side you can insert the values for this particular user in your collection and see if a particular stage has filled up and return whether the client can move on to the next stage.
Meteor.methods({
'storeValues': function(options){
for (var i = 0; i < options.values.length; i++){
myCol.insert({
value:options.values[i].value,
name:options.values[i].name,
stage: options.stage
});
}
if (options.values.length > 'amount-per-stage'){
return options.stage + 1;
} else {
return options.stage;
}
}
});
This way you store the data that gets entered in each form, while moving up one stage each time if all fields have been entered.
What you could do is use observeChanges:
var query = MyCol.find({item: 'envelops'});
var handle = query.observeChanges({
added: function () {
somethingHappened();
},
changed: function () {
somethingHappened();
}
});
var somethingHappened = function(){
// do something
}
Query contains your collection, and the handle function automatically checks whether any changes are being made to that collection, triggering the somethingHappened function if there are.
Inside somethingHappened you can place the behaviour that you would normally place in your method.
You can use observeChanges both client side and server side, but in this case you only need it on the server side.
Related
Using a clarity datagrid version 2.3
Seeing an issue where if the user starts typing into the input field of datagrid column filter, the filter input focuses out automatically as soon as a key is pressed.
Since the datagrid is paginated and server driven, this causes the API to get fired as soon as a
key is pressed after the debounce time.
The automatic focus out of the input field cause the filter to only have a single character and the API gets triggered since the debouce is only 800.
Have looked at clarity github for any reported issues, doesn't look like its reported or anyone having similar issue.
Expected behavior should be the input focus out should not happend until the user moves the cursor away or presses enter, which is when the debounce should kickin after which the api should be called.
HTML:
<clr-datagrid
(clrDgRefresh)= refreshDataGrid($event)>
...
</clr-datagrid>
TS Component:
debouncer = new Subject<any>();
ngOnInit() {
this.debouncer.asObservable().pipe(
debounceTime(800)
).subscribe(state => {
// do something here.. like call an API to filter the grid.
})
}
refreshDataGrid(state) {
this.debouncer.next(state);
}
Any help is appreciated.
Currently I'm hacking my component, to make sure the focus is not lost on the input field until done so by the user.
refreshDataGrid(state) {
const isClrFilterInputField = document.querySelector('.datagrid-filter .clr-input');
if (isClrFilterInputField instanceof HTMLElement) {
isClrFilterInputField.focus();
}
this.debouncer.next(state);
}
This is still not a clean answer, but as far as I have searched, this seems like an issue with clarity datagrid itself, until I hear from someone with a cleaner answer.
Most likely the upgrade version might have this fixed.
Yet to check that.
Unfortunately I think we designed the datagrid to emit the changes on each filter value change with debouncing intended to be done on the app side as consumers see fit.
That said, it is possible to accomplish what you describe. I've implmented a quick and dirty guard based on events but there may be better ways. I'll add code snippets here and a link to the working stackblitz at the end.
You are on the right track with the debouncer. But we don't need to debounce with time, we only need to 'debounce' on certain events.
Instead of debouncing with time, what if we debounce with an #HostListener for clicks on the filter input? (I'll leave it as an exercise for you to implement a HostListener for the focusin event since focusin bubble's up and blur does not). To do that we need:
A Hostlistener that can hear keydown.enter event on the filter input
A guard to prevent requests
A property to store the datagrid state as user enters text
In general the code needs to:
Fetch data when component inits but not after unless directed
Keep track of state events that get emitted from the datagrid
listen to keydown.enter events (and any other events like the filter input focusout - becuase it bubbles up, unlike blur)
Check that the event was generated on a datagrid filter input
dismiss the guard
make the request
re-enlist the guard
Here is a rough attempt that does that:
export class DatagridFullDemo {
refreshGuard = true; // init to true to get first run data
debouncer = new Subject<any>(); // this is now an enter key debouncer
datagridState: ClrDatagridStateInterface; // a place to store datagrid state as it is emitted
ngOnInit() {
// subscribe to the debouncer and pass the state to the doRefresh function
this.debouncer.asObservable().subscribe(state => {
this.doRefresh(state);
});
}
// a private function that takes a datagrid state
private doRefresh(state: ClrDatagridStateInterface) {
// Guard against refreshes ad only run them when true
if (this.refreshGuard) {
this.loading = true;
const filters: { [prop: string]: any[] } = {};
console.log("refresh called");
if (state.filters) {
for (const filter of state.filters) {
const { property, value } = <{ property: string; value: string }>(
filter
);
filters[property] = [value];
}
}
this.inventory
.filter(filters)
.sort(<{ by: string; reverse: boolean }>state.sort)
.fetch(state.page.from, state.page.size)
.then((result: FetchResult) => {
this.users = result.users;
this.total = result.length;
this.loading = false;
this.selectedUser = this.users[1];
// Set the guard back to false to prevent requests
this.refreshGuard = false;
});
}
}
// Listen to keydown.enter events
#HostListener("document:keydown.enter", ["$event"]) enterKeydownHandler(
event: KeyboardEvent
) {
// Use a host listener that checks the event element parent to make sure its a datagrid filter
const eventSource: HTMLElement = event.srcElement as HTMLElement;
const parentElement = eventSource.parentElement as HTMLElement;
if (parentElement.classList.contains("datagrid-filter")) {
// tell our guard its ok to refresh
this.refreshGuard = true;
// pass the latest state to the debouncer to make the request
this.debouncer.next(this.datagridState);
}
}
refresh(state: ClrDatagridStateInterface) {
this.datagridState = state;
this.debouncer.next(state);
}
}
Here is a working stackblitz: https://stackblitz.com/edit/so-60980488
When you create a MDL table, one of the options is to apply the class 'mdl-data-table--selectable'. When MDL renders the table an extra column is inserted to the left of your specified columns which contains checkboxes which allow you to select specific rows for actions. For my application, I need to be able to process some JavaScript when a person checks or unchecks a box. So far I have been unable to do this.
The problem is that you don't directly specify the checkbox controls, they are inserted when MDL upgrades the entire table. With other MDL components, for instance a button, I can put an onclick event on the button itself as I'm specifying it with an HTML button tag.
Attempts to put the onclick on the various container objects and spans created to render the checkboxes has been unsuccessful. The events I attach don't seem to fire. The closest I've come is attaching events to the TR and then iterating through the checkboxes to assess their state.
Here's the markup generated by MDL for a single checkbox cell:
<td>
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect mdl-data-table__select mdl-js-ripple-effect--ignore-events is-upgraded" data-upgraded=",MaterialCheckbox">
<input type="checkbox" class="mdl-checkbox__input">
<span class="mdl-checkbox__focus-helper"></span>
<span class="mdl-checkbox__box-outline">
<span class="mdl-checkbox__tick-outline"></span>
</span>
<span class="mdl-checkbox__ripple-container mdl-js-ripple-effect mdl-ripple--center">
<span class="mdl-ripple"></span>
</span>
</label>
</td>
None of this markup was specified by me, thus I can't simply add an onclick attribute to a tag.
If there an event chain I can hook into? I want to do it the way the coders intended.
It's not the nicest piece of code, but then again, MDL is not the nicest library out there. Actually, it's pretty ugly.
That aside, about my code now: the code will bind on a click event on document root that originated from an element with class mdl-checkbox.
The first problem: the event triggers twice. For that I used a piece of code from Underscore.js / David Walsh that will debounce the function call on click (if the function executes more than once in a 250ms interval, it will only be called once).
The second problem: the click events happens before the MDL updates the is-checked class of the select box, but we can asume the click changed the state of the checkbox since last time, so negating the hasClass on click is a pretty safe bet in determining the checked state in most cases.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
$(document).on("click", ".mdl-checkbox", debounce(function (e) {
var isChecked = !$(this).hasClass("is-checked");
console.log(isChecked);
}, 250, true));
Hope it helps ;)
We currently don't have a way directly to figure this out. We are looking into adding events with V1.1 which can be subscribed to at Issue 1210. Remember, just subscribe to the issue using the button on the right hand column. We don't need a bunch of +1's and other unproductive comments flying around.
One way to hack it is to bind an event to the table itself listening to any "change" events. Then you can go up the chain from the event's target to get the table row and then grab the data you need from there.
You could delegate the change event from the containing form.
For example
var form = document.querySelector('form');
form.addEventListener('change', function(e) {
if (!e.target.tagName === 'input' ||
e.target.getAttribute('type') !== 'checkbox') {
return;
}
console.log("checked?" + e.target.checked);
});
I have a label in abc.aspx, say 'label1'. I want to assign a value to 'label1' from another page xyz.ashx. How can i do this?
In general, this doesn't make sense.
When your second page is executing, the first page is gone. It simply no longer exists. There is no label for you to assign to.
Even if you could assign to the label, the previous request is over. The HTML (without the change) has already been sent to the user's browser.
I think u pls try this
call getValuein document.ready
getValue = function() {
$.post('../ax/a.ashx?val=1'
,
{
spc: data // pass some data
}
, function(data) {
document.getElementById("<%=textBox1.ClientID %>").value = data;
});
}
I have a partial view that is rendered within a main view. The partial view takes advantage of System.ComponentModel.DataAnnotations and Html.EnableClientValidation().
A link is clicked, and div containing the partial view is displayed within a JQuery.Dialog().
I then click the save button without entering any text in my validated input field. This causes the client side validation to fire as expected, and display the '*required' message beside the invalid field.
When the cancel button is clicked, I want to reset the client side MVC validation back to it's default state and remove any messages, ready for when the user opens the dialog again. Is there a recommended way of doing this?
This answer is for MVC3. See comments below for help updating it to MVC 4 and 5
If you just want to clear the validation-messages so that they are not shown to the user you can do it with javascript like so:
function resetValidation() {
//Removes validation from input-fields
$('.input-validation-error').addClass('input-validation-valid');
$('.input-validation-error').removeClass('input-validation-error');
//Removes validation message after input-fields
$('.field-validation-error').addClass('field-validation-valid');
$('.field-validation-error').removeClass('field-validation-error');
//Removes validation summary
$('.validation-summary-errors').addClass('validation-summary-valid');
$('.validation-summary-errors').removeClass('validation-summary-errors');
}
If you need the reset to only work in your popup you can do it like this:
function resetValidation() {
//Removes validation from input-fields
$('#POPUPID .input-validation-error').addClass('input-validation-valid');
$('#POPUPID .input-validation-error').removeClass('input-validation-error');
//Removes validation message after input-fields
$('#POPUPID .field-validation-error').addClass('field-validation-valid');
$('#POPUPID .field-validation-error').removeClass('field-validation-error');
//Removes validation summary
$('#POPUPID .validation-summary-errors').addClass('validation-summary-valid');
$('#POPUPID .validation-summary-errors').removeClass('validation-summary-errors');
}
I hope this is the effect you seek.
If you are using unobtrusive validation that comes with MVC you can simply do:
$.fn.clearErrors = function () {
$(this).each(function() {
$(this).find(".field-validation-error").empty();
$(this).trigger('reset.unobtrusiveValidation');
});
};
------------------------------------------------------------------------
Third Party Edit:
This mostly worked in my case, but I had to remove the $(this).find(".field-validation-error").empty(); line. This appeared to affect the re-showing of the validation messages when resubmitting.
I used the following:
$.fn.clearErrors = function () {
$(this).each(function() {
$(this).trigger('reset.unobtrusiveValidation');
});
};
and then called it like this:
$('#MyFormId input').clearErrors();
function resetValidation() {
$('.field-validation-error').html("");
}
You can simply define a new function in jQuery:
$.fn.resetValidation = function () {
$(this).each(function (i, e) {
$(e).trigger('reset.unobtrusiveValidation');
if ($(e).next().is('span')) {
$(e).next().empty();
}
});
};
and then use it for your input fields:
$('#formId input').resetValidation();
Thank you. I had a similar question for a slightly different scenario. I have a screen where when you click one of the submit buttons it downloads a file. In MVC when you return a file for download, it doesn't switch screens, so any error messages which were already there in the validation summary remain there forever. I certainly don't want the error messages to stay there after the form has been submitted again. But I also don't want to clear the field-level validations which are caught on the client-side when the submit button is clicked. Also, some of my views have more than one form on them.
I added the following code (thanks to you) at the bottom of the Site.Master page so it applies to all of my views.
<!-- This script removes just the summary errors when a submit button is pressed
for any form whose id begins with 'form' -->
<script type="text/javascript">
$('[id^=form]').submit(function resetValidation() {
//Removes validation summary
$('.validation-summary-errors').addClass('validation-summary-valid');
$('.validation-summary-errors').removeClass('validation-summary-errors');
});
</script>
Thanks again.
You can tap into the validation library methods to do this.
There are two objects of interest: FormContext and FieldContext. You can access the FormContext via the form's __MVC_FormValidation property, and one FieldContext per validated property via the FormContext's fields property.
So, to clear the validation errors, you can do something like this to a form:
var fieldContexts = form.__MVC_FormValidation.fields;
for(i = 0; i < fieldContexts.length; i++) {
var fieldContext = fieldContexts[i];
// Clears validation message
fieldContext.clearErrors();
}
// Clears validation summary
form.__MVC_FormValidation.clearErrors();
Then, you can hook that piece of code to whichever event you need.
Sources for this (quite undocumented) insight:
http://bradwilson.typepad.com/presentations/advanced-asp-net-mvc-2.pdf (Mentions FieldContext)
https://stackoverflow.com/a/3868490/525499 (For pointing out this link, which metions how to trigger client-side validation via javascript)
In order to complete clear the validation artifacts including the message, the coloured background of the input field, and the coloured outline around the input field, I needed to use the following code, where this was (in my case) a Bootstrap modal dialog containing an imbedded form.
$(this).each(function () {
$(this).find(".field-validation-error").empty();
$(this).find(".input-validation-error").removeClass("input-validation-error");
$(this).find(".state-error").removeClass("state-error");
$(this).find(".state-success").removeClass("state-success");
$(this).trigger('reset.unobtrusiveValidation');
});
Here you can use simply remove error message
$('.field-validation-valid span').html('')
OR
$('.field-validation-valid span').text('')
I've this issue for "Validation summery" after form ajax submit and done it like this:
$form.find('.validation-summary-errors ul').html('');
and complete code is:
$("#SubmitAjax").on('click', function (evt) {
evt.preventDefault();
var $form = $(this).closest('form');
if ($form.valid()) {
//Do ajax call . . .
//Clear validation summery
$form.find('.validation-summary-errors ul').html('');
}
});
I am using Struts 2.1.6 with Dojo plugin, whole app has ajax links (sx:a).
Did anybody succeed to implement back button functionality and linking to certain content?
Does anybody have any experience how to implement? I am planning to implement (if there is no good solution already) something like so:
changing address bar link (adding parameters) with js which I can then read and get proper content and then publish it with notifyTopics.
Or should I just change whole app to use jQuery plugin? Do jQuery has good solutions for back button and linking on ajax pages?
I can think of 2 simple ways off the top of my head:
<s:form action="actionName">
<input type="hidden" value="<s:property value="someProperty"/>" name="someProperty"/>
<input type="hidden" value="<s:property value="someProperty2"/>" name="someProperty2"/>
<s:submit value="Back" />
</s:form>
or
<s:url name="backURL" action="actionName">
<s:param name="someProperty" value="someProperty"/>
<s:param name="someProperty2" value="someProperty2"/>
</s:url>
Back
If you already have query string parameters:
Back
or
<input type="button" value="Back" onclick="javascript.window=document.referrer;"/>
I have tried to use Struts 2 with dojo and implement the back button. You are already way over the head of Struts 2's ajax implementation. They mainly used and wrote it to write simple and quick ajax function calls and is not very well suited for more extensive uses. Plus when you do s:head theme='ajax' tag; struts will import every ajax js file you 'may' need which kills load time.
I would suggest either 1. Learn dojo and use the library independent of struts 2. Or 2. Get jQuery, I was able to implement a back button functionality relatively simple (more so then struts 2 theme='ajax').
Don't know Struts, but have you looked at dojo.undo (0.4.3)?
All my links go through one action which looks for a parameter menuId (of course id of a menu which has to be shown).
From this action, before returning response I set one javascript function to be called:
setBackMenuId(menuId,sometext)
MenuId is the id, sometext is a name of that menu, so browser log history better.
function setBackMenuId(id,subtekst) {
window.location.hash = "menuId="+id;
document.title = subtekst;
selectedHash = document.location.hash;
if(intervalHashSearch == '') {
initializeHashSearch();
}
}
Then, other needed js functions:
function publishLinkTarget() {
var param = window.location.hash;
if(param) {
if(param.indexOf("menuId") > 0) {
var id = param.split("=", 2);
if(id[1]) {
setCookie('backMenuId',id[1],1,false);
setTimeout('publishLayoutContent()', 100);
}
}
}
}
var selectedHash = '';
var intervalHashSearch = '';
function initializeHashSearch() {
intervalHashSearch = window.setInterval('checkHash()', 500);
}
function stopHashSearch() {
window.clearInterval(intervalHashSearch);
intervalHashSearch = '';
}
function checkHash() {
var currentHash = document.location.hash;
if(selectedHash != currentHash) {
selectedHash = currentHash;
publishLinkTarget();
}
}
function publishLayoutContent() {
dojo.event.topic.publish("layoutContentTarget");
}
If you look at it you see, that first it is called 'setBackMenuId', which adds hash and parameter to address bar and changes title, and then remembers this hash, so interval hash search can find out the differrence. Then it initializes this hash search.
'checkHash' is running ever 500 miliseconds and is checking if hash has changed (that means, that back button was pressed, and not a new link was clicked (setBackMenuId sets selectedHash). If true (back/forward button was pressed) function 'publishLinkTarget' is called, which reads the parameters from hash, and if they are ok, first I set a cookie, so I can read it from the HttpServletRequest and find out for which menu id link is. If I am here it means that I have to also publish the content which is made with 'publishLayoutContent'.
In action class (this is MenuAction, method view, the same as published in ) only this is important:
Integer menuId = null;
if(request.getParameter("menuId") != null) {
menuId = Integer.valueOf(request.getParameter("menuId"));
} else {
menuId = getIntCookie("hiddenMenuId");
}
So, if I don't get the menu id from the parameter (link clicked) I get from a cookie (back/forward button).
And JSP with this target:
<s:url var="layoutContentUrl" action="Menu-view" namespace="/public" />
<sx:div showLoadingText="false" indicator="ajaxIndicator"
id="layout-content" href="%{layoutContentUrl}" theme="ajax"
listenTopics="layoutContentTarget" preload="false"
afterNotifyTopics="/ajaxAfter">
</sx:div>
NOTE: This is a special case if you have everything connected through one parameter, but it can be easily extended with other parameters which publish other targets. I will try to make it enough generic to publish it somewhere, but this is (I guess) a long way ahead :)
If you have any question, please post it.