Related
I am working on Kendo UI jQuery grid CRUD. I can get data display in the grid, but not adding new records.
When I click the update button to add a record after filling up columns in the pop-up window, nothing is posted to the server side as every property has a null value.
The picture shows what I got when the button is pressed.
Controller:
[HttpPost]
public JsonResult AddLostProperty(LostPropertyViewModel lostProperty)
{
try
{
using (var dbContext = new DBEntities())
{
if (lostProperty != null)
{
var newLostProperty = new sz_LostProperty()
{
Name = lostProperty.PropertyName,
CategoryId = dbContext.sz_PropertyCategory.Where(x => x.Name == lostProperty.CategoryName).Select(c => c.Id).FirstOrDefault(),
Description = lostProperty.PropertyDescription,
FoundDate = lostProperty.FoundDate,
FoundLocation = lostProperty.FoundLocation,
CratedDate = DateTime.UtcNow.Date,
CratedBy = ""
};
dbContext.sz_LostProperty.Add(newLostProperty);
dbContext.SaveChanges();
return Json(new { Success = true, Message = "The Property has been added." });
}
else
{
return Json(new { Success = false, Message = "No lost property added." });
}
}
}
catch (Exception e)
{
return Json(new { Success = false, Message = "Error: " + e });
}
}
JavaScript:
<script>
$(document).ready(function () {
var serviceBaseUrl = "#Request.Url.ToString()",
lostPropertyDataSource = new kendo.data.DataSource({
transport: {
create: {
url: serviceBaseUrl + "/AddLostProperty",
type: "POST",
dataType: "json",
complete: function (e) {
$('#manageLostPropertiesGrid').data('kendoGrid').dataSource.read();
}
},
read: {
url: serviceBaseUrl + "/GetLostProperties",
type: "GET",
dataType: "json"
},
update: {
url: serviceBaseUrl + "/UpdateLostProperty",
type: "PUT",
dataType: "json"
},
destroy: {
url: serviceBaseUrl + "/DeleteLostProperty",
type: "DELETE",
dataType: "json"
},
parameterMap: function (options, operation) {
if (operation !== "read" && options.models) {
return { models: kendo.stringify(options.models) };
}
}
},
batch: true,
pageSize: 20,
schema: {
model: {
id: "PropertyId",
fields: {
PropertyId: { editable: false, nullable: true, type: "number" },
PropertyName: { type: "string", editable: true, validation: { required: true } },
CategoryId: { type: "number", editable: true, validation: { required: true } },
PropertyDescription: { validation: { required: false } },
Image: { validation: { required: false } },
FoundDate: { type: "Date" },
FoundLocation: { editable: true, validation: { required: false } }
}
}
}
});
$("#manageLostPropertiesGrid").kendoGrid({
dataSource: lostPropertyDataSource,
pageable: true,
height: 550,
toolbar: ["create"],
columns: [
{ field: "PropertyName", title: "Property Name", width: "150px" },
{ field: "CategoryName", title: "Category", editor: propertyCategoryList,/* template: "#=CategoryName#", */width: "150px"},
{ field: "PropertyDescription", title: "Description", width: "200px" },
{ field: "FoundDate", title: "Found Date", template: "#= kendo.toString(kendo.parseDate(FoundDate, 'dd-MM-yyyy'), 'dd-MM-yyyy') #", width: "130px" },
{ field: "FoundLocation", title: "Found Location", width: "120px" },
{ command: ["edit", "destroy"], title: " ", width: "250px" }],
editable: "popup"
}).data("kendoGrid");
});
From the browser, I can see the object sent to the server below:
What am I doing wrong?
I believe that in this case is problem in your parameter type at server side.
You have enabled batch: true editing which is useful if you want make many changes in your grid but send only one request with changed models in the end. It is very useful for example in case of inCell edit mode, when you would see many many requests and so decrease them is something you want, but in case of popup edit, I personally do not see any reason to use batch editing, but yes, I know Telerik has this in their demo.
So, because batch editing is enabled, there is called parameterMap before request is executed. (note: parameterMap is called only if you have batch edit enabled, otherwise it's ignored). That parameterMap wraps all your models into json string array and send that array with request to the server. In your case, there is always one record edited, but it doesn't matter - it will be sent as an array (in json string format).
Because it is sent as serialized string, you can
1) Change parameter of your AddLostProperty method to string models and then deserialize into array which allows you to work with it as you are used to
public ActionResult AddLostProperty(string models)
{
...
var data = JsonConvert.DeserializeObject<IEnumerable<LostPropertyViewModel>>(models);
...
}
2) If we will follow Telerik demo, you can use such implementation
public ActionResult AddLostProperty()
{
var products = this.DeserializeObject<IEnumerable<LostPropertyViewModel>>("models");
if (products != null)
{
//logic
}
return this.Jsonp(products);
}
3) And this is solution I would prefer
Just remove batch: true and parameterMap (since without batch it's useless) - and it should start send single object to your server method.
I am using Highsoft.Highstock .NET on my ASP.net MVC 5 application.
I create a list of single line charts directly inside the controller and send all the data to the view.
When I change the navigation (start/enddate slider at the bottom of the chart) for one chart, I would like to update all other charts to the same timeframe (all charts have the same timespan).
I would like to do this with jquery, but I am not able right now.
I think I have to use the
(I was also reading the API from Highstock but there is something different, e.g.: my charts do not have a property "chart.title.text" -> my one has "chart.title.textStr")
JS solution:
To update all charts when you change the navigator position on one of them you can use xAxis.events.afterSetExtremes event to set new extremes on all charts (you can find all charts reference in Highcharts.charts array).
Code:
var json = "[[1534512600000,217.58],[1534771800000,215.46],[1534858200000,215.04],[1534944600000,215.05],[1535031000000,215.49],[1535117400000,216.16],[1535376600000,217.94],[1535463000000,219.7],[1535549400000,222.98],[1535635800000,225.03],[1535722200000,227.63],[1536067800000,228.36],[1536154200000,226.87],[1536240600000,223.1],[1536327000000,221.3],[1536586200000,218.33],[1536672600000,223.85],[1536759000000,221.07],[1536845400000,226.41],[1536931800000,223.84],[1537191000000,217.88],[1537277400000,218.24],[1537363800000,218.37],[1537450200000,220.03],[1537536600000,217.66],[1537795800000,220.79],[1537882200000,222.19],[1537968600000,220.42],[1538055000000,224.95],[1538141400000,225.74],[1538400600000,227.26],[1538487000000,229.28],[1538573400000,232.07],[1538659800000,227.99],[1538746200000,224.29],[1539005400000,223.77],[1539091800000,226.87],[1539178200000,216.36],[1539264600000,214.45],[1539351000000,222.11],[1539610200000,217.36],[1539696600000,222.15],[1539783000000,221.19],[1539869400000,216.02],[1539955800000,219.31],[1540215000000,220.65],[1540301400000,222.73],[1540387800000,215.09],[1540474200000,219.8],[1540560600000,216.3],[1540819800000,212.24],[1540906200000,213.3],[1540992600000,218.86],[1541079000000,222.22],[1541165400000,207.48],[1541428200000,201.59],[1541514600000,203.77],[1541601000000,209.95],[1541687400000,208.49],[1541773800000,204.47],[1542033000000,194.17],[1542119400000,192.23],[1542205800000,186.8],[1542292200000,191.41],[1542378600000,193.53],[1542637800000,185.86],[1542724200000,176.98],[1542810600000,176.78],[1542983400000,172.29],[1543242600000,174.62],[1543329000000,174.24],[1543415400000,180.94],[1543501800000,179.55],[1543588200000,178.58],[1543847400000,184.82],[1543933800000,176.69],[1544106600000,174.72],[1544193000000,168.49],[1544452200000,169.6],[1544538600000,168.63],[1544625000000,169.1],[1544711400000,170.95],[1544797800000,165.48],[1545057000000,163.94],[1545143400000,166.07],[1545229800000,160.89],[1545316200000,156.83],[1545402600000,150.73],[1545661800000,146.83],[1545834600000,157.17],[1545921000000,156.15],[1546007400000,156.23],[1546266600000,157.74],[1546439400000,157.92],[1546525800000,142.19],[1546612200000,148.26],[1546871400000,147.93],[1546957800000,150.75],[1547044200000,153.31],[1547130600000,153.8]]";
var data = JSON.parse(json);
function updateExtremes() {
var currentChart = this.chart,
chartIndex = currentChart.index,
charts = Highcharts.charts,
min = currentChart.xAxis[0].min,
max = currentChart.xAxis[0].max;
charts.forEach(function(chart) {
if (chart.index !== chartIndex) {
chart.xAxis[0].setExtremes(min, max, true, false);
}
});
}
Highcharts.stockChart('container1', {
chart: {
height: 250
},
rangeSelector: {
selected: 0
},
title: {
text: 'AAPL Stock Price'
},
xAxis: {
events: {
afterSetExtremes: function() {
updateExtremes.call(this);
}
}
},
series: [{
name: 'AAPL',
data: data,
tooltip: {
valueDecimals: 2
}
}]
});
Highcharts.stockChart('container2', {
chart: {
height: 250
},
rangeSelector: {
selected: 0
},
title: {
text: 'AAPL Stock Price'
},
xAxis: {
events: {
afterSetExtremes: function() {
updateExtremes.call(this);
}
}
},
series: [{
name: 'AAPL',
data: data,
tooltip: {
valueDecimals: 2
}
}]
});
Highcharts.stockChart('container3', {
chart: {
height: 250
},
rangeSelector: {
selected: 0
},
title: {
text: 'AAPL Stock Price'
},
xAxis: {
events: {
afterSetExtremes: function() {
updateExtremes.call(this);
}
}
},
series: [{
name: 'AAPL',
data: data,
tooltip: {
valueDecimals: 2
}
}]
});
Demo:
https://jsfiddle.net/BlackLabel/74rgyq13/
JS API reference:
https://api.highcharts.com/highcharts/xAxis.events.afterSetExtremes
https://api.highcharts.com/class-reference/Highcharts.Axis#setExtremes
C# API reference:
http://dotnet.highcharts.com/Help/Highcharts/html/class_highsoft_1_1_web_1_1_mvc_1_1_charts_1_1_x_axis_events.html#aa8efb1a8237bc71a0268e5ee7e522096
http://dotnet.highcharts.com/Help/Highcharts/html/class_highsoft_1_1_web_1_1_mvc_1_1_charts_1_1_x_axis_events.html#a120bc76164638d4a76a7ee20280d54ff
I'm developing a web application with Telerik Kendo in Razor. Here is my problem:
I have a variable that I set as a type List<class>.
#{
ViewBag.Title = "Home Page";
var dpdminst = new DB();
var data = dpdminst.getdata();}
I want to be able to use this variable (data) to set my DataSource in my Javascript:
<script>
var displaydata = #data
$(document).ready(function () {
$("#grid").kendoGrid({
height: 550,
groupable: true,
sortable: true,
pageable: {
refresh: true,
pageSizes: true,
buttonCount: 5
},
dataSource: {
data:displaydata,
schema: {
model: {
fields: {
amount: { type: "string" },
}
}
},
columns:["amount"]
}
});
});
</script>
Does anyone know if this can be done?
Here is my JsonResult:
public JsonResult GetJsonData()
{
var DBinst = new DB();
var TradeData = DBinst.tradedata();
var json = JsonConvert.SerializeObject(TradeData);
var result = new JsonResult()
{
Data = json
};
return result;
}
Have an action method which returns the data you want in JSON format. in your document.ready event, make an ajax call to get this data and then you can set it as your data source.
public ActionResult GetJsonData()
{
var dpdminst = new DB();
var data = dpdminst.getdata();
return Json(data,JsonRequestBehaviour.AllowGet);
}
and in your view use the getJSON method to get data from this action method and use that as needed. You may format the incoming json as per your UI requirements
$(document).ready(function () {
$.getJSON("#Url.Action("GetJsonData","YourControllerName")",function(data){
// you have your json data in the "data" variable.
// now you may use it to set the data source of your grid library
});
});
If you dont want to deal with ajax/json, then I would try to achieve what you want as follows:
<script>
var displaydata = [
#foreach (var record in dpdminst.getdata())
{
#: { amount: '#record' },
}
];
$(document).ready(function () {
$("#grid").kendoGrid({
height: 550,
groupable: true,
sortable: true,
pageable: {
refresh: true,
pageSizes: true,
buttonCount: 5
},
dataSource: {
data:displaydata,
schema: {
model: {
fields: {
amount: { type: "string" },
}
}
},
},
columns:["amount"]
});
});
</script>
Also please notice that you had columns:["amount"] in a wrong place, also this code has to be in your cshtml for razor syntax to work properly.
I posted a question yesterday. This is the link but I didn't get any response. I have made few changes in the code and I am re-submitting the code with the error I get. I am using ExtJS MVC approach to upload a file to the server. On the server end I have ASP.Net Web API's for handling the server side functionality. I have an ExtJS form inside a window that I am using to upload file. My controller on the server side recieves the file properly and also returns the following JSON:
{"$id":"1","success":true,"result":"Uploaded Successfully.","error":null}
The issue is that the upload does not finish. It keeps on showing "Please wait..." message. Looking into the Chrome developer tool I get the error: "Uncaught TypeError: undefined is not a function"
Here is the complete code of the view:
Ext.define('MyApp.view.AddCountry', {
extend: 'Ext.window.Window',
alias: 'widget.addcountry',
closable: true,
closeAction: 'destroy',
modal: true,
height: '28%',
width: '45%',
layout: 'fit',
initComponent: function () {
var me = this;
me.items = [{
xtype: 'form',
bodyPadding: '5 5 0',
itemId: 'addCountryForm',
frame: false,
headers: { 'Content-Type': 'text/html' },//Removing this also doesn't work.
items: [{
xtype: 'textfield',
fieldLabel: 'Name',
maxLength: 25,
enforceMaxLength: true,
allowBlank: false,
flex: 1
}, {
xtype: 'textfield',
fieldLabel: 'Description',
maxLength: 100,
enforceMaxLength: true,
allowBlank: false,
flex: 1
}, {
xtype: 'filefield',
name: 'flagImage',
fieldLabel: 'Flag',
buttonText: 'Select Image...',
flex: 1
}],
buttons: [{
text: 'Save',
itemId: 'btnAddCountry'
}, {
text: 'Cancel',
itemId: 'btnCancelAddingCountry'
}]
}];
this.callParent();
}
});
Controller code:
AddNewCountry: function (button) {
var form = button.up('form').getForm();
form.submit({
url: 'localhost/myapp/api/addcountry',
waitMsg: 'Please wait...',
success: function (f, o) {
Ext.Msg.alert('Success', o.result.result);
},
failure: function (f, o) {
Ext.Msg.alert('Error', o.result.error);
}
});
},
This is what I have written on the server side:
public UploadResult AddNewCountry()
{
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count > 0)
{
string fileName = httpRequest.Files[0].FileName;//I am able to get this
}
UploadResult objResult = new UploadResult();
objResult.success = true;
objResult.result = "Uploaded Successfully.";
return objResult;
}
public class UploadResult
{
public bool success { get; set; }
public string result { get; set; }
public string error { get; set; }
}
I am sure there has to be something in the Form's config but I am unable to find. Can someone point out the issue here?
I have 4 grid tables. They use the same modal. I cannot use the same store as each grid has to send a parameter value to get the data from db. So, what's happening is I ended up making 4 different stores and then when I load my application, it lags because it waits to load all the 4 stores. this is my grid.. so I have 3 more grids like this
this.grid1 =Ext.create('Ext.grid.Panel',{
title:'GridView App', store: store, loadMask:true,
columns:[
{ header:'Q1', sortable:true, dataIndex:'Q1', flex:1,},
{ header:'Q2', sortable:true, dataIndex:'Q2', flex:1,},
{ header:'Q3', sortable:true, dataIndex:'Q3', flex:1,},
{ header:'Q4', sortable:true, dataIndex:'Q4', flex:1,}
and this is my store1... and similarly I have 3 more stores like this each with parameter Q2, Q3, Q4 respectively
var store1 =Ext.create('Ext.data.JsonStore',{
storeId:'myData', scope:this,
fields:[
{ name:'Q1', type:'int'},
{ name:'Q2', type:'int'},
{ name:'Q3', type:'int'},
{ name:'Q4', type:'int'}
],
sorters:[{ property:'Q1', direct:'ASC'}],
proxy:{
type:'ajax',
url:'GridView/writeRecord',
extraParams: { ID: Q1 },
reader: newExt.data.JsonReader({
root:'myTable',
totalProperty:'count'
})
}
});
Is there a faster/better way to implement it than the way that I have?
UPDATE -
SECOND UPDATE -
Here is my layout for the whole application... my rightcontainer is disabled at first and contains the actual grids and forms, and click on the item on tab enables the rightcontainer and loads all the grid.
Ext.define('ExtjsApp.app1.appPanel', {
extend: 'Ext.panel.Panel',
alias: 'widget.mypanel',
layout: {
type: 'vbox',
align: 'stretch'
},
scope: this,
titleAlign: 'center',
minWidth: 900,
bodyPadding: 10,
requires: [],
items: [],
constructor: function () {
this.callParent(arguments);
this.regForm = Ext.create('ExtjsApp.app1.RegForm', {});
leftTreeStore = Ext.create('Ext.data.TreeStore', {
scope: this,
storeId: 'leftTreeStore',
fields: [
{ name: 'text', type: 'string' },
{ name: 'dataId', type: 'string' },
{ name: 'listName', type: 'string' },
{ name: 'leaf', type: 'bool' }
],
root: { expanded: true },
proxy: {
type: 'ajax',
url: 'app1/getRecords',
extraParams: { organization: 'GOOGLE' },
reader: { type: 'json' }
},
autoLoad: true,
expanded: true,
autoSync: true,
listeners: {
}
});
allRecordsStore = Ext.create('Ext.data.TreeStore', {
id: 'allRecordsStore',
autoLoad: false,
autoSync: false,
scope: this,
fields: [
{ name: 'text', type: 'string' },
{ name: 'dataId', type: 'string' },
{ name: 'listName', type: 'string' },
{ name: 'leaf', type: 'bool' }
],
root: { expanded: true },
proxy: {
type: 'ajax',
url: 'app1/getRecords',
extraParams: { organization: 'GOOGLE' },
reader: { type: 'json' }
}
});
this.currentMonthsTree = Ext.create('Ext.tree.TreePanel', {
scope: this,
title: 'Current 12 Months',
titleAlign: 'center',
tabIndex: 0,
height: 500,
flex: 1,
rootVisible: false,
store: leftTreeStore,
id: 'currentMonthsTree',
useArrows: true,
hideHeaders: true,
columns: [
{
xtype: 'treecolumn',
id: 'ID',
dataIndex: 'text',
flex: 1
}
],
viewConfig: {
plugins: {
ptype: 'treeviewdragdrop',
enableDrop: false,
appendOnly: false,
enableDrag: false
},
listeners: {
itemclick: function (view, rec, item) {
if (rec.isLeaf()) {
alert('isLeaf');
}
else if (!rec.isLeaf()) {
alert('isNotLeaf');
}
}
},
allowCopy: true,
copy: true
}
});
this.currentMonthsTree.on('selectionchange', function (selected) {
FnDisplayRecord(selected.selected.items[0]);
});
this.allRecordsTree = Ext.create('Ext.tree.TreePanel', {
scope: this,
title: 'All Records',
titleAlign: 'center',
flex: 1,
tabIndex: 1,
rootVisible: false,
store: allRecordsStore,
id: 'allRecordsTree',
useArrows: true,
hideHeaders: true,
columns: [
{
xtype: 'treecolumn',
id: 'ID',
dataIndex: 'text',
flex: 1
}
],
viewConfig: {
plugins: {
ptype: 'treeviewdragdrop',
enableDrop: false,
enableDrag: false,
appendOnly: false
},
listeners: {
itemclick: function (view, rec, item) {
if (rec.isLeaf()) {
alert('isLeaf');
}
else if (!rec.isLeaf()) {
alert('isNotLeaf');
}
}
},
allowCopy: true,
copy: true
}
});
this.allRecordsTree.on('selectionchange', function (selected) {
FnDisplayRecord(selected.selected.items[0]);
//alert('Hello');
});
function FnClearValues() {
//Clear All Values
alert('ClearALLValues');
}
function FnSetValues(myObj) {
//I set all my form values using Ext.getCmp
Ext.getCmp('Textl').setValue(myObj.Text1);
}
function FnDisplayRecord(row) {
if (row.get('leaf') == true) {
console.log(row.data.dataId);
var tempID = row.data.dataId;
Ext.getCmp('rightContainer').setLoading(true, true);
Ext.getCmp('requisitionPOGridPanel').store.loadData([], false);
Ext.Ajax.request({
method: 'GET',
url: 'app1/getRecord',
headers: { 'Content-Type': 'application/json' },
dataType: 'json',
params: {
ID: tempID
},
success: function (response) {
Ext.getCmp('rightContainer').setLoading(false, false);
myObj = Ext.JSON.decode(response.responseText);
if (AsbestosObj.DateIssued != '') {
FnSetValues(AsbestosObj);
Ext.getCmp('GridPanel').store.load({ params: { ID: tempID} });
Ext.getCmp('Grid1').store.load({ params: { ID: tempID, qID: 'Q01'} });
Ext.getCmp('Grid2').store.load({ params: { ID: tempID, qID: 'Q02'} });
Ext.getCmp('Grid3').store.load({ params: { ID: tempID, qID: 'Q03'} });
Ext.getCmp('Grid4').store.load({ params: { ID: tempID, qID: 'Q04'} });
}
else { FnClearValues(); }
},
failure: function () {
Ext.Msg.alert('Message', 'Error');
}
});
}
else if (row.get('leaf') == false) {
FnClearValues();
}
}
this.rightContainer = Ext.create('Ext.form.Panel', {
scope: this,
id: 'rightContainer',
layout: {
type: 'vbox',
align: 'stretch',
pack: 'start'
},
autoScroll: true,
disabled: true,
border: 1,
flex: 1,
items: [
this.regForm
]
});
this.tabContainer = Ext.create('Ext.tab.Panel', {
scope: this,
activeTab: 0,
flex: 0.5,
id: 'tabContainer',
layout: { type: 'vbox', align: 'stretch' },
plain: true,
listeners: {
tabchange: function (panel, newTab, oldTab) {
Ext.getCmp('rightContainer').disable();
FnClearValues();
var getTabStat = this.getActiveTab();
if (getTabStat.tabIndex == 0) {
Ext.getCmp('currentMonthsTree').store.load();
}
else if (getTabStat.tabIndex == 1) {
Ext.getCmp('allRecordsTree').store.load();
}
}
},
items: [
this.currentMonthsTree, this.allRecordsTree
]
});
this.mainContainer = Ext.create('Ext.container.Container', {
scope: this,
bodyPadding: 10,
title: 'MAIN',
layout: {
type: 'hbox',
align: 'stretch'
},
flex: 1,
items: [
this.tabContainer,
{ xtype: 'splitter', width: 5, animate: true },
this.rightContainer
]
});
this.add(this.mainContainer);
},
loadingOn: function () {
setTimeout(function () { Ext.getCmp('currentMonthsTree').setLoading(true, true); }, 100);
},
loadingOff: function () {
setTimeout(function () { Ext.getCmp('currentMonthsTree').setLoading(false, false); }, 100);
}
});
Please reference my SO question that is very similar: Combo box loads too slow
Basically, you will want to define all your models as you do normally.
Then you will want to define all the stores for your page as array stores without proxies, like this:
var myStore1 = Ext.create("Ext.data.ArrayStore", {
model: "MyModel1",
data: []
});
var myStore2 = Ext.create("Ext.data.ArrayStore", {
model: "MyModel1",
data: []
});
Then you will want to create a single call to wherever you are getting your data from, you will need to change the server to output all the arrays into an single JSON object, something like this, and for super optimization, make them array arrays, this would be the output I would expect from the server:
{
grid_data_1: [...],
grid_data_2: [...]
}
Then on your webpage after you create all the stores, make a single ajax call to get the data for all four grids:
Ext.Ajax.request({
url: 'url',
method: 'GET',
params: {
...whatever you want
},
success: function (response, opts) {
var result = Ext.decode(response.responseText);
myStore1.loadData(result.grid_data_1);
myStore2.loadData(result.grid_data_2);
...
},
});
This will make it much more efficient, you probably don't need to use array arrays in your case because there is only 5 rows per grid, but optimizing 4 ajax calls into one should have a large impact.
You cannot do much more here. I think you can do some micro tunes but I doubt they are worth time they took to identify. If your app do the following you've done it all the right way;
Time till your is loaded
Init only the required controller and the stores (check each request)
Show the main view
As soon as your grids get rendered they will fire the load operation, at least when they have a paging toolbar. You could begin the load operation earlier, meaning before you create the view that may give you some milliseconds but I doubt that you can save more time.
This is of course based on the available information's.