Client-size manipulation of a Dynamics CRM Form - javascript

ORIGINAL QUESTION:
I am trying to create some javascript to add to a form in Microsoft Dynamics CRM.
I have the following script which I have assigned to the forms onLoad event:
$(document).ready(function () {
if ($('#CheckBox1').is(':checked')) {
$('<div id="div2">Some Data Here</div>').insertAfter("#Div1");
$('#divHeader').height('+=25px');
var newtop = $('#divMain').position().top + 25;
$('#divMain').css('top', newtop + 'px');
}
});
The following is a stripped down version of the forms HTML
<div id="divHeader">
<div id="Div1"></div>
</div>
<div id="divMain"></div>
When the form loads, what should happen is this:
<div id="divHeader">
<div id="Div1"></div>
<div id="Div2">Some Data Here</div>
</div>
<div id="divMain"></div>
That does happen. However, the problem is that the divHeader and divMain do not resize, so the newly added Div2 can't be seen unless the user scrolls down within the divHeader.
If I add an alert:
$(document).ready(function () {
if ($('#CheckBox1').is(':checked')) {
alert("Random alert");
$('<div id="div2">Some Data Here</div>').insertAfter("#Div1");
$('#divHeader').height('+=25px');
var newtop = $('#divMain').position().top + 25;
$('#divMain').css('top', newtop + 'px');
}
});
The whole thing works perfectly. How do I get this to work without the alert?
UPDATE 1:
setTimeout also works instead of using an alert.
$(document).ready(function () {
setTimeout(function () {
if ($('#CheckBox1').is(':checked')) {
$('<div id="div2">Some Data Here</div>').insertAfter("#Div1");
$('#divHeader').height('+=25px');
var newtop = $('#divMain').position().top + 25;
$('#divMain').css('top', newtop + 'px');
}
}, 5000);
});
So it seems $(document).ready doesn't seem to do it's job properly. In both cases alert or setTimeout, the form gets extra time to finish loading before the div tags are resized.

It is entirely unsupported to do DOM manipulations in Dynamics CRM. See Do Not Access the DOM.
You will instead need to use supported methods of manipulating the form (i.e. Xrm.Page). While it is not possible to dynamically add/remove content on a CRM-form, you can show and hide elements. As I understand it, you are trying to show some content if a boolean field (presented as a checkbox) on the form is true.
This can be done by adding the control that you want to show/hide to your form (that control can either be one of the standard CRM-fields or a custom web resource, depending on your requirements). You would then write a bit of JavaScript to show/hide the control in question when your boolean attribute changes its value:
function onload() {
Xrm.Page.getAttribute("booleanField").addOnChange(showMoreStuffInHeader);
showMoreStuffInHeader();
}
function showMoreStuffInHeader() {
var visible = Xrm.Page.getAttribute("booleanField").getValue();
Xrm.Page.getControl("someDataHereControl").setVisible(visible);
}

Use Jquery Plugin Wait until element exists Pluging will wait for element to appear in DOM and then fire given handler function.
$(document).ready(function () {
$('#CheckBox1').waitUntilExists(function () { // OR $('#Div1').waitUntilExists(function ()
if ($('#CheckBox1').is(':checked')) {
$('<div id="div2">Some Data Here</div>').insertAfter("#Div1");
$('#divHeader').height('+=25px');
var newtop = $('#divMain').position().top + 25;
$('#divMain').css('top', newtop + 'px');
}
});
});

Related

Creating a way to navigate back when using jQuery for AJAX

I am dynamically loading content into part of a page using jQuery .load().
It is working well, but I am having trouble building a way for the user to navigate back to the original content after the new content has been loaded.
I have created a 'close' icon with css which exists on the new page which is loaded, but I am not sure how to set up the jQuery / JavaScript in order for it to navigate the user back to the original state of that part of the page.
This is the relevant js:
// pages to load
var loadLudwig = "lw.html";
$("#work a:first-child").click(function() {
$("#work").fadeTo('slow', 0, function() {
$("#work").load(loadLudwig, function(){
$("#work").fadeTo('slow', 1);
});
});
});
// (& this part is working fine)
The relevant HTML (on the original page) is like this (its a grid of images embedded within anchor tags):
<section id="work">
...img and svg stuff
</section>
I tried many variations of:
$("#close-button").click(function() {
$("#work").fadeTo('slow', 0, function () {
$("#work").load('home.html #work', function() {
$("#work").fadeTo('slow', 1);
});
});
});
but this loads the content very strangely / some of the original functionality of #work is lost.
How do I get my close button to navigate back to the original state of #work?
In the jquery documentation for .load() is stated that:
Script Execution
When calling .load() using a URL without a suffixed selector
expression, the content is passed to .html() prior to scripts being
removed. This executes the script blocks before they are discarded. If
.load() is called with a selector expression appended to the URL,
however, the scripts are stripped out prior to the DOM being updated,
and thus are not executed. An example of both cases can be seen below:
Here, any JavaScript loaded into #a as a part of the document will
successfully execute.
1. $( "#a" ).load( "article.html" );
However, in the following case, script blocks in the document being
loaded into #b are stripped out and not executed:
1. $( "#b" ).load( "article.html #target" );
This is a probable cause for lack of functionality.
I'd also look into event binding. In your code examples you're using .click but if you are loading content or you are creating elements on-the-fly you should be favoring .on(). This method delegates events instead of just binding them to a DOM node.
I'd recommend you reading the whole article.
EDIT:
Here is a quick n'dirty way of achieving the effect
// pages to load
var loadLudwig = "lw.html",
$ludwig,
$work = $('#work'),
$workContent = $work.children(),
$closeButton = $("#close-button");
$work.find('a:first-child').click(function() {
$work.fadeTo('slow', 0, function() {
//Here is the tricky part
//Detaching keeps all the jQuery data on the elements
$workContent.detach();
//The first time, load the content,
//if the content is already loaded
//append it to the container
if(!$ludwig){
$work.load(loadLudwig, function(){
//Save the content in a var
//so you can reuse it later
$ludwig = $work.children();
$work.fadeTo('slow', 1);
});
} else {
$ludwig.appendTo($work);
$work.fadeTo('slow', 1);
}
});
});
$closeButton.click(function() {
$work.fadeTo('slow', 0, function () {
//Remove the old content, don't worry
//because is stored in $ludwig
$work.children().detach();
//Instead of reloading the content, just
//attach the fragment again
$workContent.appentTo($work);
$work.fadeTo('slow', 1);
});
});
You probably need to save the html somewhere. For example:
// Top of file
var oldHTML = "";
// Lots of stuff...
$("#work a:first-child").click(function() {
$("#work").fadeTo('slow', 0, function() {
// Store the old html
oldHTML = $("#work").html();
$("#work").load(loadLudwig, function(){
$("#work").fadeTo('slow', 1);
});
});
});
// Code for the close button
$("#close-button").click(function() {
$("#work").fadeTo('slow', 0, function () {
$("#work").html(oldHTML).fadeIn("slow");
});
});
Alternatively, instead of replacing the html, you could create another child. Of course, you might have to slightly change your markup.
<section id="work">
<div id="oldHTML">
...img and svg stuff
</div>
<div id="newSection" style="display:none;">
</div>
</section>
Then replace $("#work") with $("#oldHTML") in your first piece of code like so:
$("#oldHTML a:first-child").click(function() {
$("#oldHTML").fadeTo('slow', 0, function() {
$("#oldHTML").hide();
$("#newSection").load(loadLudwig, function(){
$("#newSection").show().fadeTo('slow', 1);
});
});
});
// Code for the close button
$("#close-button").click(function() {
$("#newSection").fadeTo('slow', 0, function () {
$("#newSection").hide();
$("#work").fadeIn("slow");
});
});

Cordova / Phonegap -- jquery not executing Javascript on <div> load

I have a single page (index.html) document for phonegap that displays different "pages" when each new "div" is referenced. For example:
<div class="upage ui-page-theme-a" id="view_history" data-role="page" >
I am wanting to have a javascript function automatically execute via Jquery when the "div" is displayed. My code is below.
However I can't get anything to cause it to execute on div load. I can put an "onclick" call in the HTML, ie:
<a onclick="listMeasurements()" class="button" style="" data-role="button">List
Current Measurements</a>
and that works fine, but for some reason I can't get it to execute just from loading. Any insights would be greatly appreciated.
Sorry in advance if I'm overlooking something obvious.
$('#view_history').on('pagecontainershow', listMeasurements()) ;
// I've also tried bind() etc.
function listMeasurements() {
"use strict" ;
var fName = "listMeasurements():" ;
var measurements_recorded = window.localStorage.length;
$("#Measurements").html("<strong>" + measurements_recorded + "</strong>
measurement(s) recorded");
// Empty the list of recorded tracks
$("#measurement_list").empty();
// Iterate over all of the recorded tracks, populating the list
for(var i=0; i<measurements_recorded; i++){
$("#measurement_list").append("<li data-uib=\"jquery_mobile/listitem\" data-ver=\"0\"><span>" + window.localStorage.key(i) + "</span></li>");
}
$('#measurement_list').listview().listview('refresh');
}
From jQuery Mobile 1.4.2 the events have changed & don't work with a binding to a specific page like you have done i.e this -
$('#view_history').on('pagecontainershow', listMeasurements()) ;
won't work anymore.
You need to put a binding something like this on document -
function showSomeLove() {
alert("What is love, baby don't hurt me, no more!");
}
$(document).on( "pagecontainershow", function( event, ui ) {
var currentPageId = $( ":mobile-pagecontainer" ).pagecontainer( "getActivePage" ).attr('id');
if(currentPageId == 'page-a') {
/*Execute or Call your function here*/
showSomeLove();
} else if(currentPageId == 'page-b') {
/*do something else*/
}
});
Here's a WORKING DEMO - http://codepen.io/nitishdhar/pen/gnqFH
For more details read the pagecontainer widget - http://api.jquerymobile.com/pagecontainer/

Update jQuery Progress Bar even on Page Reload

I am currently working on a progress bar with jQuery that changes the inner progress bar according to checkboxes.
The script below works quite nicely except for the fact that when I reload the page, the progress bar is empty and the checkboxes are still activated.
This is probably due to the function not counting the number of checked checkboxes but uses the function checkbox.change() to start calculating.
My question now is how to configurate the script in order to update the progress bar even on page reload.
Thanks a lot in advance!
JQuery Script:
$( document ).ready(function() {
var checkbox = $(":checkbox"),
checkbox_length = checkbox.length;
checkbox.change(function () {
var that = $(this),
progress = 0,
checked_length = 0;
if(that.is(':last-child')) {
that.siblings().attr('checked', true);
}
checked_length = $(":checkbox:checked").length;
progress = Math.round(checked_length / checkbox_length * 100);
$('.bar').animate({'width' : progress + '%'}, 400);
$(".progresstext").html(progress + '%');
$.cookie('cookieProcessLevelProgress', progress, { expires: 7, path: '/' });
});
});
HTML code:
<div class="meter"><div class="bar"></div></div><span class="progresstext"></span>
I couldn't really figure out a small part of your code, but what I pasted below is a good start... basically put the "setting" of the progress bar into a function (DRY) and then call it on page load, and on checkbox change :)
function setProgress( $checkboxes ) {
var chk_length = $checkboxes.filter(":checked").length;
var progress = Math.round(chk_length / $checkboxes.length * 100);
$('.bar').animate({'width' : progress + '%'}, 400);
$(".progresstext").html(progress + '%');
};
$(function() {
var $checkboxes = $(":checkbox");
setProgress( $checkboxes );
$checkboxes.change(function(e) {
$(this)
.prevAll(":checkbox")
.prop("checked",true)
.end()
.nextAll(":checkbox")
.prop("checked",false);
setProgress( $checkboxes );
});
});
For your information, you can associate HTML5 progress tag instead of associating jQuery with it. Suppose you use the <progress> tag, like <progress value="6" max="10"> and then update the inner parameter using basic JavaScript. For me, the code looks somewhat like this:
<div id="content">
<progress value="2" max="10">
</div>
The last bit is onClick of checkbox, the value of x gets updated from 2 to 3, using x++. Next update the code using document.getElementById("content").innerHTML and that's it boom, it should work.
Happy coding.

jQuery hover-based popup events for every product in list

I have a little problem with a function in jquery. I need to display a pop-up every time that a user is hover a Product. My products are in a list, and I would like to have a pop-up specified to every product in order to display it's title and description :
This is the List of Products (displayed in a thumbnail)
<!-- This Div contains the List of Products -->
<ul class="thumbnails">
#foreach (var thumbnail in Model.ForfaitsInThumbNail)
{
<div class="thumbnail">
<img src= "#Url.Action("GetById", "ImageForfait", new { idForfait = thumbnail.Forfait.Id })" alt=""/>
<div class="caption">
<h2 class="fontTitle">#thumbnail.Forfait.Titre</h2> <h5 class="fontYearHijri"> pour l'année #thumbnail.Forfait.AnneeHijri H </h5>
.......
</div>
<!-- This div should contain the pop-up for every Product -->
<div class='pop-up' id='pop-up'> <h3>"#thumbnail.Forfait.Titre"</h3><p>"#thumbnail.Forfait.Description"</p></div>
</div>
</div>
}
</ul>
This is my poor jquery function wich only works with one Product and not all of them in a list
<script>
$('.thumbnail').each(function() {
$(this).hover(function (e) {
$('.pop-up').show()
.css('top', e.pageY + 20)
.css('left', e.pageX + 10)
.appendTo('body');
}, function () {
$('.pop-up').hide();
});
});
$('.thumbnail').each(function() {
$('.thumbnail').mousemove(function(e) {
$(".pop-up").css('top', e.pageY + 20).css('left', e.pageX + 10);
});
});
This function has no errors, but it displays only the pop-up of the last Product, and that seems logic because it can't identify the div of the specified Product. So my question is, how can I implement this functionnality like I want to be (special pop-up for every Product)? Any Ideas ? Thank You :)
I've update this answer by constructing a fiddle to show a working solution. In future, could you please provide a Fiddle where possible, as it saves us all a lot of time having to construct one for ourselves.
Here is my JavaScript:
// Note, this is important
$(document).ready(function () {
// We don't need an each, and include the e event handler
$('.thumbnail').hover(function (e) {
$('#pop-up').show()
.css('top', e.pageY + 20)
.css('left', e.pageX + 10)
.appendTo('body');
}, function () {
$('#pop-up').hide();
});
$('.thumbnail').mousemove(function (e) {
$("#pop-up").css('top', e.pageY + 20).css('left', e.pageX + 10);
});
});
Things you should also note: You don't need to have both an ID #pop-up and a class .popup on the same element, unless this is some special pop-up which inherits from the standard pop-up class. It looks like your ID/Class usage might be off, so just make sure that you're using IDs whenever something is unique, and classes when you have multiple instances.
Couple of fixes I made:
Always wrap your jQuery in a $(document).ready() method, this will ensure that your DOM is fully loaded before the events are applied
You forgot to include an event handler on your hover function, though I think you've updated your question with this included.
You forget to pass event object parameter on hover event
//do this
...
$(this).hover(e){
--
}
...
Hope this should work.
If you notice you used (e.pageX) so where does (e) come from. It has to be passed as a parameter.

JScrollPane Plugin - Reinitialize on Collapse

I'm trying to get JScrollPane to reinitialize on expand/collapse of my accordion found here. You can demo the accordion by clicking on one of the parents (Stone Tiles, Stone Sinks, Stone Wall Clading, etc).
Right now I set it as a click event using the following JQuery...
var pane = $('.menuwrap')
pane.jScrollPane();
var api = pane.data('jsp');
var i = 1;
$("ul#widget-collapscat-5-top > li.collapsing").click(function() {
$(this).delay(3000);
api.reinitialise();
});
It seems to work when you click the parent the second time, but not the first. I have no idea why but I went into trying to edit the JS for the accordion so that I can add this function when the collapse is complete (as opposed to trying to do this click workaround). The collapse JS can be viewed here.
I tried to add the JS for the reinitialize function here, but I think I'm not doing something properly.
May you point me in the right direction?
Thanks!
The api.reinitialise() is working properly. What is happening is that it updates the size when you click, and at this moment the element is not expanded yet. You may notice that if you expand, colapse and expand again the same section, nothing happens. But if you expand one and then click another one, the ScrollPane will adjust to the size of the first expanded element.
You can solve this with events: place $(this).trigger('colapseComplete') when the colapse ends. Then you can use:
//Listening to the colapseComplete event we triggered above
$("#widget-collapscat-5-top > li.collapsing").on('colapseComplete', function() {
api.reinitialise();
});
Maybe you can alter the addExpandCollapse function to call the reinitialise function at the end of each of its click actions this way :
function addExpandCollapse(id, expandSym, collapseSym, accordion) {
jQuery('#' + id + ' .expand').live('click', function() {
if (accordion==1) {
var theDiv = jQuery(this).parent().parent().find('span.collapse').parent().find('div');
jQuery(theDiv).hide('normal');
jQuery(this).parent().parent().find('span.collapse').removeClass('collapse').addClass('expand');
createCookie(theDiv.attr('id'), 0, 7);
}
jQuery('#' + id + ' .expand .sym').html(expandSym);
expandCat(this, expandSym, collapseSym);
api.reinitialise(); // HERE
return false;
});
jQuery('#' + id + ' .collapse').live('click', function() {
collapseCat(this, expandSym, collapseSym);
api.reinitialise(); // and HERE
return false;
});
}
and to be on a safer side, make sure you have the var api = pane.data('jsp'); line before the above piece of code anywhere in the file.

Categories

Resources