Skuid v2 and Javascript - Docs obsolete?

I’m on Skuid 15.3.6 at the moment.
Trying to dig into the List component to see if I can ‘select’ an item so it shows as selected, like if you click on it.

Looking at Using Skuid with Javascript: A Primer page
https://docs.skuid.com/v15.3.6/v2/en/skuid/javascript/javascript-primer.html
Working thru the page it goes off the rails right at “Create a test page”, having you do things that, at least I’m, not finding in my copy of Skuid. New Page doesn’t have Next Step, or Starting point with Use a page template

Making a test Skuid page, and continuing,
I can type skuid in the console, and the skuid object is returned.

The primer then says to type skuid.model.map() into the console and " Skuid will return an object that has all of your model IDs as its properties, with the model data as values of those properties."

I just get an error

VM49730:1 Uncaught TypeError: Cannot read properties of undefined (reading ‘map’)
at :1:13

Is there something I don’t have enabled? or just obsolete docs listed as for current Skuid versions?

Hey Seth! Apologies for the delayed response and the confusion this doc caused: you’re right that this page needs updating. It was originally written for v1 pages, so there’s been some changes.

The main problems:

  • Page templates are no longer available during page creation in v2.
  • In v2 we need a page context for the skuid API. The easiest way to do that at runtime is skuid.debug.page() (More info on why below, but first let’s see how this changes the examples).

Working examples

First step is to get a working page. You mentioned already having one, but you could also use the XML for the list page tutorial.

Next (and I’ll be updating the doc to reflect this) create a reference to skuid.debug.page() and use that in place of skuid in the current examples:

let skuidPage = skuid.debug.page()

So with that set, anytime you see skuid, you use skuidPage instead:

let modelsObject = skuidPage.model.map()
let modelsArray = skuidPage.model.getModelList()

So why does it work this way? In v2, Skuid pages are sandboxed, and the skuid API doesn’t make an assumption about which page to access. Sandboxing helps keep models and things from trampling over each other when you have a lot of pages in one browser window (think Page Include components or perhaps multiple pages within a Salesforce Lightning page). But this means the skuid API doesn’t have access to page-specific data, aka skuid doesn’t have access to the APIs we list in that topic. This is why the current doc leads you to that undefined error.

Using skuid.debug.page() we can easily access a page’s APIs at runtime (it’s also possible with skuid.runtime.getPage(), though that’s more advanced than we need, especially for a primer.) For more info, see this section on sandboxing.

Caveats to remember

  • Everything above assumes you only have one Skuid page in the browser window. Probably a safe assumption if you’re following along with the primer, but if you’re trying this in a page with Page Include components or within a Lightning page with multiple Skuid pages, you’ll likely need to specify which page by providing a name for the filter parameter in the debug API, like this: skuid.debug.page('AccountsList')

  • It’s important to note that writing a snippet for a Skuid page would still require skuid.model.map(). That’s because a snippet, by nature of being in a Skuid page, knows the page context and has access to those APIs. Said another way, there’s no need to use skuid.debug (which doesn’t work in snippets anyway). When writing a JavaScript snippet stored in a Skuid page, skuid.model.map() would be the API you’d want to use.

Documentation updates to be made

  • Remove page template instructions and instead suggest creating a list page and/or providing XML to download
  • Add a note to the top of that document clarifying the behavior at runtime vs when writing snippets.
  • Update code samples to use skuid.debug.page()–likely by creating a reference at the start of the primer to avoid typing it every time
  • Outside of the primer, but another update: note the use of skuid.debug.page() instead of just skuid.runtime.getPage() in our sandboxing section

Thanks for bringing this to our attention. Let me know if you have more questions!

Thanks Cody,

That helps some.

I’m trying to ‘finish the job’ on a console page: List of Campaigns on the left, page include for campaign details on the right.

I have an interaction on the list to reload the page-include for the selected item (row).
The list is setup to highlight the item on hover, and make it ‘selected’ when you click on it, and it reloads the page-include with the model row in context. Selecting the list row makes it clear which item in the list your working with.

On the console page-load, I have the page-include default to loading with the first row in the model.

I’m trying to make the list on the left, have the corresponding first ‘row’ start in ‘selected’ mode. So it indicates what item in the list is displayed with the page-include.

Basically make the UX/UI operate consistently. The only other thing I can think of, is have the page-include have a none-selected mode which displays “Select a Campaign on the left to start.” That seems like a waste too. Missed opportunities all around.

In the JS console, I can dig into the model rows, but as expected, there doesn’t seem to be a ‘selected’ flag. I’d expect the ‘item selected mode’ setting to be in the List object.

I can get to the list skuidpage.component.getByType(‘skuid__list’)[0].
I can see Symbol(Private).state which seems to have data, but :person_shrugging: on what to look at and how to access it (since Symbol(Private) seems to be private.

I’m hoping/expecting for something like list.item[0].selected = true

Thanks,
Seth

Ahhh yeah, unfortunately I’m not sure I’m much help there. It sounds like you’ve got all the other functionality in place, and you definitely got to the List component via JS in the correct way. However there aren’t currently any declarative actions or supported APIs for selecting a particular List item. Hopefully we can look into adding some functionality around that.

All of the workaround ideas I can think of involve using UI-only fields. Maybe an action that sets an isSelected UI-only field on the row to true while marking all other rows as false, and then using that field for some display logic… But I’m not sure how much further that could take the UX you have in place without some refactoring.

Hey Cody,

Try this page out. In order for this to work you’ll need to select a theme and add a child button to circular and set the background and icon colors.

<skuid__page unsavedchangeswarning="yes" personalizationmode="server" showsidebar="false" showheader="false" theme="TEST">
	<models>
		<model id="Accounts" limit="20" query="true" createrowifnonefound="false" datasource="salesforce" sobject="Account" unloadwarningifunsavedchanges="false">
			<fields>
				<field id="RecordTypeId"/>
				<field id="RecordType.Name"/>
				<field id="Name"/>
				<field id="Id"/>
				<field id="Selected" uionly="true" displaytype="BOOLEAN" length="255" label="Selected" ogdisplaytype="TEXT" defaultvaluetype="fieldvalue" defaultValue="false"/>
			</fields>
			<conditions/>
			<actions>
				<action>
					<actions>
						<action type="action-sequence" action-sequence-id="fb9cbccd-da4e-4b6c-8ed9-39dc4990ec30"/>
					</actions>
					<events>
						<event>models.loaded</event>
					</events>
				</action>
			</actions>
		</model>
		<model id="SelectedAccount" limit="" query="false" createrowifnonefound="false" datasource="salesforce" sobject="Account">
			<fields>
				<field id="RecordTypeId"/>
				<field id="RecordType.Name"/>
				<field id="Name"/>
				<field id="Id"/>
				<field id="Selected" uionly="true" displaytype="BOOLEAN" length="255" label="Selected" ogdisplaytype="TEXT" defaultvaluetype="fieldvalue" defaultValue="false"/>
			</fields>
			<conditions/>
			<actions/>
		</model>
	</models>
	<components>
		<skuid__grid uniqueid="sk-3RVn-20159" flexDirection="row" justifyContent="flex-start" alignItems="flex-start">
			<divisions>
				<division minWidth="400px" ratio="1" alignSelf="auto" maxWidth="400px">
					<components>
						<skuid__list uniqueid="sk-3RVj-18234" model="Accounts" pageSize="10" rowActionsOnLeft="true" showListFooter="true">
							<columns>
								<column fillRatio="1">
									<items>
										<item displayType="template" template="{{{Name}}}"/>
									</items>
								</column>
							</columns>
							<rowActions>
								<action type="multi" icon="sk-webicon-ink:arrow-right">
									<actions>
										<action type="updateRow" fieldmodel="Accounts" affectedrows="all">
											<updates>
												<update valuesource="fieldvalue" field="Selected" enclosevalueinquotes="false" value="false"/>
											</updates>
										</action>
										<action type="updateRow" fieldmodel="Accounts" affectedrows="context">
											<updates>
												<update valuesource="fieldvalue" field="Selected" enclosevalueinquotes="false" value="true"/>
											</updates>
										</action>
										<action type="emptyModelData">
											<models>
												<model>SelectedAccount</model>
											</models>
										</action>
										<action type="adoptRows" sourcemodel="Accounts" targetmodel="SelectedAccount" affectedrows="context"/>
										<action type="runComponentAction" componentid="sk-3RVt-22345" action="unload"/>
										<action type="runComponentAction" componentid="sk-3RVt-22345" action="load" includeType="skuid"/>
									</actions>
									<renderConditions logictype="and"/>
									<enableConditions/>
									<styleVariantConditions>
										<rule logictype="and" styleSettingsVariantOverride="4eaed3db-f1f0-44b7-81a4-c4348946d639">
											<condition type="fieldvalue" operator="=" enclosevalueinquotes="false" fieldmodel="Accounts" sourcetype="fieldvalue" nosourcerowbehavior="deactivate" field="Selected" fieldtargetobjects="Account" value="true"/>
										</rule>
										<rule logictype="and" styleSettingsVariantOverride="circular">
											<condition type="fieldvalue" operator="=" enclosevalueinquotes="false" fieldmodel="Accounts" sourcetype="fieldvalue" nosourcerowbehavior="deactivate" field="Selected" fieldtargetobjects="Account" value="false"/>
										</rule>
									</styleVariantConditions>
								</action>
							</rowActions>
							<massActions/>
							<filtering enableSearch="false"/>
							<sorting enable="false"/>
						</skuid__list>
					</components>
				</division>
				<division alignSelf="auto" minWidth="100px" ratio="1">
					<components>
						<skuid__pageInclude includeType="skuid" uniqueid="sk-3RVt-22345" pageName="AccountRecord" queryString="acc-id={{$Model.SelectedAccount.data.0.Id}}" lazyLoad="false" showLoadingIndicator="false">
							<renderConditions logictype="and"/>
						</skuid__pageInclude>
					</components>
				</division>
			</divisions>
		</skuid__grid>
	</components>
	<resources>
		<labels/>
		<javascript/>
		<css/>
		<actionsequences>
			<actionsequence id="df2417c5-22d1-4a75-b61f-927f85b7c98a" label="Page Rendered" type="event-triggered" event-scope="component" event-name="page.rendered">
				<description/>
				<actions>
					<action type="action-sequence" action-sequence-id="fb9cbccd-da4e-4b6c-8ed9-39dc4990ec30"/>
				</actions>
			</actionsequence>
			<actionsequence id="fb9cbccd-da4e-4b6c-8ed9-39dc4990ec30" label="Set all as unselected" type="reusable">
				<description/>
				<actions>
					<action type="updateRow" fieldmodel="Accounts" affectedrows="all">
						<updates>
							<update valuesource="fieldvalue" field="Selected" enclosevalueinquotes="false" value="false"/>
						</updates>
					</action>
					<action type="save">
						<models>
							<model>Accounts</model>
						</models>
					</action>
				</actions>
			</actionsequence>
		</actionsequences>
	</resources>
	<styles>
		<styleitem type="background" bgtype="none"/>
	</styles>
</skuid__page>

Hi Pat,

Cody was responding to my question. I like what you did. I made the list click-interaction do update ui-only selected field dance. Very slick.

As an aside, today I learned about UNLOADING a page include. Is that needed? I was just loading over existing page includes.

I need to have the first row of the model be ‘selected’ on the inital page load/render. I’d put it in the Event:“Skuid Page: Rendered” action sequence, but at least with action framework, there’s no way to update one specific row of a model.

Presumably I can use a Javascript snippet action to update the first row selected UI-only field. But given the state of the docs that’s probably a bridge too far.

Thanks

You can do this decoratively. Just not elegantly. You can target an id within a model using merge syntax like {{$Model.Accounts.data.0.Id}} to set the condition on another model in order to isolate it. Then update that row in this other model and then adopt it back into the original. :face_vomiting: