I am writing a plugin for gerrit 2.16.12 that should show a button for starting an extended build stage on our Jenkins for a particular change. My problems are two.
It seems that to show the button I need to reload the page once. If I go back to the page later, I still need to do a reload.
If I press the action, the plugin shows a popup with some values filled in. From there I can submit the build with the parameters needed. But if I press the button a second time, the values are not filled in and the submit button in the form does not work.
Perhaps they are related, not sure. Any ideas?
This is my plugin html file:
<dom-module id="build-all-stages">
<script>
//alert("Gerrit plugin is being installed");
console.log("Loading build-all-stages plugin");
Gerrit.install(plugin => {
changeActions = plugin.changeActions();
key = changeActions.add(changeActions.ActionType.CHANGE, 'Build All Stages');
changeActions.addTapListener(key, ev => {
plugin.popup("build-all-stages-mod").then(popup => {
el = document.querySelector('gr-change-metadata');
if (el == null) {
alert("Could not find the gr-change-metadata element");
popup.close();
return;
}
change = el.change;
revision = el.revision;
bast = document.querySelector('build-all-stages-mod');
if (bast == null) {
alert("Could not find the plugin element.");
popup.close();
return;
}
bast.addEventListener('done', () => {
console.log("Closing popup");
popup.close();
});
bast.gerrit_project = change.project;
bast.gerrit_branch = change.branch;
bast.gerrit_newrev = change.branch;
bast.gerrit_refname = change.branch;
bast.gerrit_refspec = revision.ref;
bast.gerrit_patchset_revision = revision.commit.commit;
});
});
});
console.log("Loaded build-all-stages plugin");
</script>
</dom-module>
<dom-module id="build-all-stages-mod">
<template>
<h2>Build all stages in Jenkins</h2>
Please make sure that you are allready logged in to Jenkins and then verify the values below pefore pressing submit:
<form id="jenkins_form"
method="post"
on-submit="handleDone"
target="_blank"
action="https://jenkins.mycompany.com/job/[[gerrit_branch]]/job/Stage0-All_pipeline_stages-Test/buildWithParameters">
<table>
<tr><td>GERRIT_PROJECT: </td><td> <input name="GERRIT_PROJECT" size=40 value="[[gerrit_project]]"/></td></tr>
<tr><td>GERRIT_BRANCH: </td><td> <input name="GERRIT_BRANCH" size=40 value="[[gerrit_branch]]"/></td></tr>
<tr><td>GERRIT_NEWREV: </td><td> <input name="GERRIT_NEWREV" size=40 value="[[gerrit_newrev]]"/></td></tr>
<tr><td>GERRIT_REFNAME: </td><td> <input name="GERRIT_REFNAME" size=40 value="[[gerrit_refname]]"/></td></tr>
<tr><td>GERRIT_REFSPEC: </td><td> <input name="GERRIT_REFSPEC" size=40 value="[[gerrit_refspec]]"/></td></tr>
<tr><td>GERRIT_PATCHSET_REVISION: </td><td> <input name="GERRIT_PATCHSET_REVISION" size=40 value="[[gerrit_patchset_revision]]"/></td></tr>
</table>
<input name="userFlag" value="true" type="hidden"/>
<button type="submit" value="Submit">Submit</button>
<button id="jenkins_cancel" type="button" on-click="handleDone">Cancel</button>
</form>
</template>
<style>
:host {
display: block;
border: 1px solid black;
padding: 5px;
}
td {
padding: 3px;
}
</style>
<script>
gerrit_type = {
type: String,
value: "",
notify: false,
reflectToAttribute: true
};
gerrit_type = String;
Polymer({
is: 'build-all-stages-mod',
properties: {
gerrit_project: gerrit_type,
gerrit_branch: gerrit_type,
gerrit_newrev: gerrit_type,
gerrit_refname: gerrit_type,
gerrit_refspec: gerrit_type,
gerrit_patchset_revision: gerrit_type,
},
handleDone: function() {
this.fire('done', null);
}
});
</script>
The plugin should listen to the showChange event so the button will display automatically.
Gerrit.install(plugin => {
plugin.on('showchange', params => {
console.log(params)
const changeActions = plugin.changeActions();
changeActions.add('revision', 'display name');
//...
}
}
Related
I've created a little Fiddle to illustrate the issue: https://stackblitz.com/edit/react-avejvc-mmhqda?file=index.js
This form works:
<Form initialValues={{ surname: 'Mouse'}}>
<Form.Item name="surname">
<Input />
</Form.Item>
</Form>
This form doesn't:
<Form initialValues={{ surname: 'Mouse'}}>
<Form.Item name="surname">
<Input />
{null}
</Form.Item>
</Form>
The only difference is that the Form.Item in the second form has two children.
Is there an intention behind this?
In case anyone wonders why I am asking. So sth like this is breaking the form:
<Form.Item name={name}>
{type==="string" && <Input />}
{type==="integer" && <InputNumber />}
</Form.Item>
The official documentation here gives examples of using multiple children in one Form.Item.
<Form.Item label="Field">
<Form.Item name="field" noStyle><Input /></Form.Item> // that will bind input
<span>description</span>
</Form.Item>
You appear to have a problem with what you are putting in the Form.Item, ie. {null} may not be allowed.
I found a solution and have a better understanding now of what is going on.
From the docs (https://ant.design/components/form/#Form.Item):
After wrapped by Form.Item with name property, value(or other property defined by valuePropName) onChange(or other property defined by trigger) props will be added to form controls, the flow of form data will be handled by Form
There is a working example in the docs too, here is the codepen: https://codepen.io/pen?&editors=001
const { useState } = React;;
const { Form, Input, Select, Button } = antd;
const { Option } = Select;
const PriceInput = ({ value = {}, onChange }) => {
const [number, setNumber] = useState(0);
const [currency, setCurrency] = useState('rmb');
const triggerChange = (changedValue) => {
onChange?.({
number,
currency,
...value,
...changedValue,
});
};
const onNumberChange = (e) => {
const newNumber = parseInt(e.target.value || '0', 10);
if (Number.isNaN(number)) {
return;
}
if (!('number' in value)) {
setNumber(newNumber);
}
triggerChange({
number: newNumber,
});
};
const onCurrencyChange = (newCurrency) => {
if (!('currency' in value)) {
setCurrency(newCurrency);
}
triggerChange({
currency: newCurrency,
});
};
return (
<span>
<Input
type="text"
value={value.number || number}
onChange={onNumberChange}
style={{
width: 100,
}}
/>
<Select
value={value.currency || currency}
style={{
width: 80,
margin: '0 8px',
}}
onChange={onCurrencyChange}
>
<Option value="rmb">RMB</Option>
<Option value="dollar">Dollar</Option>
</Select>
</span>
);
};
const Demo = () => {
const onFinish = (values) => {
console.log('Received values from form: ', values);
};
const checkPrice = (_, value) => {
if (value.number > 0) {
return Promise.resolve();
}
return Promise.reject(new Error('Price must be greater than zero!'));
};
return (
<Form
name="customized_form_controls"
layout="inline"
onFinish={onFinish}
initialValues={{
price: {
number: 0,
currency: 'rmb',
},
}}
>
<Form.Item
name="price"
label="Price"
rules={[
{
validator: checkPrice,
},
]}
>
<PriceInput />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
ReactDOM.render(<Demo />, mountNode);
How to pass default value in md-autocomplete?
Image 1 : HTML code
Image 2 : JS code
Image 3 : Output
As you can see, I am not getting any default country as output. Is there any way to do that?
Assign yr SearchText the default value & selectedItem the object.
$scope.local ={
...
searchText : 'Default Value',
selectedItem : 'Default object'
...
}
I write small codepen with autocomplete and default value.
What you must do:
Init main model.
Define model field, used in autocomplete md-selected-item property.
Define callback for loading autocomplete items.
Before save main model extract id (or other field) from associated field.
Main error in your code here:
$scope.local = {
...
selectedItem: 1, // Must be object, but not integer
...
}
(function(A) {
"use strict";
var app = A.module('app', ['ngMaterial']);
function main(
$q,
$scope,
$timeout
) {
$timeout(function() {
$scope.user = {
firstname: "Maxim",
lastname: "Dunaevsky",
group: {
id: 1,
title: "Administrator"
}
};
}, 500);
$scope.loadGroups = function(filterText) {
var d = $q.defer(),
allItems = [{
id: 1,
title: 'Administrator'
}, {
id: 2,
title: 'Manager'
}, {
id: 3,
title: 'Moderator'
}, {
id: 4,
title: 'VIP-User'
}, {
id: 5,
title: 'Standard user'
}];
$timeout(function() {
var items = [];
A.forEach(allItems, function(item) {
if (item.title.indexOf(filterText) > -1) {
items.push(item);
}
});
d.resolve(items);
}, 1000);
return d.promise;
};
}
main.$inject = [
'$q',
'$scope',
'$timeout'
];
app.controller('Main', main);
}(this.angular));
<head>
<link href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/0.11.0/angular-material.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular-aria.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular-animate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-material/0.11.0/angular-material.min.js"></script>
</head>
<body ng-app="app" flex layout="column" layout-margin ng-controller="Main">
<md-content layout="column" class="md-whiteframe-z1" layout-margin>
<md-toolbar>
<div class="md-toolbar-tools">
<h3>Form</h3>
</div>
</md-toolbar>
<md-content class="md-whiteframe-z1">
<div class="md-padding">
<md-input-container>
<label for="firstname">First name</label>
<input type="text" name="firstname" ng-model="user.firstname" />
</md-input-container>
<md-input-container>
<label for="lastname">Last name</label>
<input type="text" name="lastname" ng-model="user.lastname" />
</md-input-container>
<md-autocomplete md-selected-item="user.group" md-items="item in loadGroups(filterText)" md-item-text="item.title" md-search-text="filterText">
<md-item-template>{{ item.title }}</md-item-template>
<md-not-found>No items.</md-not-found>
</md-autocomplete>
</div>
</md-content>
</md-content>
<md-content class="md-whiteframe-z1" layout-margin>
<md-toolbar>
<div class="md-toolbar-tools">
<h3>Model as JSON</h3>
</div>
</md-toolbar>
<md-content class="md-padding">
<p>
{{ user | json }}
</p>
</md-content>
</md-content>
</body>
I know this is an old question, but some people may benefit from my solution. I struggled with the problem of the model for the auto-complete being asynchronous and having my autocompletes as part of an ng-repeat. Many of the solutions to this problem found on the web have only a single auto complete and static data.
My solution was to add another directive to the autocomplete with a watch on the variable that I want to set as default for the auto complete.
in my template:
<md-autocomplete initscope='{{quote["Scope"+i]}}' ng-repeat='i in [1,2,3,4,5,6,7,8]'
class='m-1'
md-selected-item="ScopeSelected"
md-clear-button="true"
md-dropdown-position="top"
md-search-text="pScopeSearch"
md-selected-item-change='selectPScope(item.label,i)'
md-items="item in scopePSearch(pScopeSearch,i)"
md-item-text="item.label"
placeholder="Plowing Scope {{i}}"
md-min-length="3"
md-menu-class="autocomplete-custom-template"
>
then in my module:
Details.directive('initscope', function () {
return function (scope, element, attrs) {
scope.$watch(function (){
return attrs.initscope;
}, function (value, oldValue) {
//console.log(attrs.initscope)
if(oldValue=="" && value!="" && !scope.initialized){
//console.log(attrs.initscope);
var item = {id:0, order:0,label:attrs.initscope?attrs.initscope:"" }
scope.ScopeSelected = item;
scope.initialized = true;
}
});
};
});
this checks for changes to the quote["Scope"+i] (because initially it would be null) and creates an initial selected item and sets the autocompletes' selected item to that object. Then it sets an initialized value to true so that this never happens again.
I used timeout to do this.
$timeout(function() {
$scope.local = {selectedItem : 1}
}, 2000);
Im trying to apply the JQuery UI highlight effect to an element when an item that is bound to a knockout observablearray is updated.
The highlight effect is applied but the highlight color used is always the elements current background color. even if I specify the highlight color using the { color: 'XXXXXXX' } option.
any ideas what might be happening?
Thanks,
Steve.
Code below: The element is the span.tag
<div class="row">
<div class="span12">
<div class="tagsinput favs span12" style="height: 100%;" data-bind="foreach: favs, visible: favs().length > 0">
<span class="tag" data-bind="css: $root.selectedFav() == userPrefID() ? 'selected-fav' : '', attr: { id: 'fav_' + userPrefID() }">
<span data-bind="text: name, click: $root.loadFav.bind($data)"></span>
<a class="tagsinput-fav-link"><i class="icon-trash" data-bind="click: $root.delFav.bind($data)"></i></a>
<a class="tagsinput-fav-link-two" data-bind="visible: $root.selectedFav() == userPrefID()"><i class="icon-save" data-bind=" click: $root.saveFav.bind($data)""></i></a>
</span>
</div>
</div>
</div>
// This is the code that does a save via ajax then highlights the element when done.
$.getJSON('#Url.Action("SaveFav","User")', { id: item.userPrefID(), fav: window.JSON.stringify(fav) }, function (result) {
var savedFav = ko.utils.arrayFirst(self.favs(), function (aFav) {
return aFav.userPrefID() == result.userPrefID; // <-- is this the desired fav?
});
// Fav found?
if (savedFav) {
// Update the fav!
savedFav.value(result.value);
}
}).done(function () {
var elementID = "#fav_" + item.userPrefID();
highlightElement(elementID);
});
// Function to highlight the element
function highlightElement(element) {
$(element).effect("highlight", {}, 1500);
}
I would do this the 'knockout' way... use a custom bindingHandler. You shouldn't be directly manipulating DOM in your viewModel, but only touching properties of your viewModel.
Taking this approach, you simply set a boolean value to true when your save is complete... this triggers the highlight effect (the jquery/dom manipulation neatly hidden away from your viewmodel) and when highlight effect completes, the handler sets the boolean back to false. Nice and tidy.
HTML:
<div id="#fav" data-bind="highlight: done">This is a test div</div>
<br />
<button data-bind="click: save">Simulate Save</button>
Javascript:
ko.bindingHandlers.highlight = {
update: function(element, valueAccessor) {
var obs = valueAccessor();
var val = ko.unwrap(obs);
if (val) {
$(element).effect("highlight", {}, 1500, function() {
obs(false);
});
}
}
};
var vm = function() {
var self = this;
self.done = ko.observable(false);
self.save = function() {
self.done(true);
};
}
ko.applyBindings(new vm());
Fiddle:
http://jsfiddle.net/brettwgreen/pd14q4f5/
I'm pretty new to jQuery and UI, and I'm having two issue with the autocomplete, and I suspect they're related. When I select an item in the list, the value, not the label, is displayed in the input. Second, the autocomplete will not close. When I step through my code, the select function gets called, and I see my label, not the value, displayed in the input, as I want. Unfortunately, the close function gets called (twice), and the ui.item.value replaces the label in the input and the autocomplete doesn't close.
My autocomplete code, along with the input HTML, is below. If it matters, the autocomplete is nested in a jQueryUI dialog.
HTML:
<input id="id_projectid" type="text" class="projEntryControl ui-autocomplete-input ui-corner-all" name="projectid" autocomplete="off"></input>
AutoComplete:
$('#id_projectid').autocomplete({
source: function(request, response) {
$.ajax({
url: "/chargeback/projList/" + $('#id_departmentid').val(),
dataType:"json",
data: {
project_startsWith: request.term
},
success: function(data) {
response( $.map( data.results, function( item ) {
return {
label: item.projectName,
value: item.id
}
}));
}
});
},
minLength: 3,
select: function(event, ui) {
$('#id_projectid').val(ui.item.label);
},
open: function() {
$( '#id_projectid' ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" );
},
close: function() {
$( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" );
}
});
EDIT: Adding more code as requested.
Here's the javascript that sets up the dialog, and connects the buttons to open the dialog
function setupProjectEntryDialog() {
$( "#addPEForm" ).dialog({
autoOpen: false,
height: 490,
width: 376,
modal: true,
buttons: {
"Save": function() {
var bValid = true;
allFields.removeClass( "ui-state-error" );
bValid = bValid && checkRegexp( hrsWorked, /^(?!\d{3})(?![2-9]\d)(?!1[1-9])(10|[1-9]{1})(.\d{0,2})?$/, "Please enter a valid number of hours worked." );
if ( bValid ) {
//save the changes to the database
var dataArray = $("#peUpdateForm").serializeArray();
var ed = new Object();
ed.name = "entryDateId";
ed.value = $("#entryDateId").text();
dataArray.push(ed);
if ($("#projEntryId").val() != "") {
var pe = new Object();
pe.name = "projEntryId";
pe.value = $("#projEntryId").val();
dataArray.push(pe);
}
$.post('/chargeback/savepe/', dataArray, function(data){
alert(data.msg);
//reload the project entries and total hours worked
showProjectEntries($('.entryDate.selected'));
getTotalHoursForEntryDate($('.entryDate.selected'));
}, "json");
//close the window
$( this ).dialog( "close" );
$('#id_projectid').autocomplete("destroy");
}
},
Cancel: function() {
$( this ).dialog( "close" );
$('#id_projectid').autocomplete("destroy");
}
},
close: function() {
allFields.val( "" ).removeClass( "ui-state-error" );
}
});
//connect the add new button
$("#addNewPEButton").click(function() {
$( "#addPEForm" ).dialog( "open" );
});
//hook up the add new project entry form submittal
$("#addNewPE").submit(function() {
addProjectEntry($(this));
return false;
});
//connect the delete Project Entry button
$('#deletePEButton').click(function() {
deleteProjectEntry($('.peRow.selected'));
return false;
});
//connect the row click method to the function
$('.peRow').click(function() {
peRowWasClicked($(this));
});
$('.peRow').dblclick(function() {
peRowWasDoubleClicked($(this));
})
}
Here's the javascript that loads the Dialog using a Django form and template to generate the HTML.
function addProjectEntry(anED) {
//ensure all peRows are not selected and disable the delete project entry button
$('.peRow').removeClass('selected');
$('#deletePEButton').attr('disabled', true);
//disable newFavoriteFromPEButton
$('#newFavoriteFromPEButton').attr('disabled', true);
//get the id of the selected entry date and strip the ed_ from it
$selectedED = $(".entryDate.selected");
var edId = $selectedED[0].id.split("_");
$("#addPEForm").load("/chargeback/cb_timeentry/newPE/" + edId[1], function() {
//connect the variables to the newly loaded html
connectDialogVariables();
//connect the project filtering to the department change
/*$("#id_departmentid").change(function() {
getProjectsForDepartment();
});*/
//connect the project and program number autocompletes to the controls
$('#id_projectid').autocomplete({
source: function(request, response) {
$.ajax({
url: "/chargeback/projList/" + $('#id_departmentid').val(),
dataType:"json",
data: {
project_startsWith: request.term
},
success: function(data) {
response( $.map( data.results, function( item ) {
return {
label: item.projectName,
value: item.id
}
}));
}
});
},
minLength: 3,
select: function(event, ui) {
$('#id_projectid').val(ui.item.label);
$(this).close();
return false;
},
open: function() {
$( '#id_projectid' ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" );
},
close: function() {
$( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" );
}
});
});
}
This is the HTML after the ajax call completes, and the above javascript completes. The autocomplete begins the search when typing in the correct text input, and is positioned correctly, but isn't part of the form.
<div class="ui-dialog ui-widget ui-widget-content ui-corner-all ui-draggable ui-resizable ui-dialog-buttons" style="outline: 0px none; z-index: 1002; position: absolute; height: auto; width: 376px; top: 66px; left: 297.5px; display: block;" tabindex="-1" role="dialog" aria-labelledby="ui-id-9">
<div class="ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix">
<span id="ui-id-9" class="ui-dialog-title">Project Entry Update</span>
<a href="#" class="ui-dialog-titlebar-close ui-corner-all" role="button">
<span class="ui-icon ui-icon-closethick">close</span>
</a>
</div>
<div id="addPEForm" class="ui-dialog-content ui-widget-content" style="width: auto; min-height: 0px; height: 367px;" scrolltop="0" scrollleft="0"><form action="" id="peUpdateForm" method="post">
<div style="display:none"><input type="hidden" value="BTrLZBVfA2ltExq3OUU5015BVxPKO9lL" name="csrfmiddlewaretoken"></div>
<p><label for="id_departmentid">Department:</label>
<select name="departmentid" class="projEntryControl" id="id_departmentid">
<option selected="selected" value="">Choose a Department</option>
<option value="1">ABND</option>
<option value="2">ATT</option>
<option value="3">AVI</option>
<option value="4">CCS</option>
<option value="5">PBW</option>
</select></p>
<p>
<label for="id_projectid">Project:</label>
<input type="text" name="projectid" class="projEntryControl ui-autocomplete-input" id="id_projectid" autocomplete="off">
<span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span>
</p>
<p>
<label for="id_progNumId">Program Number:</label>
<input type="text" name="progNumId" class="projEntryControl" id="id_progNumId">
</p>
<p>
<label for="id_hoursWorked">Hours Worked:</label>
<input type="text" id="id_hoursWorked" maxlength="5" class="projEntryControl" value="0.0" name="hoursWorked">
</p>
<p>
<label for="id_notes">Notes:</label>
<textarea name="notes" cols="40" rows="10" id="id_notes"></textarea
</p>
<p style="display:none;" id="entryDateId">1</p>
<p style="display:none;" id="projEntryId"></p>
</form>
</div>
<div class="ui-resizable-handle ui-resizable-n" style="z-index: 1000;"></div>
<div class="ui-resizable-handle ui-resizable-e" style="z-index: 1000;"></div>
<div class="ui-resizable-handle ui-resizable-s" style="z-index: 1000;"></div>
<div class="ui-resizable-handle ui-resizable-w" style="z-index: 1000;"></div>
<div class="ui-resizable-handle ui-resizable-se ui-icon ui-icon-gripsmall-diagonal-se ui-icon-grip-diagonal-se" style="z-index: 1000;"></div>
<div class="ui-resizable-handle ui-resizable-sw" style="z-index: 1000;">
</div><div class="ui-resizable-handle ui-resizable-ne" style="z-index: 1000;"></div>
<div class="ui-resizable-handle ui-resizable-nw" style="z-index: 1000;"></div>
<div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix">
<div class="ui-dialog-buttonset">
<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" role="button" aria-disabled="false">
<span class="ui-button-text">Save</span>
</button>
<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" role="button" aria-disabled="false">
<span class="ui-button-text">Cancel</span>
</button>
</div>
</div>
</div>
<div class="ui-widget-overlay" style="width: 979px; height: 567px; z-index: 1001;"></div>
<ul class="ui-autocomplete ui-menu ui-widget ui-widget-content ui-corner-all" id="ui-id-11" tabindex="0" style="z-index: 1003; display: none;">
</ul>
I am able to display Flexigrid in a normal view called from my main menu. I am using this sample http://mvc4beginner.com/Sample-Code/Insert-Update-Delete/Asp-.Net-MVC-Ajax-Insert-Update-Delete-Using-Flexigrid.html to make it work and it works fine for me.
However, my idea is to use a bit more complex interface - have a regular view with the search controls and on pressing search button show the grid with data for the items I searched.
I tried couple of things so far and can not make it to work. Here is the latest Index view I tried:
#model CardNumbers.Objects.Client
#{
ViewBag.Title = "Clients";
}
<h2>Clients</h2>
<br />
#using (Ajax.BeginForm("Search", "Client",
new AjaxOptions
{
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "ClientsResults"
}))
{
<fieldset>
<legend>Search</legend>
<label for="clientNo">Client No: </label>
<input type="number" name="searchClientNo" class="numericOnly" /><br />
<label for="clientName">Client Name: </label>
<input type = "text" size =25 data-autocomplete="#Url.Action("QuickSearch", "Client")" name ="searchClientName" />
<div>
<input type="submit" value="Find / Refresh" />
#*<input type="button" value="Find / Refresh" id="ClientsSearch" data-url="#Url.Action("Client", "Client")" />
#*<input type="submit" value="Find / Refresh" />*#
#* #Ajax.ActionLink("Find / Refresh", "Client", new AjaxOptions {UpdateTargetId = "ClientResults",
InsertionMode = InsertionMode.Replace, HttpMethod = "POST"}) *#
#*}*#
</div>
</fieldset>
<div style="padding-left:150px; padding-top:50px; padding-bottom:50px;" id="ClientsResults">
#*#{Html.RenderPartial("_Client", Model); }*#
#*<table id="flexClients" style="display:none"/>*#
</div>
}
#*<br />*#
You can see all the commented attempts here also. So, the Search method in the Clients controller now has this code:
public ActionResult Search(int? searchClientNo = null, string searchClientName = null)
{
// Assume we want to select everything
var clients = Db.Clients; // Should set type of clients to IQueryable<Clients>
if ((searchClientNo ?? 0) != 0) //Number was supplied
clients = clients.Where(c => (c.Number == searchClientNo));
// If clientNo was supplied, clients is now filtered by that. If not, it still has the full list. The following will further filter it.
if (!String.IsNullOrWhiteSpace(searchClientName)) // Part of the name was supplied
clients = clients.Where(c => (c.Name.Contains(searchClientName)));
return PartialView("_ClientsSearch", clients);
//return PartialView("_Client", clients);
}
The commented view is the partial view which has a flexigrid and it's not working. The _ClientsSearch view is the "normal" index view created by using the template and this works.
Do you see what exactly I am missing? The flexigrid method is simply not firing at all when I attempt to use it as a partial view from that main view.
I haven't figured out the more complex scenario I had originally in mind, but I was able to make it work using the regular view. The helpful idea first came from this FAQ:
http://code.google.com/p/flexigrid/wiki/FAQ and also looking a bit closer to that sample I used.
So, now my Client view is this:
#model CardNumbers.Objects.Client
#{
ViewBag.Title = "Client";
}
<form id="frmClientsSearch">
<label for="clientNo">Client No: </label>
<input type="number" name="searchClientNo" class="numericOnly" /><br />
<label for="clientName">Client Name: </label>
<input type = "text" size =25 value ="Please enter the search value"
name ="searchClientName" />
<input type="button" id="btnClientsSearch" value ="Find / Refresh" />
</form>
<div style="padding-left: 150px; padding-top: 50px; padding-bottom: 50px;" id="ClientsResults">
<table id="flexClients" style="display: none">
</table>
</div>
<div style="display: none">
<form id="sform">
<input type="hidden" id="fntype" name="fntype">
<input type="hidden" id="Id" name="Id">
<label for="Number">Client No: </label>
<input type="number" id="Number" name="Number" class="numericOnly" />
<label for="Name">Client Name: </label>
<input type="text" size="25" id="Name" name="Name" /><br />
<label for="Contact11">Contact 1: </label>
<input type="text" size="25" id="Contact1" name="Contact1" /><br />
<div class="float-right">
<button type="Submit" id="btnSave">Submit</button>
<button type=reset id="btnCancel">Cancel</button>
</div>
</form>
</div>
And the main work is done in the .js file (note the AddFormData method):
/// <reference path = "jquery-1.5.1-vsdoc.js"/>
/// <reference path = "jquery-ui-1.8.11.js"/>
$(document).ready(function() {
$(":input[data-autocomplete]").each(function() {
$(this).autocomplete({ source: $(this).attr("data-autocomplete") });
});
});
$(function () {
$('input[name="delete"]').click(function () {
return confirm('Are you sure?');
});
});
$(".numericOnly").keypress(function (e) {
if (String.fromCharCode(e.keyCode).match(/[^0-9]/g)) return false;
});
$("#flexClients").flexigrid({
url: '/Client/Client/',
dataType: 'json',
colModel: [
{ display: 'Client Id', name: 'Id', width: 100, sortable: true, align: 'center', hide: true},
{ display: 'Client #', name: 'Number', width: 100, sortable: true, align: 'center' },
{ display: 'Name', name: 'Name', width: 350, sortable: true, align: 'center' },
{ display: 'Contact 1', name: 'Contact1', width: 350, sortable: true, align: 'center' },
],
buttons: [
{ name: 'Add', bclass: 'add', onpress: test },
{ name: 'Edit', bclass: 'edit', onpress: test },
{ name: 'Delete', bclass: 'delete', onpress: test },
{ separator: true }
],
searchitems: [
{ display: 'Client Name', name: 'Name' }
],
sortname: "Name",
sortorder: "asc",
usepager: true,
title: 'Clients',
useRp: true,
rp: 15,
showTableToggleBtn: true,
width: 1000,
onSubmit: addFormData,
height: 300
});
//This function adds parameters to the post of flexigrid. You can add a verification as well by return to false if you don't want flexigrid to submit
function addFormData() {
//passing a form object to serializeArray will get the valid data from all the objects, but, if the you pass a non-form object, you have to specify the input elements that the data will come from
var dt = $('#sform').serializeArray();
dt = dt.concat($('#frmClientsSearch').serializeArray());
$("#flexClients").flexOptions({ params: dt });
return true;
}
$('#sform').submit(function () {
$('#flexClients').flexOptions({ newp: 1 }).flexReload();
alert("Hello World");
return false;
});
function test(com, grid) {
if (com === 'Delete') {
var clientName = $('.trSelected td:eq(2)').text();
if (clientName) //Variable is defined and not empty
{
if (confirm("Are you sure you want to delete " + $.trim(clientName) + "?"))
return false;
$('#fntype').val('Delete');
$('#Id').val($('.trSelected td:eq(0)').text());
$('#Number').val('');
$('#Name').val('');
$('#Contact1').val('');
$('.trSelected', grid).each(function () {
var id = $(this).attr('id');
id = id.substring(id.lastIndexOf('row') + 3);
addFormData(); $('#flexClients').flexOptions({ url: '/Client/Client/' }).flexReload();
});
clearForm();
}
} else if (com === 'Add') {
$("#sform").dialog({
autoOpen: false,
show: "blind",
width: 1000,
height: 500
});
$("#sform").dialog("open");
$('#fntype').val('Add');
$('#Number').val('');
$('#Name').val('');
$('#Contact1').val('');
} else if (com === 'Edit') {
$('.trSelected', grid).each(function () {
$("#sform").dialog({
autoOpen: false,
show: "blind",
width: 1000,
height: 500
});
$("#sform").dialog("open");
$('#fntype').val('Edit');
$('#Id').val($('.trSelected td:eq(0)').text());
$('#Number').val($('.trSelected td:eq(1)').text());
$('#Name').val($('.trSelected td:eq(2)').text());
$('#Contact1').val($('.trSelected td:eq(3)').text());
});
}
}
function clearForm() {
$("#sform input").val("");
};
$(function () {
$('#btnSave').click(function () {
addFormData();
$('#flexClients').flexOptions({ url: '/Client/Client/' }).flexReload();
clearForm();
$('#sform').dialog('close');
return false;
});
});
$(function () {
$('#btnCancel').click(function () {
// clearForm();
$('#sform').dialog('close');
return false;
});
});
$(function () {
$('#btnClientsSearch').click(function () {
addFormData();
$('#flexClients').flexOptions({ url: '/Client/Client/' }).flexReload();
//$.ajax({
// url: $(this).data('url'),
// type: 'GET',
// cache: false,
// success: function (result) {
// $('#ClientsResults').html(result);
// }
//});
return;//false;
});
});
And my Client method in the controller is the same as it used to be with minor change.
Now, my next challenge is to generalize the above and also somehow instead of calling the form sForm I showed use a more complex form with validations as I if it is from the Create/Edit view.