field errors start

This commit is contained in:
Kevin G Christiano 2020-12-22 16:35:21 -05:00
parent e6e8b0c605
commit db424ce04a
3 changed files with 79 additions and 64 deletions

View File

@ -19,7 +19,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
var observer: NSKeyValueObservation? var observer: NSKeyValueObservation?
public var templateModel: ListPageTemplateModel? public var templateModel: ListPageTemplateModel?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Computed Properties // MARK: - Computed Properties
//-------------------------------------------------- //--------------------------------------------------
@ -41,7 +41,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Methods // MARK: - Methods
//-------------------------------------------------- //--------------------------------------------------
open override func parsePageJSON() throws { open override func parsePageJSON() throws {
try parseTemplate(json: loadObject?.pageJSON) try parseTemplate(json: loadObject?.pageJSON)
try super.parsePageJSON() try super.parsePageJSON()
@ -54,8 +54,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
open override func viewForTop() -> UIView { open override func viewForTop() -> UIView {
guard let headerModel = templateModel?.header, guard let headerModel = templateModel?.header,
let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar) let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar)
else { return super.viewForTop() } else { return super.viewForTop() }
// Temporary, Default the horizontal padding // Temporary, Default the horizontal padding
if var container = templateModel?.header as? ContainerModelProtocol, container.useHorizontalMargins == nil { if var container = templateModel?.header as? ContainerModelProtocol, container.useHorizontalMargins == nil {
@ -67,8 +67,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
override open func viewForBottom() -> UIView { override open func viewForBottom() -> UIView {
guard let footerModel = templateModel?.footer, guard let footerModel = templateModel?.footer,
let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar) let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar)
else { return super.viewForBottom() } else { return super.viewForBottom() }
return molecule return molecule
} }
@ -86,7 +86,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
//Handle scroll //Handle scroll
handleScrollToSpecificRow() handleScrollToSpecificRow()
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Handle scroll to spefic row // MARK: - Handle scroll to spefic row
@ -117,12 +117,12 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
guard let moleculeInfo = getMoleculeInfo(for: indexPath), guard let moleculeInfo = getMoleculeInfo(for: indexPath),
let estimatedHeight = (moleculeInfo.class as? MoleculeViewProtocol.Type)?.estimatedHeight(with: moleculeInfo.molecule, delegateObject() as? MVMCoreUIDelegateObject) let estimatedHeight = (moleculeInfo.class as? MoleculeViewProtocol.Type)?.estimatedHeight(with: moleculeInfo.molecule, delegateObject() as? MVMCoreUIDelegateObject)
else { return 0 } else { return 0 }
return estimatedHeight return estimatedHeight
} }
open override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { open override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return moleculesInfo?.count ?? 0 return moleculesInfo?.count ?? 0
} }
@ -130,8 +130,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
open override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { open override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let moleculeInfo = getMoleculeInfo(for: indexPath), guard let moleculeInfo = getMoleculeInfo(for: indexPath),
let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.identifier) let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.identifier)
else { return UITableViewCell() } else { return UITableViewCell() }
(cell as? MoleculeViewProtocol)?.reset() (cell as? MoleculeViewProtocol)?.reset()
(cell as? MoleculeListCellProtocol)?.setLines(with: templateModel?.line, delegateObject: delegateObjectIVar, additionalData: nil, indexPath: indexPath) (cell as? MoleculeListCellProtocol)?.setLines(with: templateModel?.line, delegateObject: delegateObjectIVar, additionalData: nil, indexPath: indexPath)
@ -156,6 +156,24 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
// MARK: - MoleculeDelegateProtocol // MARK: - MoleculeDelegateProtocol
//-------------------------------------------------- //--------------------------------------------------
public override func handleFieldErrors(_ fieldErrors: [Any]?, loadObject: MVMCoreLoadObject) {
super.handleFieldErrors(fieldErrors, loadObject: loadObject)
for case let fieldError as [AnyHashable: Any] in fieldErrors ?? [] {
guard let fieldKey = fieldError["fieldKey"] as? String,
let userError = fieldError["userMessage"] as? String,
let entryFieldModel = formValidator?.fields[fieldKey] as? EntryFieldModel
else { continue }
entryFieldModel.errorMessage = userError
DispatchQueue.main.async {
let c = self.tableView.cellForRow(at: IndexPath(row: 0, section: 0))?.layoutSubviews()
}
}
}
open override func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { open override func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) {
if let tableView = tableView { if let tableView = tableView {
@ -222,8 +240,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
func createMoleculeInfo(with listItem: MoleculeModelProtocol?) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? { func createMoleculeInfo(with listItem: MoleculeModelProtocol?) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? {
guard let listItem = listItem, guard let listItem = listItem,
let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(listItem) let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(listItem)
else { return nil } else { return nil }
let moleculeName = moleculeClass.nameForReuse(with: listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName let moleculeName = moleculeClass.nameForReuse(with: listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName

View File

@ -18,12 +18,8 @@ import UIKit
@objc public var loadObject: MVMCoreLoadObject? @objc public var loadObject: MVMCoreLoadObject?
public var model: MVMControllerModelProtocol? public var model: MVMControllerModelProtocol?
public var pageModel: PageModelProtocol? { public var pageModel: PageModelProtocol? {
get { get { model }
return model set { model = newValue as? MVMControllerModelProtocol }
}
set {
model = newValue as? MVMControllerModelProtocol
}
} }
/// Set if this page is containted in a manager. /// Set if this page is containted in a manager.
@ -31,12 +27,10 @@ import UIKit
/// A temporary iVar backer for delegateObject() until we change the protocol /// A temporary iVar backer for delegateObject() until we change the protocol
public lazy var delegateObjectIVar: MVMCoreUIDelegateObject = { public lazy var delegateObjectIVar: MVMCoreUIDelegateObject = {
return MVMCoreUIDelegateObject.create(withDelegateForAll: self) MVMCoreUIDelegateObject.create(withDelegateForAll: self)
}() }()
public func delegateObject() -> DelegateObject? { public func delegateObject() -> DelegateObject? { delegateObjectIVar }
return delegateObjectIVar
}
public var formValidator: FormValidator? public var formValidator: FormValidator?
@ -52,7 +46,7 @@ import UIKit
/// Checks if the screen width has changed /// Checks if the screen width has changed
open func screenSizeChanged() -> Bool { open func screenSizeChanged() -> Bool {
return !MVMCoreGetterUtility.cgfequalwiththreshold(previousScreenSize.width, view.bounds.size.width, 0.1) !MVMCoreGetterUtility.cgfequalwiththreshold(previousScreenSize.width, view.bounds.size.width, 0.1)
} }
//-------------------------------------------------- //--------------------------------------------------
@ -61,8 +55,8 @@ import UIKit
open func observeForResponseJSONUpdates() { open func observeForResponseJSONUpdates() {
guard !observingForResponses, guard !observingForResponses,
(pagesToListenFor()?.count ?? 0 > 0 || modulesToListenFor()?.count ?? 0 > 0) (pagesToListenFor()?.count ?? 0 > 0 || modulesToListenFor()?.count ?? 0 > 0)
else { return } else { return }
observingForResponses = true observingForResponses = true
NotificationCenter.default.addObserver(self, selector: #selector(responseJSONUpdated(notification:)), name: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(responseJSONUpdated(notification:)), name: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil)
@ -80,28 +74,28 @@ import UIKit
} }
open func modulesToListenFor() -> [String]? { open func modulesToListenFor() -> [String]? {
return loadObject?.requestParameters?.modules as? [String] loadObject?.requestParameters?.modules as? [String]
} }
@objc open func responseJSONUpdated(notification: Notification) { @objc open func responseJSONUpdated(notification: Notification) {
// Checks for a page we are listening for. // Checks for a page we are listening for.
var newData = false var newData = false
if let pagesLoaded = notification.userInfo?.optionalDictionaryForKey(KeyPageMap), if let pagesLoaded = notification.userInfo?.optionalDictionaryForKey(KeyPageMap),
let pageType = pagesToListenFor()?.first(where: { (pageTypeListened) -> Bool in let pageType = pagesToListenFor()?.first(where: { (pageTypeListened) -> Bool in
guard let page = pagesLoaded.optionalDictionaryForKey(pageTypeListened), guard let page = pagesLoaded.optionalDictionaryForKey(pageTypeListened),
let pageType = page.optionalStringForKey(KeyPageType), let pageType = page.optionalStringForKey(KeyPageType),
pageType == pageTypeListened pageType == pageTypeListened
else { return false } else { return false }
return true return true
}) { }) {
newData = true newData = true
loadObject?.pageJSON = pagesLoaded.optionalDictionaryForKey(pageType) loadObject?.pageJSON = pagesLoaded.optionalDictionaryForKey(pageType)
} }
// Checks for modules we are listening for. // Checks for modules we are listening for.
if let modulesLoaded = notification.userInfo?.optionalDictionaryForKey(KeyModuleMap), if let modulesLoaded = notification.userInfo?.optionalDictionaryForKey(KeyModuleMap),
let modulesListened = modulesToListenFor() { let modulesListened = modulesToListenFor() {
for moduleName in modulesListened { for moduleName in modulesListened {
if let module = modulesLoaded.optionalDictionaryForKey(moduleName) { if let module = modulesLoaded.optionalDictionaryForKey(moduleName) {
newData = true newData = true
@ -196,9 +190,9 @@ import UIKit
open class func verifyRequiredModulesLoaded(for loadObject: MVMCoreLoadObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject>) -> Bool { open class func verifyRequiredModulesLoaded(for loadObject: MVMCoreLoadObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject>) -> Bool {
guard let pageType = loadObject?.pageType, guard let pageType = loadObject?.pageType,
var modulesRequired = MVMCoreUIViewControllerMappingObject.shared()?.modulesRequired(forPageType: pageType), var modulesRequired = MVMCoreUIViewControllerMappingObject.shared()?.modulesRequired(forPageType: pageType),
!modulesRequired.isEmpty !modulesRequired.isEmpty
else { return true } else { return true }
guard let loadedModules = loadObject?.modulesJSON else { return false } guard let loadedModules = loadObject?.modulesJSON else { return false }
@ -263,8 +257,8 @@ import UIKit
/// Sets the navigation item for this view controller. /// Sets the navigation item for this view controller.
open func setNavigationItem() { open func setNavigationItem() {
guard let navigationItemModel = getNavigationModel(), guard let navigationItemModel = getNavigationModel(),
let navigationController = navigationController let navigationController = navigationController
else { return } else { return }
// Utilize helper function to set the navigation item state. // Utilize helper function to set the navigation item state.
NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: self) NavigationController.setNavigationItem(navigationController: navigationController, navigationItemModel: navigationItemModel, viewController: self)
@ -273,9 +267,9 @@ import UIKit
/// Sets the appearance of the navigation bar based on the model. /// Sets the appearance of the navigation bar based on the model.
open func setNavigationBar() { open func setNavigationBar() {
guard let navigationItemModel = getNavigationModel(), guard let navigationItemModel = getNavigationModel(),
let navigationController = navigationController else { let navigationController = navigationController else {
MVMCoreUISession.sharedGlobal()?.splitViewController?.parent?.setNeedsStatusBarAppearanceUpdate() MVMCoreUISession.sharedGlobal()?.splitViewController?.parent?.setNeedsStatusBarAppearanceUpdate()
return return
} }
// Utilize helper function to set the split view and navigation item state. // Utilize helper function to set the split view and navigation item state.
@ -405,7 +399,7 @@ import UIKit
} }
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask { open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return MVMCoreGetterUtility.isOnIPad() ? UIInterfaceOrientationMask.all : UIInterfaceOrientationMask.portrait MVMCoreGetterUtility.isOnIPad() ? UIInterfaceOrientationMask.all : UIInterfaceOrientationMask.portrait
} }
open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
@ -424,6 +418,7 @@ import UIKit
open func viewControllerReady(inManager manager: UIViewController & MVMCoreViewManagerProtocol) { open func viewControllerReady(inManager manager: UIViewController & MVMCoreViewManagerProtocol) {
pageShown() pageShown()
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - MVMCoreLoadDelegateProtocol // MARK: - MVMCoreLoadDelegateProtocol
//-------------------------------------------------- //--------------------------------------------------
@ -435,15 +430,18 @@ import UIKit
// Open the support panel // Open the support panel
if error == nil, if error == nil,
loadObject?.requestParameters?.openSupportPanel ?? (loadObject?.systemParametersJSON?.boolForKey(KeyOpenSupport) ?? false) == true { loadObject?.requestParameters?.openSupportPanel ?? (loadObject?.systemParametersJSON?.boolForKey(KeyOpenSupport) ?? false) == true {
MVMCoreUISession.sharedGlobal()?.splitViewController?.showRightPanel(animated: true) MVMCoreUISession.sharedGlobal()?.splitViewController?.showRightPanel(animated: true)
} }
} }
/// Override this method to avoid adding form params. /// Override this method to avoid adding form params.
open func addFormParams(_ requestParameters: MVMCoreRequestParameters) { open func addFormParams(_ requestParameters: MVMCoreRequestParameters) {
formValidator?.addFormParams(requestParameters: requestParameters) formValidator?.addFormParams(requestParameters: requestParameters)
} }
public func handleFieldErrors(_ fieldErrors: [Any]?, loadObject: MVMCoreLoadObject) { }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - MVMCoreActionDelegateProtocol // MARK: - MVMCoreActionDelegateProtocol
//-------------------------------------------------- //--------------------------------------------------
@ -469,9 +467,9 @@ import UIKit
open func getModuleWithName(_ moleculeName: String) -> MoleculeModelProtocol? { open func getModuleWithName(_ moleculeName: String) -> MoleculeModelProtocol? {
guard let moduleJSON = loadObject?.modulesJSON?.optionalDictionaryForKey(moleculeName), guard let moduleJSON = loadObject?.modulesJSON?.optionalDictionaryForKey(moleculeName),
let moleculeName = moduleJSON.optionalStringForKey("moleculeName"), let moleculeName = moduleJSON.optionalStringForKey("moleculeName"),
let modelType = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self) let modelType = ModelRegistry.getType(for: moleculeName, with: MoleculeModelProtocol.self)
else { return nil } else { return nil }
do { do {
return try modelType.decode(jsonDict: moduleJSON) as? MoleculeModelProtocol return try modelType.decode(jsonDict: moduleJSON) as? MoleculeModelProtocol
@ -483,8 +481,8 @@ import UIKit
} }
// Needed otherwise when subclassed, the extension gets called. // Needed otherwise when subclassed, the extension gets called.
open func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) {} open func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { }
open func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? { return nil } open func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? { nil }
open func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) { } open func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) { }
open func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation) { } open func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation) { }
@ -514,13 +512,13 @@ import UIKit
} }
open func showRightPanelForScreenBeforeLaunchApp() -> Bool { open func showRightPanelForScreenBeforeLaunchApp() -> Bool {
return loadObject?.pageJSON?.lenientBoolForKey("showRightPanel") ?? false loadObject?.pageJSON?.lenientBoolForKey("showRightPanel") ?? false
} }
// TODO: make molecular // TODO: make molecular
open func isOverridingRightButton() -> Bool { open func isOverridingRightButton() -> Bool {
guard let rightPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("rightPanelButtonLink") guard let rightPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("rightPanelButtonLink")
else { return false } else { return false }
MVMCoreActionHandler.shared()?.handleAction(with: rightPanelLink, additionalData: nil, delegateObject: delegateObject()) MVMCoreActionHandler.shared()?.handleAction(with: rightPanelLink, additionalData: nil, delegateObject: delegateObject())
return true return true
} }
@ -528,7 +526,7 @@ import UIKit
// TODO: make molecular // TODO: make molecular
open func isOverridingLeftButton() -> Bool { open func isOverridingLeftButton() -> Bool {
guard let leftPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("leftPanelButtonLink") guard let leftPanelLink = loadObject?.pageJSON?.optionalDictionaryForKey("leftPanelButtonLink")
else { return false } else { return false }
MVMCoreActionHandler.shared()?.handleAction(with: leftPanelLink, additionalData: nil, delegateObject: delegateObject()) MVMCoreActionHandler.shared()?.handleAction(with: leftPanelLink, additionalData: nil, delegateObject: delegateObject())
return true return true
} }
@ -536,8 +534,8 @@ import UIKit
// Eventually will be moved to Model // Eventually will be moved to Model
open func bottomProgress() -> Float? { open func bottomProgress() -> Float? {
guard let progressString = loadObject?.pageJSON?.optionalStringForKey(KeyProgressPercent), guard let progressString = loadObject?.pageJSON?.optionalStringForKey(KeyProgressPercent),
let progress = Float(progressString) let progress = Float(progressString)
else { return nil } else { return nil }
return progress / Float(100) return progress / Float(100)
} }
@ -558,8 +556,8 @@ import UIKit
// TODO: Make this into a protocol // TODO: Make this into a protocol
if UIAccessibility.isVoiceOverRunning { if UIAccessibility.isVoiceOverRunning {
if let toolBar = textField.inputAccessoryView as? UIToolbar, if let toolBar = textField.inputAccessoryView as? UIToolbar,
let _ = toolBar.items?.last, let _ = toolBar.items?.last,
let pickerView = textField.inputView as? UIPickerView { let pickerView = textField.inputView as? UIPickerView {
view.accessibilityElements = [pickerView, toolBar] view.accessibilityElements = [pickerView, toolBar]
} }
@ -610,6 +608,6 @@ import UIKit
//-------------------------------------------------- //--------------------------------------------------
func executeBehaviors<T>(_ behaviorBlock:(_ behavior:T)->Void) { func executeBehaviors<T>(_ behaviorBlock:(_ behavior:T)->Void) {
model?.behaviors?.compactMap({ $0 as? T }).forEach { behaviorBlock($0) } model?.behaviors?.compactMap { $0 as? T }.forEach { behaviorBlock($0) }
} }
} }

View File

@ -22,7 +22,6 @@ public protocol FormFieldProtocol: FormItemProtocol {
} }
extension FormFieldProtocol { extension FormFieldProtocol {
var baseValue: AnyHashable? {
return nil var baseValue: AnyHashable? { nil }
}
} }