I am building a navigation bar that is driven based off of values retrieved from a SharePoint List. Right now, I am using an <ul> for my column headers and <li> for my contents. I can get the headers to display correctly and can also get the contents of that column to display correctly. What I'm having troubles with is that the <li> seems be appended to the <ul> which is great, but it's also putting it inside of it. My <ul> has a border around it and I want the content to be appended directly under that border but instead, it's putting everything inside of it.
Here is the specific block of code I believe is wrong:
$('#TableElement').hover(function () {
$('[id^=Header]').hover(function () {
$("#" + this.id).append("<li>" + this.id + "</li>");
});
});
Here is all of my code:
/////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////
////////////////////////////////////////EVERYTHING BELOW THIS LINE IS GOOD TO GO/////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////
//Print Headers to Screen. This will drive the core functionalty of the navpart
var siteUrl = '/sites/dev';
ExecuteOrDelayUntilScriptLoaded(retrieveListItems, "sp.js");
theCounter = 0;
var Headers = new Array();
var getCurrentElementId = null;
function retrieveListItems() {
var clientContext = new SP.ClientContext(siteUrl);
var oList = clientContext.get_web().get_lists().getByTitle('myList');
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml("<Where><IsNotNull><FieldRef Name='Title' /></IsNotNull></Where>");
this.collListItem = oList.getItems(camlQuery);
clientContext.load(collListItem);
clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
}
function onQuerySucceeded(sender, args) {
var listItemInfo = '';
var listItemEnumerator = collListItem.getEnumerator();
while (listItemEnumerator.moveNext()) {
var oListItem = listItemEnumerator.get_current();
theCounter += 1;
Headers[theCounter - 1] = oListItem.get_item('Title');
}
var HeaderDisplay = _.uniq(Headers);
for (var i = 0; i <= HeaderDisplay.length - 1; i++) {
$('#TableElement').append("<th id=Header" + i + ">" + HeaderDisplay[i] + "::::::" + "</th>");
}
}
function onQueryFailed(sender, args) {
alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
/////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////
////////////////////////////////////////EVERYTHING ABOVE THIS LINE IS GOOD TO GO/////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////
// You got the headers to print as expected. Right now you need to figure out how to get the current ID
// that the mouse is over. Try looking at another project you did where the mouse goes into the table header
// and the outline expands.
$('#TableElement').hover(function () {
$('[id^=Header]').hover(function () {
// Come back to this::::::: var content = $(this).html();
$("#" + this.id).append("<li>" + this.id + "</li>");
});
});
//This should be the universal onmouseover event that will expose only links
//and values relavent to the selected header.
//$(document).ready(function onPageLoad() {
// $().SPServices({
// operation: "GetListItems",
// async: false,
// listName: "myList",
// CAMLQuery: "<Query><Where><IsNotNull><FieldRef Name='Title' /></IsNotNull></Where></Query>",
// completefunc: function completeFunction(xData, Status) {
// $(xData.responseXML).SPFilterNode("z:row").each(function () {
// var Headers = "<th>" + $(this).attr("ows_Title") + "</th>";
// $("#TableElement").append(Headers);
// });
// }
// });
//});
HTML Code:
<%# Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%# Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%# Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%# Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%# Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<%# Import Namespace="Microsoft.SharePoint" %>
<%# Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="VisualWebPart1UserControl.ascx.cs" Inherits="AnotherMarcPart.VisualWebPart1.VisualWebPart1UserControl" %>
<!DOCTYPE Html />
<html>
<head>
<link rel="Stylesheet" type="text/css" href="c:\users\administrator\documents\visual studio 2010\Projects\AnotherMarcPart\AnotherMarcPart\VisualWebPart1\Stylesheet1.css" />
<title></title>
</head>
<body>
<ul id="TableElement"></ul>
<script type="text/javascript" src="c:\users\administrator\documents\visual studio 2010\Projects\AnotherMarcPart\AnotherMarcPart\VisualWebPart1\jQuery_v1.10.2.js"></script>
<script type="text/javascript" src="c:\users\administrator\documents\visual studio 2010\Projects\AnotherMarcPart\AnotherMarcPart\VisualWebPart1\jquery.SPServices-2013.01.js"></script>
<script type="text/javascript" src="c:\users\administrator\documents\visual studio 2010\Projects\AnotherMarcPart\AnotherMarcPart\VisualWebPart1\Underscore.js 1.5.2.js"> </script>
<script type="text/javascript" src="c:\users\administrator\documents\visual studio 2010\Projects\AnotherMarcPart\AnotherMarcPart\VisualWebPart1\JScript1.js"></script>
</body>
</html>
<li> tags must go inside a <ul>. If you want your content after the <ul>, then don't put it in an <li> (perhaps put it in a <div> instead) and use jQuery's .after() or .insertAfter() to put it after the <ul>.
Also, this code is likely wrong:
$('#TableElement').hover(function () {
$('[id^=Header]').hover(function () {
$("#" + this.id).append("<li>" + this.id + "</li>");
});
});
You don't want two .hover() handlers inside of one another. You will be installing the second .hover() over and over every time you hover over the first one. That will give you lots of duplicate event handlers and the function inside will get executed multiple times.
If .insertAfter() isn't exactly what you want, then show us your HTML and show us exactly where you want the new content inserted. You said you wanted it after the <ul> tag's border so that's what .insertAfter() will do.
Based on how you've edited your question, you would append to that other UL like this:
$("#TableElement").append("<li>" + this.id + "</li>");
That will make the <li> be the last <li> inside of the TableElement <ul> which will be inside it's border. There is no way to put an <li> inside a <ul> and have the <li> be outside the border around the <ul>. To do that, you would have to create a container object AFTER the <ul> and put the content into that container instead.
Related
I am working with a SharePoint hosted add on that has a JavaScript component that I would like to use to update some of the choice values for one of the Site Columns I created. Everything I see indicates I should have access to a spChoiceField.Choices.Add(value), or spChoiceField.AddChoice(value), or spChoiceField.set_choices(value) but none of these are valid for me.
I am working with code that looks like:
if (clientContext != undefined && clientContext != null) {
var web = clientContext.get_web();
fieldTitle = "TQM Requesting:";
fieldChoice = clientContext.castTo(web.get_availableFields().getByTitle(fieldTitle), SP.FieldChoice);
TQMtoAdd = TQMToInsert.value;
clientContext.load(fieldChoice);
I expect fieldChoice to provide one of the add functions but it does not.
I checked the following articles:
How to update Choice column in SharePoint
Update multiple choice field in sharepoint using rest api
Sharepoint choice field
Thank you,
Duncan
Tested script in my local to update choice field of host web in SharePoint hosted add-in.
<%-- The following 4 lines are ASP.NET directives needed when using SharePoint components --%>
<%# Page Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" MasterPageFile="~masterurl/default.master" Language="C#" %>
<%# Register TagPrefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%# Register TagPrefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%# Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%-- The markup and script in the following Content element will be placed in the <head> of the page --%>
<asp:Content ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<script type="text/javascript" src="../Scripts/jquery-1.9.1.min.js"></script>
<SharePoint:ScriptLink Name="sp.js" runat="server" OnDemand="true" LoadAfterUI="true" Localizable="false" />
<meta name="WebPartPageExpansion" content="full" />
<!-- Add your CSS styles to the following file -->
<link rel="Stylesheet" type="text/css" href="../Content/App.css" />
<!-- Add your JavaScript to the following file -->
<script type="text/javascript" src="/_layouts/15/sp.runtime.js"></script>
<script type="text/javascript" src="/_layouts/15/sp.js"></script>
<script type="text/javascript" src="../Scripts/App.js"></script>
<script type="text/javascript">
var appWebContext;
var listResult;
var hostweburl;
$(document).ready(function () {
UpdateChoice();
});
function UpdateChoice() {
appWebContext = new SP.ClientContext.get_current();
hostweburl = decodeURIComponent($.getUrlVar("SPHostUrl"));
var hostwebContext = new SP.AppContextSite(appWebContext, hostweburl);
var web = hostwebContext.get_web();
var fieldTitle = "MyChoice";
var fieldChoice = appWebContext.castTo(web.get_availableFields().getByTitle(fieldTitle), SP.FieldChoice);
appWebContext.load(fieldChoice);
appWebContext.executeQueryAsync(function () {
var newValues = "NewOption";//strStatusValues.split(",");
var currentChoices = fieldChoice.get_choices();
//for (var i = 0; i < newValues.length; i++) {
// currentChoices.push(newValues[i]);
//}
currentChoices.push(newValues);
fieldChoice.set_choices(currentChoices);
fieldChoice.updateAndPushChanges();
debugger;
appWebContext.executeQueryAsync(function () {
console.log("Added new choice values to the column");
}, function (sender, args) {
deferred.reject(args.get_message());
});
},
function (sender, args) {
deferred.reject(args.get_message());
});
}
// jQuery plugin for fetching querystring parameters
jQuery.extend({
getUrlVars: function () {
var vars = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for (var i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
},
getUrlVar: function (name) {
return jQuery.getUrlVars()[name];
}
});
</script>
</asp:Content>
<%-- The markup in the following Content element will be placed in the TitleArea of the page --%>
<asp:Content ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server">
Page Title
</asp:Content>
<%-- The markup and script in the following Content element will be placed in the <body> of the page --%>
<asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server">
<div>
<p id="message">
<!-- The following content will be replaced with the user name when you run the app - see App.js -->
initializing...
</p>
</div>
</asp:Content>
I have been working on the following code and with help from #squint, I have got to a point where it appears to work, but very erratic and randomly.
Code Edited with #TomasZato suggestions
<%#LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<!--#include file="../Connections/DVerto.asp" -->
<%
Dim Recordset1
Dim Recordset1_cmd
Dim Recordset1_numRows
Set Recordset1_cmd = Server.CreateObject ("ADODB.Command")
Recordset1_cmd.ActiveConnection = MM_DVerto_STRING
Recordset1_cmd.CommandText = "SELECT Part_Number FROM dbo.Stock_Header WHERE Part_Number like '84%'"
Recordset1_cmd.Prepared = true
Set Recordset1 = Recordset1_cmd.Execute
Recordset1_numRows = 0
%>
<%
Dim Repeat1__numRows
Dim Repeat1__index
Repeat1__numRows = 10
Repeat1__index = 0
Recordset1_numRows = Recordset1_numRows + Repeat1__numRows
%>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
</head>
<body onLoad="loadDoc()">
<table width="50%" border="0" cellspacing="2" cellpadding="2">
<%
While ((Repeat1__numRows <> 0) AND (NOT Recordset1.EOF))
%>
<tr>
<td class="prodref"><%=(Recordset1.Fields.Item("Part_Number").Value)%></td>
<td class="demo"> </td>
</tr>
<%
Repeat1__index=Repeat1__index+1
Repeat1__numRows=Repeat1__numRows-1
Recordset1.MoveNext()
Wend
%>
</table>
<script>
// This creates array of elements with requested class name - eg. [HTMLElement, HTMLElement ...]
var elements = document.getElementsByClassName("prodref");
var outputElements = document.getElementsByClassName("demo");
// Allways check if there is correct number of demo elements to print loaded data in
if(elements.length != outputElements.length) {
console.error("The number of prodref and demo elements is not the same!");
}
// Loop through both arrays of elements and make AJAX request for every one of them
for (var i=0, length=elements.length; i < length; i++) {
loadDoc(elements[i], outputElements[i]);
}
function loadDoc(element, demoElement) {
console.log("creating loadDoc() call for element", element);
var xhttp = new XMLHttpRequest();
var url = "data.asp?prodref="+element.innerHTML;
// Onload calls if request was successful
xhttp.onload = function() {
console.log("loadDoc() call for element", element, "succeeded");
demoElement.innerHTML = xhttp.responseText;
};
// Error calls if there is an error
xhttp.onerror = function() {
console.error("There was some problem with the request for element",element," with url '", url, "', check the Net debug panel.");
}
// Do not make sync requests, that makes page lag. Just DON'T!!!
xhttp.open("GET", url, false);
xhttp.send();
}
</script>
</body>
</html>
<%
Recordset1.Close()
Set Recordset1 = Nothing
%>
Code (Data.asp)
<%#LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<!--#include file="../Connections/PSCRM.asp" -->
<%
Dim rs_proditem__MMColParam
rs_proditem__MMColParam = "1"
If (Request.QueryString("prodref") <> "") Then
rs_proditem__MMColParam = Request.QueryString("prodref")
End If
%>
<%
Dim rs_proditem
Dim rs_proditem_cmd
Dim rs_proditem_numRows
Set rs_proditem_cmd = Server.CreateObject ("ADODB.Command")
rs_proditem_cmd.ActiveConnection = MM_PSCRM_STRING
rs_proditem_cmd.CommandText = "SELECT * FROM dba.proditem as t1 LEFT JOIN dba.proditem_xtra as t2 ON t1.prodref=t2.prodref WHERE t1.prodref = ? and rber_mi_source = 'M'"
rs_proditem_cmd.Prepared = true
rs_proditem_cmd.Parameters.Append rs_proditem_cmd.CreateParameter("param1", 200, 1, 25, rs_proditem__MMColParam) ' adVarChar
Set rs_proditem = rs_proditem_cmd.Execute
rs_proditem_numRows = 0
%>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
</head>
<div class="demo"><%=(rs_proditem.Fields.Item("proddesc").Value)%></div>
<body>
</body>
</html>
<%
rs_proditem.Close()
Set rs_proditem = Nothing
%>
When the page loads, some results are seen, sometimes returns 2 results, then more. Sometimes it misses a row out.
I have also noticed that on load the data in the third column <td class='demo'></td> which is being pulled from data.asp seems to change while the page is still loading?
Any help would be greatly accepted.
The problem is that you produced totally obscure code and you forgot what it's doing. First to the cause - I actually don't understand how come it ever works. First you do this:
a[i] = document.getElementsByClassName("prodref").innerHTML;
This is wrong because
a is HTMLElementCollection and is not supposed to be changed
document.getElementsByClassName("prodref") is an also HTMLElementCollection and has no property innerHTML
a[i] should now contain undefined, but since HTMLElementCollection array is immutable this code just doesn't do anything.
After this, you try to do this - technically it should work, because your wrong code above didn't do anything:
xhttp.open("GET", "data.asp?prodref="+a[i].innerHTML, true);
But it fails, which indicates there's an error you're hiding from me (eg. problem in the VBS script).
Fixed code with remarks
I decided to clean up and comment your code:
// This creates array of elements with requested class name - eg. [HTMLElement, HTMLElement ...]
var elements = document.getElementsByClassName("prodref");
var outputElements = document.getElementsByClassName("demo");
// Allways check if there is correct number of demo elements to print loaded data in
if(elements.length != outputElements.length) {
console.error("The number of prodref and demo elements is not the same!");
}
// Loop through both arrays of elements and make AJAX request for every one of them
for (var i=0, length=elements.length; i < length; i++) {
loadDoc(elements[i], outputElements[i]);
}
function loadDoc(element, demoElement) {
console.log("creating loadDoc() call for element", element);
var xhttp = new XMLHttpRequest();
var url = "data.asp?prodref="+element.innerHTML;
// Onload calls if request was successful
xhttp.onload = function() {
console.log("loadDoc() call for element", element, "succeeded");
demoElement.innerHTML = xhttp.responseText;
};
// Error calls if there is an error
xhttp.onerror = function() {
console.error("There was some problem with the request for element",element," with url '", url, "', check the Net debug panel.");
}
// Do not make sync requests, that makes page lag. Just DON'T!!!
xhttp.open("GET", url, false);
xhttp.send();
}
Of course, since you decided not to provide a good test page, I couldn't test this code.
I would like to know if it's possible to have a SP 2013 Hosted app that injects a piece of Javascript that gets executed on every page load.
For the sake of simplicity, imagine that I want to create an App that on every page load of the SP Site displays a alert('Hello world!');
I don't want to have a Remote Web, pure and simple Hosted App that can be added by anyone simply by picking it from the SP Store.
Is this possible?
Thanks!
You can inject the javascript using a custom action script link as #AlexCode suggests but the app will require web - full control permissions. I can't remember where I adapted this code from while I was looking into add-in development. Also this is for POC only you should probably look to make it more robust before using it in a live environment.
App.js contents
(function(undefined) {
"use strict";
var actions, web, context, hostContext, actionDescription;
console.log('running function');
// getQueryStringParameter: method to retrieve query string parameter values
var getQueryStringParameter = function(param) {
var params = document.URL.split('?')[1].split('&');
var length = params.length;
for (var i = 0; i < length; i = i + 1) {
var singleParam = params[i].split('=');
if (singleParam[0] == param) {
return singleParam[1];
}
}
};
// inject: method to return as a string the js that will be ran by the custom action
var inject = function() {
debugger;
var scriptToRun;
scriptToRun += '(function (){' +
'var elem = document.getElementsByTagName("head")[0];' +
'var script = document.createElement("script");' +
'script.appendChild(document.createTextNode(alert("hello world")));' +
'elem.appendChild(script);' +
'}());';
return scriptToRun;
};
var success = function() {
alert('Done');
}
var fail = function() {
alert('Failed');
}
// unprovision: removes the custom action and the JavaScript file
var unprovision = function() {
context = SP.ClientContext.get_current();
hostContext = new SP.AppContextSite(context, decodeURIComponent(getQueryStringParameter('SPHostUrl')));
// load the custom actions from the host web
actions = hostContext.get_web().get_userCustomActions();
context.load(actions);
web = hostContext.get_web();
context.load(web);
context.executeQueryAsync(unprovisionEx, fail);
};
// unprovisionEx: method to remove the custom action
var unprovisionEx = function() {
var enumerator = actions.getEnumerator();
var removeThese = [];
// find the custom action
while (enumerator.moveNext()) {
var action = enumerator.get_current();
if (action.get_description() == actionDescription && action.get_location() == 'ScriptLink') {
// add it to a temporary array (we cannot modify an enumerator while enumerating)
removeThese.push(action);
}
}
// do the actual removal of the custom action
var length = removeThese.length;
for (var i = 0; i < length; i++) {
removeThese[i].deleteObject();
delete removeThese[i];
}
context.executeQueryAsync(success, fail);
};
// provisionScriptLink: method that adds the custom action
var provisionScriptLink = function() {
var enumerator = actions.getEnumerator();
var removeThese = [];
// check if the custom action already exists, if it does then remove it before adding the new one
while (enumerator.moveNext()) {
var action = enumerator.get_current();
if (action.get_description() == actionDescription && action.get_location() == 'ScriptLink') {
removeThese.push(action);
}
}
var length = removeThese.length;
for (var i = 0; i < length; i++) {
removeThese[i].deleteObject();
delete removeThese[i];
}
// create the custom action
var newAction = actions.add();
// the 'description' is what we'll use to uniquely identify our custom action
newAction.set_description(actionDescription);
newAction.set_location('ScriptLink');
newAction.set_scriptBlock(inject());
newAction.update();
context.executeQueryAsync(success, fail);
};
// provision: starts with uploading the JavaScript file to the host we, once done it will continue with the provisionScriptLink() method
var provision = function() {
context = SP.ClientContext.get_current();
hostContext = new SP.AppContextSite(context, decodeURIComponent(getQueryStringParameter('SPHostUrl')));
// load the custom actions from the host web
actions = hostContext.get_web().get_userCustomActions();
context.load(actions);
web = hostContext.get_web();
context.load(web);
context.executeQueryAsync(provisionScriptLink, fail);
};
document.getElementById("add").onclick = provision;
}());
Default.apsx content
<%-- The following 4 lines are ASP.NET directives needed when using SharePoint components --%>
<%# Page Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" MasterPageFile="~masterurl/default.master" Language="C#" %>
<%# Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%# Register TagPrefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%# Register TagPrefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%-- The markup and script in the following Content element will be placed in the <head> of the page --%>
<asp:Content ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<script type="text/javascript" src="/_layouts/15/sp.runtime.js"></script>
<script type="text/javascript" src="/_layouts/15/sp.js"></script>
<!-- Add your CSS styles to the following file -->
<link rel="Stylesheet" type="text/css" href="../Content/App.css" />
</asp:Content>
<%-- The markup in the following Content element will be placed in the TitleArea of the page --%>
<asp:Content ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server">
Page Title
</asp:Content>
<%-- The markup and script in the following Content element will be placed in the <body> of the page --%>
<asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server">
<div>
<button type="button" value="add" name="add" id="add">Add</button>
</div>
</asp:Content>
<asp:Content ContentPlaceHolderID="PlaceHolderUtilityContent" runat="server">
<!-- Add your JavaScript to the following file -->
<script type="text/javascript" src="../Scripts/App.js"></script>
</asp:Content>
You can provide a custom master page to the host site from app site via javascript. Anyway the host site must use the new master page.
You can see this article for more info
I have four same UserControls inserted into Default.aspx. I want to write the data into HiddenField and write this data into Label1 using javascript.
The method only works for the last loaded UserControl - HiddenFieldLoader3.
Why the method does not work in all usercontrols? How can i fix my code?
Default.aspx
<%# Register Src="~/HiddenFieldLoader.ascx" TagPrefix="uc1" TagName="HiddenFieldLoader" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Default.aspx</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="HiddenFieldLoader.js"></script>
</head>
<body>
<form id="form1" runat="server">
<uc1:HiddenFieldLoader runat="server" ID="HiddenFieldLoader" />
<uc1:HiddenFieldLoader runat="server" ID="HiddenFieldLoader1" />
<uc1:HiddenFieldLoader runat="server" ID="HiddenFieldLoader2" />
<uc1:HiddenFieldLoader runat="server" ID="HiddenFieldLoader3" />
</form>
</body>
</html>
UserControl: HiddenFieldLoader.ascx
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="HiddenFieldLoader.ascx.cs" Inherits="NewControls.HiddenFieldLoader" %>
<script type="text/javascript">
HFLoader.declareVariables("<%=Button1.ClientID %>", "<%=HiddenField1.ClientID %>", "<%=Label1.ClientID %>");
</script>
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
<asp:HiddenField ID="HiddenField1" runat="server" />
<input type="button" ID="Button1" value="Save" runat="server" />
<input type="button" ID="Button2" onclick="HFLoader.showEvent();" value="Show" runat="server" />
Javascript file: HiddenFieldLoader.js
HFLoader = {
button: null,
hiddenField: null,
label: null,
declareVariables: function (btn, hf, label) {
HFLoader.button = btn;
HFLoader.hiddenField = hf;
HFLoader.label = label;
},
///////SHOW EVENT
showEvent: function () {
$("#" + HFLoader.label).html($("#" + HFLoader.hiddenField).val());
},
//////!SHOW EVENT
saveHiddenField: function (data) {
$("#" + HFLoader.hiddenField).val(data);
},
buttons: function () {
$("#" + HFLoader.button).click(function () {
var datatest = "Data from button ID: " + $(this).attr("id");
HFLoader.saveHiddenField(datatest);
$("#" + HFLoader.label).html($("#" + HFLoader.hiddenField).val());
return false;
});
},
init: function () {
$(function () {
HFLoader.buttons();
});
}
};
HFLoader.init();
It doesn't work because you're using a single instance variable, HFLoader, and not an instance.
So one way to fix it is to wrap your HFLoader in a closer (function, local scope) so that the object is scoped per method call. In the example below, I wrapped the HFLoader variable in a creator function. Then, each call to HFLoaderCreator will produce a different version (instance) of the HFLoader object. The reason I chose to do it this way instead of creating a true JS class was because it required minimal code changes to demonstrate how it could be done.
Edit. Let's also say you want to recall the HFLoader at a later date to call on things, such as the saveHiddenField method. To do this, I've done 2 this. 1) I've added data('HFLoader') to the three elements passed in, so you can use any one of them to recall the HFLoader settings. And 2) I've added custom events that you can trigger to call the methods. These two options show two different ways that jQuery plugin developers use to allow access to underlying structures.
<script type="text/javascript">
HFLoaderCreator("<%=Button1.ClientID %>", "<%=HiddenField1.ClientID %>", "<%=Label1.ClientID %>");
</script>
function HFLoaderCreator(btn, hf, label)
{
var HFLoader = {
button: null,
hiddenField: null,
label: null,
declareVariables: function (btn, hf, label) {
HFLoader.button = btn;
HFLoader.hiddenField = hf;
HFLoader.label = label;
},
saveHiddenField: function (data) {
$("#" + HFLoader.hiddenField).val(data);
},
buttons: function () {
$("#" + HFLoader.button).click(function () {
var datatest = "Data from button ID: " + $(this).attr("id");
HFLoader.saveHiddenField(datatest);
$("#" + HFLoader.label).html($("#" + HFLoader.hiddenField).val());
return false;
});
// add the data so it can be recalled at a later date
$("#" + HFLoader.button).data('HFLoader', HFLoader);
$("#" + HFLoader.hiddenField).data('HFLoader', HFLoader);
$("#" + HFLoader.label).data('HFLoader', HFLoader);
// add custom event handlers - saveHiddenField
$("#" + HFLoader.button).on('saveHiddenField', HFLoader.saveHiddenField);
$("#" + HFLoader.hiddenField).on('saveHiddenField', HFLoader.saveHiddenField);
$("#" + HFLoader.label).on('saveHiddenField', HFLoader.saveHiddenField);
// add custom event handlers - showEvent
$("#" + HFLoader.button).on('showEvent', HFLoader.showEvent);
$("#" + HFLoader.hiddenField).on('showEvent', HFLoader.showEvent);
$("#" + HFLoader.label).on('showEvent', HFLoader.showEvent);
},
init: function () {
$(function () {
HFLoader.buttons();
});
}
};
HFLoader.declareVariables(btn, hf, label);
HFLoader.init();
return HFLoader;
}
So now to make use of it all. In JS, if you wanted to look up the HFLoader data based on the Button1 ID, you might do something like this:
<script type="text/javascript">
var HFLoader = $('#<%=Button1.ClientID %>').data('HFLoader');
HFLoader.showEvent();
</script>
If you wanted to use ButtonID1 to trigger the custom event, which would ultimately call the showEvent method, you could do:
<script type="text/javascript">
$('#<%=Button1.ClientID %>').trigger('showEvent');
</script>
And if you wanted to do it inline, like your first example...
<input type="button" ID="Button2" onclick="$('#<%=Button1.ClientID %>').data('HFLoader').showEvent();" value="Show" />
Although I'd highly recommend instead using JS to wire an event to Button2, such as:
$(function()
{
$('#<%=Button2.ClientID').click(function()
{
$('#<%=Button1.ClientID %>').data('HFLoader').showEvent();
return false;
});
});
That way it's not inline script. Overall, the script for HFLoader is a bit hacked together. I'd recommend taking a look at the examples I've put together to extract the pieces of useful information and consider rethinking and/or rewriting the way you're doing things. Your brain is pushing you towards some kind of "global" view, or some kind of automatically scoped variable called HFLoader. But if you have 5 instances on a page, then you really want instances of some HFLoader object and not some global concept of HFLoader. It's like creating a new instance of a class instead of using a static or singleton instance of a class -- two very different concepts!
I hope this helps.
This is my view in ASP.NET MVC.
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Administration.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="HeadContent" runat="server">
<script type="text/javascript">
var ddlContentTypes;
$(document).ready(function () {
ddlContentTypes = $("#ContentTypes");
ddlContentTypes.bind("change", loadCreate);
loadCreate();
});
function loadCreate() {
var typeId = $("#ContentTypes option:selected").val();
$.get('~/' + typeId + '/Create', function (data) {
$("#CreateForm").html(data);
});
$("fieldset input").each(function (index, item) {
$(item).attr("disabled", true);
});
}
</script>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
<h2>
<%=Resources.Localize.CreateWidget %></h2>
<p>
<% Html.RenderPartial("ContentTypeSelector"); %></p>
<div id="CreateForm">
</div>
</asp:Content>
As you can see, it loads some HTML (actually user control) and adds it to the CreateForm div. This actually works fine.
The problem is this
$("fieldset input").each(function (index, item) {
$(item).attr("disabled", true);
});
never runs. The fieldset tag is in the response, so you don't see it here but it's there - everything is returned back fine (I checked with Firebug).
Why do the above two lines of JS never run or have any effect?
The fieldset tag doesn't exist when you call this code. Try moving this code to the inside of your success function and it might work.
function loadCreate() {
var typeId = $("#ContentTypes option:selected").val();
$.get('~/' + typeId + '/Create', function (data) {
$("#CreateForm").html(data);
$("fieldset input").each(function (index, item) {
$(item).attr("disabled", true);
});
});
}
I think your problem is here:
$.get('~/' + typeId + '/Create', function (data) {
$("#CreateForm").html(data);
});
should be:
$.get("<%=ResolveUrl("~/") %>" + typeId + "/Create", function (data) {
$("#CreateForm").html(data); // thanks Peter
$("fieldset input").attr("disabled", "disabled"); // thanks Nick
});
That's probably tossing a js exception and it's never getting to the fieldset loop.